import { Config } from '../../config/simpleEnv.js' import { httpClient } from '../../utils/httpClient.js' import { logger } from '../../utils/logger.js' import { PacificaProxyClient } from '../../exchanges/pacifica/PacificaProxyClient.js' /** * 同一平台对冲管理器 * 支持在同一交易所内使用不同账户进行对冲操作,通过代理实现网络隔离 */ export class SamePlatformHedgingManager { constructor(exchange, riskLimits) { this.exchange = exchange this.accounts = new Map() this.hedgePairs = new Map() this.clients = new Map() // 初始化风险限制 this.riskLimits = { maxPositionSize: riskLimits?.maxPositionSize || 1.0, maxTotalExposure: riskLimits?.maxTotalExposure || 5.0, maxAccountBalance: riskLimits?.maxAccountBalance || 10000, minAccountBalance: riskLimits?.minAccountBalance || 100, maxDailyTrades: riskLimits?.maxDailyTrades || 100, maxSlippage: riskLimits?.maxSlippage || 0.02, emergencyStopLoss: riskLimits?.emergencyStopLoss || 0.05, enabled: riskLimits?.enabled ?? true, } } /** * 添加对冲账户 */ addAccount(accountId, config) { const account = { id: accountId, exchange: this.exchange, config, positions: new Map(), lastUpdate: 0, proxyConfig: this.getProxyForAccount(accountId), } this.accounts.set(accountId, account) // 创建对应的交易客户端 this.createTradingClient(accountId, config) logger.info(`添加${this.exchange}对冲账户`, { accountId, hasProxy: !!account.proxyConfig, exchange: this.exchange, }) } /** * 创建对冲对 */ createHedgePair(pairId, longAccountId, shortAccountId, symbol, targetRatio = 1.0) { if (!this.accounts.has(longAccountId) || !this.accounts.has(shortAccountId)) { throw new Error(`账户不存在: ${longAccountId} 或 ${shortAccountId}`) } const pair = { id: pairId, longAccountId, shortAccountId, symbol, targetRatio, currentLongPosition: 0, currentShortPosition: 0, netExposure: 0, lastRebalance: 0, isActive: true, } this.hedgePairs.set(pairId, pair) logger.info(`创建${this.exchange}对冲对`, { pairId, longAccount: longAccountId, shortAccount: shortAccountId, symbol, targetRatio, }) } /** * 创建交易客户端 */ createTradingClient(accountId, config) { if (this.exchange === 'pacifica') { const client = new PacificaProxyClient({ account: config.account, privateKey: config.privateKey, agentWallet: config.agentWallet, agentPrivateKey: config.agentPrivateKey, }) this.clients.set(accountId, client) } else if (this.exchange === 'aster') { // TODO: 实现Aster客户端 logger.warn('Aster交易客户端暂未实现', { accountId }) } else if (this.exchange === 'binance') { // TODO: 实现Binance客户端 logger.warn('Binance交易客户端暂未实现', { accountId }) } } /** * 获取交易客户端 */ getTradingClient(accountId) { const client = this.clients.get(accountId) if (!client) { throw new Error(`未找到账户${accountId}的交易客户端`) } return client } /** * 获取账户专用代理配置 */ getProxyForAccount(accountId) { // 支持为不同账户配置不同的代理会话 // 这样可以模拟不同的网络来源,减少被识别为同一用户的风险 if (!Config.proxy.isConfigured(this.exchange)) { return undefined } // 为每个账户生成唯一的会话标识 const accountSpecificProxy = Config.proxy.getUrl(this.exchange) if (!accountSpecificProxy) return undefined // 如果配置了会话管理,为每个账户创建不同的会话 if (this.exchange === 'aster' && Config.proxy.aster.sessionPrefix()) { // 为不同账户生成不同的会话后缀 const accountSuffix = this.generateAccountSuffix(accountId) // 这里可以扩展为每个账户使用不同的会话配置 return accountSpecificProxy // 暂时返回相同的URL,未来可以优化 } return accountSpecificProxy } /** * 为账户生成唯一后缀 */ generateAccountSuffix(accountId) { // 基于账户ID生成稳定的后缀 let hash = 0 for (let i = 0; i < accountId.length; i++) { const char = accountId.charCodeAt(i) hash = (hash << 5) - hash + char hash = hash & hash // 转换为32位整数 } return Math.abs(hash).toString(36).substring(0, 6) } /** * 更新账户仓位 */ async updateAccountPositions(accountId) { const account = this.accounts.get(accountId) if (!account) throw new Error(`账户不存在: ${accountId}`) try { // 使用账户专用代理请求仓位信息 const positions = await this.fetchPositions(account) // 更新仓位缓存 account.positions.clear() positions.forEach(pos => { account.positions.set(pos.symbol, pos) }) account.lastUpdate = Date.now() logger.debug(`更新${this.exchange}账户仓位`, { accountId, positionCount: positions.length, proxy: account.proxyConfig ? '启用' : '禁用', }) } catch (error) { logger.error(`更新账户仓位失败`, { exchange: this.exchange, accountId, error: error.message, }) throw error } } /** * 获取仓位信息(根据交易所实现) */ async fetchPositions(account) { const baseUrl = this.getExchangeApiUrl() const endpoint = this.getPositionsEndpoint() const response = await httpClient.get(`${baseUrl}${endpoint}`, { exchange: this.exchange, accountId: account.id, timeout: 10000, retries: 2, headers: this.buildAuthHeaders(account), }) if (!response.ok) { throw new Error(`获取仓位失败: ${response.status}`) } return this.parsePositions(response.data) } /** * 获取交易所API基础URL */ getExchangeApiUrl() { switch (this.exchange) { case 'aster': return Config.aster.httpBase case 'pacifica': return Config.pacifica.baseUrl case 'binance': return Config.binance.baseUrl default: throw new Error(`不支持的交易所: ${this.exchange}`) } } /** * 获取仓位查询端点 */ getPositionsEndpoint() { switch (this.exchange) { case 'aster': return '/api/v1/positions' case 'pacifica': return '/api/v1/positions' case 'binance': return '/fapi/v2/positionRisk' default: throw new Error(`不支持的交易所: ${this.exchange}`) } } /** * 构建认证头(根据交易所实现) */ buildAuthHeaders(account) { // 这里需要根据具体交易所的认证方式实现 // 暂时返回空对象,实际使用时需要具体实现 return {} } /** * 解析仓位数据(根据交易所格式实现) */ parsePositions(data) { // 这里需要根据具体交易所的返回格式实现 // 暂时返回空数组,实际使用时需要具体实现 return [] } /** * 计算对冲对的净敞口 */ calculateNetExposure(pairId) { const pair = this.hedgePairs.get(pairId) if (!pair) throw new Error(`对冲对不存在: ${pairId}`) const longAccount = this.accounts.get(pair.longAccountId) const shortAccount = this.accounts.get(pair.shortAccountId) if (!longAccount || !shortAccount) { throw new Error(`对冲对账户不完整: ${pairId}`) } const longPosition = longAccount.positions.get(pair.symbol) const shortPosition = shortAccount.positions.get(pair.symbol) const longSize = longPosition?.size || 0 const shortSize = shortPosition?.size || 0 pair.currentLongPosition = longSize pair.currentShortPosition = shortSize pair.netExposure = longSize + shortSize // 注意:空头为负数 return pair.netExposure } /** * 执行对冲再平衡 */ async rebalanceHedgePair(pairId, tolerance = 0.01) { const pair = this.hedgePairs.get(pairId) if (!pair || !pair.isActive) return false // 更新仓位信息 await Promise.all([ this.updateAccountPositions(pair.longAccountId), this.updateAccountPositions(pair.shortAccountId), ]) const netExposure = this.calculateNetExposure(pairId) // 获取账户状态进行资金利用率分析 const longPosition = this.accountPositions.get(pair.longAccountId)?.get(pair.symbol) || 0 const shortPosition = this.accountPositions.get(pair.shortAccountId)?.get(pair.symbol) || 0 // 多维度平衡检查 const exposureDiff = Math.abs(netExposure) const positionImbalance = Math.abs(Math.abs(longPosition) - Math.abs(shortPosition)) console.log(`🔍 [平衡检查] 净敞口: ${netExposure.toFixed(4)}, 仓位不平衡: ${positionImbalance.toFixed(4)}`) // 更严格的平衡标准 const needsRebalance = exposureDiff > tolerance || positionImbalance > 0.002 if (!needsRebalance) { logger.debug(`对冲对无需再平衡`, { pairId, netExposure, positionImbalance, tolerance, exchange: this.exchange, }) return false } console.log( `🚨 [需要平衡] 净敞口: ${exposureDiff.toFixed(4)} > ${tolerance} 或仓位不平衡: ${positionImbalance.toFixed( 4, )} > 0.002`, ) // 执行再平衡交易 try { const success = await this.executeRebalanceTrades(pair, netExposure) if (success) { pair.lastRebalance = Date.now() logger.info(`对冲再平衡成功`, { pairId, netExposure, exchange: this.exchange, }) } return success } catch (error) { logger.error(`对冲再平衡失败`, { pairId, error: error.message, exchange: this.exchange, }) return false } } /** * 执行再平衡交易 - 改进版:真正的双向平衡 */ async executeRebalanceTrades(pair, netExposure) { console.log(`🔄 [再平衡] 开始双向平衡,当前净敞口: ${netExposure.toFixed(4)} BTC`) // 获取两个账户的当前状态 const longPosition = this.accountPositions.get(pair.longAccountId)?.get(pair.symbol) || 0 const shortPosition = this.accountPositions.get(pair.shortAccountId)?.get(pair.symbol) || 0 console.log(`📊 [再平衡] 当前仓位 - 多头账户: ${longPosition.toFixed(4)}, 空头账户: ${shortPosition.toFixed(4)}`) // 计算理想的平衡状态:两个账户仓位大小相等,方向相反 const totalAbsPosition = Math.abs(longPosition) + Math.abs(shortPosition) const idealPositionSize = totalAbsPosition / 2 if (idealPositionSize < 0.0005) { console.log(`ℹ️ [再平衡] 仓位太小,无需平衡`) return false } // 计算每个账户需要的调整量 const longAdjustment = idealPositionSize - Math.abs(longPosition) const shortAdjustment = idealPositionSize - Math.abs(shortPosition) console.log(`🎯 [再平衡] 理想仓位: ${idealPositionSize.toFixed(4)} BTC`) console.log(`📈 [再平衡] 调整量 - 多头: ${longAdjustment.toFixed(4)}, 空头: ${shortAdjustment.toFixed(4)}`) const minOrderSize = 0.0003 // 降低最小订单量 const orders = [] // 生成双向调整订单 if (Math.abs(longAdjustment) >= minOrderSize) { if (longAdjustment > 0) { // 多头账户需要增加多头仓位 orders.push({ accountId: pair.longAccountId, side: 'bid', amount: Math.abs(longAdjustment), reason: '再平衡-增加多头仓位', }) } else { // 多头账户需要减少仓位 orders.push({ accountId: pair.longAccountId, side: 'ask', amount: Math.abs(longAdjustment), reason: '再平衡-减少多头仓位', }) } } if (Math.abs(shortAdjustment) >= minOrderSize) { if (shortAdjustment > 0) { // 空头账户需要增加空头仓位 orders.push({ accountId: pair.shortAccountId, side: 'ask', amount: Math.abs(shortAdjustment), reason: '再平衡-增加空头仓位', }) } else { // 空头账户需要减少仓位 orders.push({ accountId: pair.shortAccountId, side: 'bid', amount: Math.abs(shortAdjustment), reason: '再平衡-减少空头仓位', }) } } if (orders.length === 0) { console.log(`ℹ️ [再平衡] 调整量都太小,跳过再平衡`) return false } // 执行双向调整订单 try { console.log(`🔄 [再平衡] 执行${orders.length}个调整订单`) for (const order of orders) { console.log(`📝 [再平衡] ${order.accountId}: ${order.side} ${order.amount.toFixed(4)} BTC - ${order.reason}`) await this.executeHedgeOrder(order.accountId, pair.symbol, order.amount, order.side) // 短暂延迟避免过快执行 await new Promise(resolve => setTimeout(resolve, 200)) } console.log(`✅ [再平衡] 双向平衡完成`) return true } catch (error) { logger.error(`执行双向再平衡交易失败`, { pair: pair.id, netExposure, orders, error: error.message, exchange: this.exchange, }) return false } } /** * 风险检查 */ async checkRiskLimits(accountId, symbol, amount, side) { if (!this.riskLimits.enabled) { return { allowed: true } } try { // 检查订单量限制 if (amount > this.riskLimits.maxPositionSize) { return { allowed: false, reason: `订单量${amount}超过单个仓位最大限制${this.riskLimits.maxPositionSize}`, } } // 检查账户余额 const client = this.getTradingClient(accountId) const balances = await client.getBalances() if (balances && Array.isArray(balances)) { const usdBalance = balances.find(b => b.asset === 'USDT' || b.asset === 'USD' || b.asset === 'USDC') if (usdBalance) { const balance = parseFloat(usdBalance.free || usdBalance.available || '0') if (balance < this.riskLimits.minAccountBalance) { return { allowed: false, reason: `账户余额${balance}低于最小要求${this.riskLimits.minAccountBalance}`, } } } } // 检查总敞口 const totalExposure = await this.calculateTotalExposure(accountId) const newExposure = side === 'bid' ? totalExposure + amount : totalExposure - amount if (Math.abs(newExposure) > this.riskLimits.maxTotalExposure) { return { allowed: false, reason: `新的总敞口${Math.abs(newExposure)}将超过最大限制${this.riskLimits.maxTotalExposure}`, } } logger.info('风险检查通过', { accountId, symbol, amount, side, currentExposure: totalExposure, newExposure, }) return { allowed: true } } catch (error) { logger.error('风险检查失败', { accountId, symbol, error: error.message, }) return { allowed: false, reason: `风险检查失败: ${error.message}`, } } } /** * 计算账户总敞口 */ async calculateTotalExposure(accountId) { try { const client = this.getTradingClient(accountId) const positions = await client.getPositions() let totalExposure = 0 if (positions && Array.isArray(positions)) { for (const position of positions) { const size = parseFloat(position.size || position.amount || '0') totalExposure += Math.abs(size) } } return totalExposure } catch (error) { logger.error('计算总敞口失败', { accountId, error: error.message, }) return 0 } } /** * 紧急止损检查 */ async checkEmergencyStopLoss(accountId) { try { const client = this.getTradingClient(accountId) const positions = await client.getPositions() if (!positions || !Array.isArray(positions)) { return false } for (const position of positions) { const pnlPercent = parseFloat(position.pnlPercent || position.unrealizedPnlPercent || '0') if (Math.abs(pnlPercent) > this.riskLimits.emergencyStopLoss) { logger.warn('触发紧急止损', { accountId, symbol: position.symbol, pnlPercent, emergencyStopLoss: this.riskLimits.emergencyStopLoss, }) // 执行紧急平仓 await this.emergencyClosePosition(accountId, position) return true } } return false } catch (error) { logger.error('紧急止损检查失败', { accountId, error: error.message, }) return false } } /** * 紧急平仓 */ async emergencyClosePosition(accountId, position) { try { const client = this.getTradingClient(accountId) const account = this.accounts.get(accountId) if (!account) { throw new Error(`账户${accountId}不存在`) } const size = Math.abs(parseFloat(position.size || position.amount || '0')) const isLong = parseFloat(position.size || position.amount || '0') > 0 const side = isLong ? 'ask' : 'bid' // 平多头用卖单,平空头用买单 const payload = { account: account.config.account || '', symbol: position.symbol, amount: size.toString(), side, reduceOnly: true, slippagePercent: '1.0', // 紧急情况允许更大滑点 } logger.warn('执行紧急平仓', { accountId, symbol: position.symbol, size, side, isLong, }) await client.createMarketOrder(payload) logger.info('紧急平仓执行成功', { accountId, symbol: position.symbol, size, side, }) } catch (error) { logger.error('紧急平仓失败', { accountId, position: position.symbol, error: error.message, }) } } /** * 执行对冲订单 */ async executeHedgeOrder(accountId, symbol, amount, side) { const client = this.getTradingClient(accountId) const account = this.accounts.get(accountId) if (!account) { throw new Error(`账户${accountId}不存在`) } // 执行风险检查 const riskCheck = await this.checkRiskLimits(accountId, symbol, amount, side) if (!riskCheck.allowed) { throw new Error(`风险控制拒绝: ${riskCheck.reason}`) } // 执行紧急止损检查 const emergencyTriggered = await this.checkEmergencyStopLoss(accountId) if (emergencyTriggered) { throw new Error(`触发紧急止损,暂停交易`) } if (this.exchange === 'pacifica') { const payload = { account: account.config.account || '', symbol, amount: amount.toString(), side, reduceOnly: false, slippagePercent: '0.5', // 0.5%滑点 } logger.info(`执行Pacifica对冲订单`, { accountId, symbol, amount, side, proxy: !!account.proxyConfig, }) // 使用市价单确保成交 const result = await client.createMarketOrder(payload) logger.info(`Pacifica对冲订单执行成功`, { accountId, symbol, side, orderId: result.orderId || result.order_id, success: result.success, }) return result } else { throw new Error(`交易所${this.exchange}的下单功能暂未实现`) } } /** * 批量执行对冲交易 */ async executeBatchHedge(orders) { const results = [] logger.info(`开始批量对冲执行`, { orderCount: orders.length, exchange: this.exchange, }) for (const order of orders) { try { const result = await this.executeHedgeOrder(order.accountId, order.symbol, order.amount, order.side) results.push({ success: true, orderId: result.orderId || result.order_id, }) } catch (error) { logger.error(`批量对冲订单执行失败`, { accountId: order.accountId, symbol: order.symbol, error: error.message, }) results.push({ success: false, error: error.message, }) } } logger.info(`批量对冲执行完成`, { total: orders.length, successful: results.filter(r => r.success).length, failed: results.filter(r => !r.success).length, exchange: this.exchange, }) return results } /** * 获取所有对冲对状态 */ getHedgePairStatuses() { const statuses = [] for (const [pairId, pair] of this.hedgePairs) { const netExposure = this.calculateNetExposure(pairId) statuses.push({ pairId, symbol: pair.symbol, longAccount: pair.longAccountId, shortAccount: pair.shortAccountId, longPosition: pair.currentLongPosition, shortPosition: pair.currentShortPosition, netExposure, targetRatio: pair.targetRatio, isActive: pair.isActive, lastRebalance: pair.lastRebalance, exchange: this.exchange, }) } return statuses } /** * 停用对冲对 */ deactivateHedgePair(pairId) { const pair = this.hedgePairs.get(pairId) if (pair) { pair.isActive = false logger.info(`停用对冲对`, { pairId, exchange: this.exchange }) } } /** * 激活对冲对 */ activateHedgePair(pairId) { const pair = this.hedgePairs.get(pairId) if (pair) { pair.isActive = true logger.info(`激活对冲对`, { pairId, exchange: this.exchange }) } } }