TradingEngine.js 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414
  1. import { logger } from '../../utils/logger.js'
  2. import { globalPriceManager } from '../price/PriceManager.js'
  3. /**
  4. * 交易引擎 - 负责信号生成和交易执行
  5. */
  6. export class TradingEngine {
  7. constructor(accountManager, riskManager, stopLossService, hedgeManager = null) {
  8. this.accountManager = accountManager
  9. this.riskManager = riskManager
  10. this.stopLossService = stopLossService
  11. this.hedgeManager = hedgeManager
  12. this.isRunning = false
  13. this.tradingIntervals = []
  14. this.stats = {
  15. totalTrades: 0,
  16. successfulTrades: 0,
  17. failedTrades: 0,
  18. totalVolume: 0,
  19. lastTradeTime: 0,
  20. sessionStartTime: Date.now(),
  21. }
  22. this.riskLimits = {
  23. maxPositionSize: 0.01,
  24. maxTotalExposure: 0.05,
  25. maxAccountBalance: 10000,
  26. minAccountBalance: 100,
  27. maxDailyTrades: 50,
  28. maxSlippage: 0.05,
  29. emergencyStopLoss: 0.1,
  30. enabled: true,
  31. }
  32. this.priceCache = {}
  33. this.orderbookPriceCache = {}
  34. }
  35. async initialize() {
  36. logger.info('TradingEngine初始化')
  37. }
  38. async start() {
  39. logger.info('TradingEngine启动')
  40. this.isRunning = true
  41. // 启动时预热orderbook价格缓存
  42. this.preloadOrderbookPrices()
  43. this.startTradingLoop()
  44. }
  45. async stop() {
  46. logger.info('TradingEngine停止')
  47. this.isRunning = false
  48. this.tradingIntervals.forEach(interval => clearInterval(interval))
  49. this.tradingIntervals = []
  50. }
  51. getStatus() {
  52. return {
  53. name: 'TradingEngine',
  54. status: this.isRunning ? 'running' : 'stopped',
  55. lastUpdate: Date.now(),
  56. details: {
  57. stats: this.stats,
  58. activeIntervals: this.tradingIntervals.length,
  59. },
  60. }
  61. }
  62. /**
  63. * 启动交易循环
  64. */
  65. startTradingLoop() {
  66. console.log('🔄 启动交易引擎')
  67. // 主交易信号生成器
  68. const mainInterval = setInterval(async () => {
  69. if (this.isRunning) {
  70. await this.generateTradingSignals()
  71. }
  72. }, 15000)
  73. this.tradingIntervals.push(mainInterval)
  74. // 高频刷量信号
  75. const volumeInterval = setInterval(async () => {
  76. if (this.isRunning) {
  77. await this.generateVolumeBoostSignals()
  78. }
  79. }, 8000)
  80. this.tradingIntervals.push(volumeInterval)
  81. console.log('✅ 交易引擎已启动')
  82. }
  83. /**
  84. * 生成交易信号
  85. */
  86. async generateTradingSignals() {
  87. const signals = []
  88. const randomFactor = Math.random()
  89. // 初始化账户状态
  90. this.accountManager.getAllAccountStates()
  91. // 检查是否需要资金平衡
  92. const needsBalancing = this.checkAccountBalance()
  93. if (needsBalancing) {
  94. const balanceSignal = this.generateBalanceSignal()
  95. if (balanceSignal) {
  96. signals.push(balanceSignal)
  97. // 高使用率时,执行降低使用率策略
  98. const accounts = Array.from(this.accountManager.getAllAccountStates().values())
  99. let maxUtilizationRate = 0
  100. let highUtilizationAccount = null
  101. accounts.forEach(account => {
  102. if (account.lastBalance > 0) {
  103. const utilizationRate = Math.abs(account.lastBalance - account.availableBalance) / account.lastBalance
  104. if (utilizationRate > maxUtilizationRate) {
  105. maxUtilizationRate = utilizationRate
  106. highUtilizationAccount = account
  107. }
  108. }
  109. })
  110. // 动态使用率平衡机制
  111. await this.executeUtilizationBalancing(accounts, maxUtilizationRate)
  112. if (maxUtilizationRate >= 0.9) {
  113. console.log(`🚨 [高使用率警报] 使用率${(maxUtilizationRate * 100).toFixed(1)}% >= 90%,启动紧急降低机制`)
  114. // 执行敞口平衡
  115. for (const signal of signals) {
  116. await this.handleTradingSignal(signal)
  117. }
  118. // 主动降低使用率:减少仓位
  119. await this.executeUtilizationReduction(highUtilizationAccount, maxUtilizationRate)
  120. return
  121. }
  122. }
  123. }
  124. // 根据使用率动态调整刷量策略
  125. const avgUtilizationRate = this.calculateAverageUtilizationRate()
  126. let shouldGenerateSignal = false
  127. if (avgUtilizationRate < 0.7) {
  128. // 使用率过低,积极刷量
  129. shouldGenerateSignal = randomFactor > 0.1 // 90%概率
  130. } else if (avgUtilizationRate <= 0.8) {
  131. // 在目标范围内,正常刷量
  132. shouldGenerateSignal = randomFactor > 0.3 // 70%概率
  133. } else if (avgUtilizationRate <= 0.9) {
  134. // 超出目标,减少刷量
  135. shouldGenerateSignal = randomFactor > 0.7 // 30%概率
  136. } else {
  137. // 使用率过高,基本不刷量
  138. shouldGenerateSignal = randomFactor > 0.9 // 10%概率
  139. }
  140. if (shouldGenerateSignal) {
  141. const shouldClosePosition = this.shouldCloseExistingPositions(avgUtilizationRate)
  142. if (shouldClosePosition) {
  143. const closeSignal = await this.generateCloseSignal()
  144. if (closeSignal && parseFloat(closeSignal.amount) > 0) {
  145. signals.push(closeSignal)
  146. }
  147. } else {
  148. const action = randomFactor > 0.6 ? 'buy' : 'sell'
  149. const amount = await this.calculateTradingAmount()
  150. // 只有当金额大于0时才添加信号
  151. if (parseFloat(amount) > 0) {
  152. signals.push({
  153. symbol: 'BTC-USD',
  154. action,
  155. amount,
  156. confidence: randomFactor,
  157. reason: `智能刷量(使用率${(avgUtilizationRate * 100).toFixed(1)}%) - ${
  158. action === 'buy' ? '买入建仓' : '卖出建仓'
  159. }`,
  160. })
  161. }
  162. }
  163. }
  164. // 对冲信号
  165. if (this.hedgeManager && randomFactor > 0.5) {
  166. const hedgeSignal = await this.generateSmartHedgeSignal()
  167. if (hedgeSignal && parseFloat(hedgeSignal.amount) > 0) {
  168. signals.push(hedgeSignal)
  169. }
  170. }
  171. // 发送信号
  172. for (const signal of signals) {
  173. await this.handleTradingSignal(signal)
  174. }
  175. }
  176. /**
  177. * 生成刷量信号
  178. */
  179. async generateVolumeBoostSignals() {
  180. if (!this.shouldTrade()) {
  181. return
  182. }
  183. const signals = []
  184. if (this.hedgeManager && Math.random() > 0.4) {
  185. const primaryAccountId = 'pacifica-1'
  186. const boostAmount = await this.calculateSmartTradeAmount(primaryAccountId, 'open')
  187. signals.push({
  188. symbol: 'BTC-USD',
  189. action: 'volume_boost',
  190. amount: boostAmount,
  191. confidence: 0.9,
  192. reason: `智能刷量 - 金额: ${boostAmount} BTC`,
  193. })
  194. }
  195. signals.forEach(signal => {
  196. this.handleTradingSignal(signal)
  197. })
  198. }
  199. /**
  200. * 处理交易信号
  201. */
  202. async handleTradingSignal(signal) {
  203. logger.info('收到交易信号', { signal })
  204. // 对于减仓信号,跳过常规交易检查(允许在高使用率时执行)
  205. const isReduceOnlySignal =
  206. signal.reduceOnly ||
  207. (signal.reason &&
  208. (signal.reason.includes('使用率控制') ||
  209. signal.reason.includes('降低使用率') ||
  210. signal.reason.includes('双向减仓')))
  211. if (isReduceOnlySignal) {
  212. console.log(`🔄 [强制执行] 减仓信号,跳过常规交易检查: ${signal.reason || 'reduceOnly'}`)
  213. } else if (!this.shouldTrade()) {
  214. logger.info('当前不适合交易,忽略信号')
  215. return
  216. }
  217. try {
  218. if (signal.action === 'hedge' && this.hedgeManager) {
  219. await this.executeHedgeSignal(signal)
  220. } else if (signal.action === 'volume_boost' && this.hedgeManager) {
  221. await this.executeVolumeBoost(signal)
  222. } else if (signal.action === 'balance_accounts') {
  223. await this.executeBalanceSignal(signal)
  224. } else if (signal.action === 'balance') {
  225. // 使用率控制平仓
  226. await this.executeUtilizationControlSignal(signal)
  227. } else if (signal.action === 'close_position') {
  228. await this.executeCloseSignal(signal)
  229. } else {
  230. await this.executeTradeSignal(signal)
  231. }
  232. } catch (error) {
  233. logger.error('交易信号处理失败', { signal, error: error.message })
  234. this.stats.failedTrades++
  235. }
  236. }
  237. /**
  238. * 执行使用率控制信号(强制平仓)
  239. */
  240. async executeUtilizationControlSignal(signal) {
  241. const clientId = signal.targetAccount || 'pacifica-1'
  242. const client = this.accountManager.getClient(clientId)
  243. if (!client) {
  244. throw new Error(`客户端 ${clientId} 不可用`)
  245. }
  246. console.log(`🔄 [使用率控制] 执行强制平仓: ${signal.amount} BTC, side=${signal.side}`)
  247. // 确保 side 映射正确
  248. const side = signal.side === 'buy' ? 'bid' : 'ask'
  249. // 获取账户信息以获取正确的账户地址
  250. const accountIndex = parseInt(clientId.split('-')[1]) - 1
  251. const accounts = this.accountManager.getAccounts()
  252. const account = accounts[accountIndex]
  253. if (!account) {
  254. throw new Error(`找不到账户 ${clientId} 的配置信息`)
  255. }
  256. const result = await client.marketOrder({
  257. account: account.account,
  258. symbol: 'BTCUSDT',
  259. amount: signal.amount,
  260. side: side,
  261. reduceOnly: true,
  262. slippagePercent: '5.0',
  263. })
  264. if (result.success) {
  265. this.stats.successfulTrades++
  266. this.stats.totalVolume += parseFloat(signal.amount)
  267. console.log(`✅ [使用率控制] 强制平仓成功: ${result.data?.order_id}`)
  268. this.accountManager.updateTradeState(clientId, {
  269. side: signal.side,
  270. amount: signal.amount,
  271. success: true,
  272. })
  273. } else {
  274. this.stats.failedTrades++
  275. throw new Error(`使用率控制平仓失败: ${result.error}`)
  276. }
  277. this.stats.totalTrades++
  278. this.stats.lastTradeTime = Date.now()
  279. }
  280. /**
  281. * 执行交易信号
  282. */
  283. async executeTradeSignal(signal) {
  284. const clientId = signal.targetAccount || 'pacifica-1'
  285. const client = this.accountManager.getClient(clientId)
  286. if (!client) {
  287. throw new Error(`客户端 ${clientId} 不可用`)
  288. }
  289. const side = signal.action === 'buy' ? 'bid' : 'ask'
  290. const accountStates = this.accountManager.getAllAccountStates()
  291. const firstAccountId = Array.from(accountStates.keys())[0]
  292. // 获取当前价格用于止盈止损
  293. const currentPrice = await this.getCurrentPrice(signal.symbol)
  294. // 执行订单
  295. const result = await client.createMarketOrder({
  296. account: firstAccountId,
  297. symbol: 'BTCUSDT',
  298. amount: signal.amount,
  299. side: side,
  300. reduceOnly: signal.reduceOnly || signal.action === 'sell',
  301. slippagePercent: '5.0',
  302. })
  303. if (result.success) {
  304. this.stats.successfulTrades++
  305. this.stats.totalVolume += parseFloat(signal.amount)
  306. this.stats.lastTradeTime = Date.now()
  307. logger.info('交易订单执行成功', { orderId: result.data?.order_id })
  308. console.log(`✅ ${signal.action.toUpperCase()} 订单成功: ${result.data?.order_id}`)
  309. // 更新账户状态
  310. this.accountManager.updateTradeState(clientId, { side, amount: signal.amount, success: true })
  311. // 设置止盈止损
  312. if (!signal.reduceOnly && result.data?.order_id) {
  313. await this.stopLossService.setupStopLossAndTakeProfit({
  314. parentOrderId: result.data.order_id,
  315. symbol: signal.symbol,
  316. amount: signal.amount,
  317. side: signal.action,
  318. currentPrice,
  319. clientId,
  320. stopLoss: signal.stopLoss,
  321. takeProfit: signal.takeProfit,
  322. enableTrailing: signal.enableTrailing,
  323. })
  324. }
  325. } else {
  326. this.stats.failedTrades++
  327. throw new Error(`订单失败: ${result.error}`)
  328. }
  329. this.stats.totalTrades++
  330. }
  331. /**
  332. * 执行对冲信号
  333. */
  334. async executeHedgeSignal(signal) {
  335. if (!this.hedgeManager) return
  336. const amount = parseFloat(signal.amount)
  337. if (!amount || amount <= 0 || !isFinite(amount)) {
  338. console.log(`❌ [订单跳过] 无效金额: ${signal.amount} → ${amount}`)
  339. return
  340. }
  341. const batchOrders = [
  342. {
  343. accountId: 'pacifica-1',
  344. symbol: 'BTC-USD',
  345. amount: amount,
  346. side: 'bid',
  347. orderType: 'market',
  348. },
  349. {
  350. accountId: 'pacifica-2',
  351. symbol: 'BTC-USD',
  352. amount: amount,
  353. side: 'ask',
  354. orderType: 'market',
  355. },
  356. ]
  357. const results = await this.hedgeManager.executeBatchHedge(batchOrders)
  358. this.processHedgeResults(results, batchOrders)
  359. }
  360. /**
  361. * 执行刷量信号
  362. */
  363. async executeVolumeBoost(signal) {
  364. if (!this.hedgeManager) return
  365. const boostSequence = [
  366. {
  367. accountId: 'pacifica-1',
  368. symbol: 'BTC-USD',
  369. amount: parseFloat(signal.amount),
  370. side: 'bid',
  371. orderType: 'market',
  372. reason: '刷量开仓-多头',
  373. },
  374. {
  375. accountId: 'pacifica-2',
  376. symbol: 'BTC-USD',
  377. amount: parseFloat(signal.amount),
  378. side: 'ask',
  379. orderType: 'market',
  380. reason: '刷量开仓-空头',
  381. },
  382. ]
  383. const results = await this.hedgeManager.executeBatchHedge(boostSequence)
  384. this.processVolumeBoostResults(results, boostSequence, signal.amount)
  385. }
  386. /**
  387. * 执行平衡信号
  388. */
  389. async executeBalanceSignal(signal) {
  390. if (!this.hedgeManager || !signal.targetAccount) return
  391. const balanceOrders = this.generateBalanceOrders(signal)
  392. const results = await this.hedgeManager.executeBatchHedge(balanceOrders)
  393. let successCount = 0
  394. results.forEach((result, index) => {
  395. if (result.success) {
  396. successCount++
  397. }
  398. })
  399. this.stats.totalTrades += results.length
  400. this.stats.successfulTrades += successCount
  401. this.stats.failedTrades += results.length - successCount
  402. const successRate = results.length > 0 ? ((successCount / results.length) * 100).toFixed(1) : '0.0'
  403. console.log(`⚖️ [平衡完成] 成功率: ${successRate}% (${successCount}/${results.length})`)
  404. }
  405. /**
  406. * 执行平仓信号
  407. */
  408. async executeCloseSignal(signal) {
  409. const clientId = signal.targetAccount || 'pacifica-1'
  410. const client = this.accountManager.getClient(clientId)
  411. if (!client) {
  412. throw new Error(`客户端 ${clientId} 不可用`)
  413. }
  414. const side = signal.action === 'buy' ? 'bid' : 'ask'
  415. const accountStates = this.accountManager.getAllAccountStates()
  416. const firstAccountId = Array.from(accountStates.keys())[0]
  417. const result = await client.createMarketOrder({
  418. account: firstAccountId,
  419. symbol: 'BTCUSDT',
  420. amount: signal.amount,
  421. side: side,
  422. reduceOnly: true,
  423. slippagePercent: '5.0',
  424. })
  425. if (result.success) {
  426. this.stats.successfulTrades++
  427. this.stats.totalVolume += parseFloat(signal.amount)
  428. console.log(`✅ 平仓成功: ${result.data?.order_id}`)
  429. this.accountManager.updateTradeState(clientId, { side, amount: signal.amount, success: true })
  430. } else {
  431. this.stats.failedTrades++
  432. throw new Error(`平仓失败: ${result.error}`)
  433. }
  434. this.stats.totalTrades++
  435. this.stats.lastTradeTime = Date.now()
  436. }
  437. // Helper methods
  438. checkAccountBalance() {
  439. const accounts = Array.from(this.accountManager.getAllAccountStates().values())
  440. if (accounts.length < 2) return false
  441. const totalNetExposure = accounts.reduce((sum, account) => sum + account.netPosition, 0)
  442. const exposureThreshold = this.riskManager.calculateDynamicExposureThreshold()
  443. return Math.abs(totalNetExposure) > exposureThreshold
  444. }
  445. generateBalanceSignal() {
  446. const accountsArray = Array.from(this.accountManager.getAllAccountStates().entries())
  447. if (accountsArray.length < 2) return null
  448. const totalNetExposure = accountsArray.reduce((sum, [_, state]) => sum + state.netPosition, 0)
  449. const dynamicThreshold = this.riskManager.calculateDynamicExposureThreshold()
  450. if (Math.abs(totalNetExposure) > dynamicThreshold) {
  451. const sortedAccounts = accountsArray.sort((a, b) => Math.abs(b[1].netPosition) - Math.abs(a[1].netPosition))
  452. const [targetAccountId] = sortedAccounts[0]
  453. const adjustmentAmount = Math.min(Math.abs(totalNetExposure) * 0.5, 0.001)
  454. const action = totalNetExposure > 0 ? 'sell' : 'buy'
  455. return {
  456. symbol: 'BTC-USD',
  457. action: 'balance_accounts',
  458. side: action,
  459. amount: adjustmentAmount.toFixed(4),
  460. confidence: 0.95,
  461. reason: `敞口平衡 - 总净敞口${totalNetExposure.toFixed(4)} BTC`,
  462. targetAccount: targetAccountId,
  463. reduceOnly: false,
  464. }
  465. }
  466. return null
  467. }
  468. /**
  469. * 计算平均使用率
  470. */
  471. calculateAverageUtilizationRate() {
  472. const accounts = Array.from(this.accountManager.getAllAccountStates().values())
  473. if (accounts.length === 0) return 0
  474. let totalUtilization = 0
  475. let validAccounts = 0
  476. accounts.forEach(account => {
  477. if (account.lastBalance > 0) {
  478. const utilizationRate = Math.abs(account.lastBalance - account.availableBalance) / account.lastBalance
  479. totalUtilization += utilizationRate
  480. validAccounts++
  481. }
  482. })
  483. return validAccounts > 0 ? totalUtilization / validAccounts : 0
  484. }
  485. shouldCloseExistingPositions(avgUtilizationRate) {
  486. const utilizationRate = avgUtilizationRate || this.calculateAverageUtilizationRate()
  487. // 根据使用率调整平仓概率
  488. if (utilizationRate >= 0.85) {
  489. return Math.random() < 0.6 // 高使用率时,60%概率平仓
  490. } else if (utilizationRate >= 0.75) {
  491. return Math.random() < 0.4 // 中高使用率时,40%概率平仓
  492. } else if (utilizationRate >= 0.65) {
  493. return Math.random() < 0.2 // 正常使用率时,20%概率平仓
  494. } else {
  495. return Math.random() < 0.1 // 低使用率时,10%概率平仓
  496. }
  497. }
  498. async generateCloseSignal() {
  499. const accountsArray = Array.from(this.accountManager.getAllAccountStates().entries())
  500. const accountWithMaxPosition = accountsArray.reduce((max, current) => {
  501. return Math.abs(current[1].netPosition) > Math.abs(max[1].netPosition) ? current : max
  502. })
  503. const [accountId, accountState] = accountWithMaxPosition
  504. if (Math.abs(accountState.netPosition) > 0.0001) {
  505. const smartCloseAmount = await this.calculateSmartTradeAmount(accountId, 'close')
  506. const action = accountState.netPosition > 0 ? 'sell' : 'buy'
  507. return {
  508. symbol: 'BTC-USD',
  509. action: action,
  510. amount: smartCloseAmount,
  511. confidence: 0.9,
  512. reason: `智能平仓 - 减少${accountState.netPosition > 0 ? '多头' : '空头'}仓位`,
  513. reduceOnly: true,
  514. targetAccount: accountId,
  515. }
  516. }
  517. return null
  518. }
  519. async generateSmartHedgeSignal() {
  520. if (!this.hedgeManager) return null
  521. const hedgeAmount = await this.calculateHedgeAmount()
  522. return {
  523. symbol: 'BTC-USD',
  524. action: 'hedge',
  525. amount: hedgeAmount,
  526. confidence: 0.85,
  527. reason: '常规对冲刷量',
  528. }
  529. }
  530. async calculateTradingAmount() {
  531. return await this.calculateSmartTradeAmount('pacifica-1', 'open')
  532. }
  533. async calculateSmartTradeAmount(accountId, action) {
  534. const state = this.accountManager.getAccountState(accountId)
  535. // 获取实时余额(与calculateHedgeAmount保持一致)
  536. const freshBalance = await this.accountManager.getAccountBalance(accountId)
  537. // Pacifica最小订单要求: 10 USDT
  538. const minOrderAmountUsd = 10
  539. const currentBtcPrice = this.getCurrentPriceSync('BTC-USD') // 获取实时BTC价格
  540. // 边界检查:确保价格有效
  541. if (!currentBtcPrice || currentBtcPrice <= 0 || !isFinite(currentBtcPrice)) {
  542. console.log(`⚠️ [价格异常] BTC价格无效: ${currentBtcPrice}, 跳过此次交易`)
  543. return '0' // 返回0表示跳过交易
  544. }
  545. const minOrderAmountBtc = minOrderAmountUsd / currentBtcPrice
  546. // 边界检查:确保最小订单金额有效
  547. if (!isFinite(minOrderAmountBtc) || minOrderAmountBtc <= 0 || minOrderAmountBtc > 1) {
  548. console.log(`⚠️ [金额异常] 计算的最小BTC金额异常: ${minOrderAmountBtc}, 跳过此次交易`)
  549. return '0' // 返回0表示跳过交易
  550. }
  551. const minAmountStr = minOrderAmountBtc.toFixed(6)
  552. // 使用实时余额进行判断
  553. const availableBalance = freshBalance || 0
  554. const utilizationRate =
  555. state?.lastBalance && state.lastBalance > 0
  556. ? Math.abs(state.lastBalance - availableBalance) / state.lastBalance
  557. : 0
  558. // 如果账户余额不足以支持最小订单,跳过交易
  559. if (availableBalance < minOrderAmountUsd) {
  560. console.log(
  561. `⚠️ [交易跳过] 账户${accountId}可用余额$${availableBalance.toFixed(2)} < 最小订单要求$${minOrderAmountUsd}`,
  562. )
  563. return '0'
  564. }
  565. if (!state) {
  566. return minAmountStr
  567. }
  568. if (action === 'open') {
  569. // 根据使用率智能调整开仓大小
  570. let targetAmount
  571. if (utilizationRate >= 0.8) {
  572. // 使用率已在目标上限,使用最小金额
  573. targetAmount = minOrderAmountBtc
  574. } else if (utilizationRate >= 0.7) {
  575. // 在目标范围内,保持适中
  576. targetAmount = Math.max((availableBalance * 0.05) / currentBtcPrice, minOrderAmountBtc)
  577. targetAmount = Math.min(targetAmount, 0.0003)
  578. } else if (utilizationRate >= 0.5) {
  579. // 低于目标,适度增加
  580. targetAmount = Math.max((availableBalance * 0.08) / currentBtcPrice, minOrderAmountBtc)
  581. targetAmount = Math.min(targetAmount, 0.0005)
  582. } else {
  583. // 使用率过低,积极开仓
  584. targetAmount = Math.max((availableBalance * 0.1) / currentBtcPrice, minOrderAmountBtc)
  585. targetAmount = Math.min(targetAmount, 0.001)
  586. }
  587. // 最终边界检查
  588. let finalAmount = Math.max(targetAmount, minOrderAmountBtc)
  589. // Pacifica lot size精度调整: 必须是0.00001的倍数
  590. const lotSize = 0.00001
  591. finalAmount = Math.round(finalAmount / lotSize) * lotSize
  592. // 确保调整后仍满足最小要求
  593. if (finalAmount < minOrderAmountBtc) {
  594. finalAmount = Math.ceil(minOrderAmountBtc / lotSize) * lotSize
  595. }
  596. const result = finalAmount.toFixed(5) // 5位小数精度匹配lot size
  597. console.log(
  598. `📊 [开仓金额] ${accountId}: $${availableBalance.toFixed(2)} 可用 → ${result} BTC (${(
  599. finalAmount * currentBtcPrice
  600. ).toFixed(2)} USDT) [lot调整]`,
  601. )
  602. return result
  603. } else {
  604. // 平仓策略:根据使用率和仓位大小决定
  605. const positionSize = Math.abs(state.netPosition)
  606. if (positionSize === 0) return minAmountStr
  607. let closeRatio = 0.3 // 默认平仓30%
  608. if (utilizationRate >= 0.85) {
  609. closeRatio = 0.5 // 高使用率时加大平仓比例
  610. } else if (utilizationRate <= 0.65) {
  611. closeRatio = 0.2 // 低使用率时减少平仓比例
  612. }
  613. const closeAmount = Math.min(positionSize * closeRatio, 0.002)
  614. let finalAmount = Math.max(closeAmount, minOrderAmountBtc)
  615. // Pacifica lot size精度调整: 必须是0.00001的倍数
  616. const lotSize = 0.00001
  617. finalAmount = Math.round(finalAmount / lotSize) * lotSize
  618. // 确保调整后仍满足最小要求
  619. if (finalAmount < minOrderAmountBtc) {
  620. finalAmount = Math.ceil(minOrderAmountBtc / lotSize) * lotSize
  621. }
  622. const result = finalAmount.toFixed(5) // 5位小数精度匹配lot size
  623. console.log(
  624. `📊 [平仓金额] ${accountId}: ${positionSize.toFixed(4)} BTC 仓位 → ${result} BTC (${(
  625. finalAmount * currentBtcPrice
  626. ).toFixed(2)} USDT) [lot调整]`,
  627. )
  628. return result
  629. }
  630. }
  631. async calculateHedgeAmount() {
  632. try {
  633. const account1Balance = await this.accountManager.getAccountBalance('pacifica-1')
  634. const account2Balance = await this.accountManager.getAccountBalance('pacifica-2')
  635. const minBalance = Math.min(account1Balance || 0, account2Balance || 0)
  636. // Pacifica最小订单要求: 10 USDT
  637. const minOrderAmountUsd = 10
  638. const currentBTCPrice = this.getCurrentPriceSync('BTC-USD')
  639. // 边界检查:确保价格有效
  640. if (!currentBTCPrice || currentBTCPrice <= 0 || !isFinite(currentBTCPrice)) {
  641. console.log(`⚠️ [对冲价格异常] BTC价格无效: ${currentBTCPrice}, 跳过对冲交易`)
  642. return '0' // 返回0表示跳过交易
  643. }
  644. const minOrderAmountBtc = minOrderAmountUsd / currentBTCPrice
  645. // 边界检查:确保最小订单金额有效
  646. if (!isFinite(minOrderAmountBtc) || minOrderAmountBtc <= 0 || minOrderAmountBtc > 1) {
  647. console.log(`⚠️ [对冲金额异常] 计算的最小BTC金额异常: ${minOrderAmountBtc}, 跳过对冲交易`)
  648. return '0'
  649. }
  650. if (minBalance < minOrderAmountUsd) {
  651. console.log(`⚠️ [对冲跳过] 最小账户余额$${minBalance.toFixed(2)} < 最小订单要求$${minOrderAmountUsd}`)
  652. return '0'
  653. }
  654. const baseAmount = (minBalance * 0.01) / currentBTCPrice
  655. const amount = baseAmount * (0.8 + Math.random() * 0.4)
  656. // 确保不低于最小订单金额
  657. let finalAmount = Math.max(minOrderAmountBtc, Math.min(0.001, amount))
  658. // Pacifica lot size精度调整: 必须是0.00001的倍数
  659. const lotSize = 0.00001
  660. finalAmount = Math.round(finalAmount / lotSize) * lotSize
  661. // 确保调整后仍满足最小要求
  662. if (finalAmount < minOrderAmountBtc) {
  663. finalAmount = Math.ceil(minOrderAmountBtc / lotSize) * lotSize
  664. }
  665. const result = finalAmount.toFixed(5) // 5位小数精度匹配lot size
  666. console.log(
  667. `📊 [对冲金额] 最小余额: $${minBalance.toFixed(2)} → ${result} BTC (${(finalAmount * currentBTCPrice).toFixed(
  668. 2,
  669. )} USDT) [lot调整]`,
  670. )
  671. return result
  672. } catch (error) {
  673. logger.error('计算对冲数量时出错', { error: error.message })
  674. // 安全的错误处理
  675. console.log(`⚠️ [对冲计算错误] 跳过此次对冲交易`)
  676. return '0' // 错误时跳过交易
  677. }
  678. }
  679. shouldTrade() {
  680. if (this.stats.totalTrades >= this.riskLimits.maxDailyTrades) {
  681. return false
  682. }
  683. const timeSinceLastTrade = Date.now() - this.stats.lastTradeTime
  684. if (timeSinceLastTrade < 5000) {
  685. return false
  686. }
  687. // 检查使用率是否过高
  688. if (this.shouldSkipTradeForUtilization()) {
  689. return false
  690. }
  691. return true
  692. }
  693. async getCurrentPrice(symbol) {
  694. return this.getCurrentPriceSync(symbol)
  695. }
  696. getCurrentPriceSync(symbol) {
  697. try {
  698. // 直接从全局价格管理器获取实时价格
  699. const price = globalPriceManager.getPrice(symbol)
  700. if (price > 0) {
  701. logger.debug(`📊 [实时价格] ${symbol}: $${price.toFixed(2)}`)
  702. return price
  703. }
  704. // 如果没有价格数据,使用fallback价格确保利用率控制能正常工作
  705. const fallbackPrice = this.getFallbackPrice(symbol)
  706. if (fallbackPrice > 0) {
  707. logger.warn(`⚠️ [价格回退] ${symbol}使用回退价格: $${fallbackPrice.toFixed(2)}`, {
  708. priceManagerStatus: globalPriceManager.getStatus(),
  709. availableSymbols: Array.from(globalPriceManager.getAllPrices().keys()).slice(0, 5),
  710. })
  711. return fallbackPrice
  712. }
  713. return 0
  714. } catch (error) {
  715. logger.error('获取价格失败', { symbol, error: error.message })
  716. return this.getFallbackPrice(symbol)
  717. }
  718. }
  719. /**
  720. * 获取回退价格,确保使用率控制机制能正常工作
  721. */
  722. getFallbackPrice(symbol) {
  723. const symbolUpper = symbol.toUpperCase()
  724. // 基于当前市场的合理回退价格
  725. if (symbolUpper.includes('BTC')) {
  726. return 95000 // BTC 回退价格
  727. }
  728. if (symbolUpper.includes('ETH')) {
  729. return 3800 // ETH 回退价格
  730. }
  731. if (symbolUpper.includes('SOL')) {
  732. return 220 // SOL 回退价格
  733. }
  734. if (symbolUpper.includes('USD') || symbolUpper.includes('USDT') || symbolUpper.includes('USDC')) {
  735. return 1 // 稳定币回退价格
  736. }
  737. // 默认回退价格(对于未知交易对)
  738. return 100
  739. }
  740. fetchRealTimePriceSync(symbol) {
  741. try {
  742. // 尝试从最近的orderbook数据获取价格
  743. const midPrice = this.getLastKnownPrice(symbol)
  744. if (midPrice > 0) {
  745. return midPrice
  746. }
  747. // 尝试从外部API获取实时价格
  748. const externalPrice = this.getExternalPrice(symbol)
  749. if (externalPrice > 0) {
  750. console.log(`📊 [外部价格] ${symbol}: $${externalPrice}`)
  751. return externalPrice
  752. }
  753. // 最后的Fallback: 从现有的orderbook缓存中获取历史价格
  754. const historicalPrice = this.getHistoricalPrice(symbol)
  755. if (historicalPrice > 0) {
  756. console.log(`📊 [历史价格] ${symbol}: $${historicalPrice}`)
  757. return historicalPrice
  758. }
  759. // 如果都失败,使用fallback
  760. if (symbol.includes('BTC')) {
  761. console.log(`🔄 [最终fallback] ${symbol}: $65000 (所有尝试失败)`)
  762. return 65000
  763. }
  764. throw new Error(`无法获取${symbol}的有效价格`)
  765. } catch (error) {
  766. logger.warn('获取实时价格失败', { symbol, error })
  767. // 提供最终的fallback而不是抛出错误
  768. if (symbol.includes('BTC')) {
  769. console.log(`🔄 [catch fallback] ${symbol}: $65000 (异常处理)`)
  770. return 65000
  771. }
  772. throw error
  773. }
  774. }
  775. /**
  776. * 从外部API获取实时价格(使用Pacifica prices API)
  777. */
  778. getExternalPrice(symbol) {
  779. try {
  780. const client = this.accountManager.getFirstAvailableClient()
  781. if (client && client.getPrices) {
  782. // 首先检查是否已有orderbook缓存数据
  783. const cached = this.orderbookPriceCache[symbol]
  784. if (cached && Date.now() - cached.timestamp < 180 * 1000) {
  785. // 3分钟有效期
  786. const midPrice = (cached.bid + cached.ask) / 2
  787. console.log(
  788. `📊 [缓存价格] ${symbol}: $${midPrice.toFixed(2)} (bid=${cached.bid.toFixed(2)}, ask=${cached.ask.toFixed(
  789. 2,
  790. )})`,
  791. )
  792. return midPrice
  793. }
  794. // 异步获取价格数据
  795. this.updatePricesAsync(symbol)
  796. // 同时触发orderbook更新
  797. this.updateOrderbookPriceAsync(symbol)
  798. }
  799. return 0 // 异步获取,这次返回0
  800. } catch (error) {
  801. return 0
  802. }
  803. }
  804. /**
  805. * 异步更新价格数据
  806. */
  807. async updatePricesAsync(symbol) {
  808. try {
  809. const client = this.accountManager.getFirstAvailableClient()
  810. if (!client || !client.getPrices) {
  811. return
  812. }
  813. const pricesData = await client.getPrices()
  814. if (pricesData && pricesData.data) {
  815. // 查找对应symbol的价格
  816. const symbolPrice = pricesData.data.find(
  817. item =>
  818. item.symbol === symbol ||
  819. item.symbol === symbol.replace('-', '') ||
  820. item.symbol === symbol.replace('USD', 'USDT'),
  821. )
  822. if (symbolPrice && symbolPrice.price) {
  823. const price = parseFloat(symbolPrice.price)
  824. if (!isNaN(price) && price > 0) {
  825. // 更新价格缓存
  826. const cacheKey = `price_${symbol}`
  827. this.priceCache[cacheKey] = {
  828. price: price,
  829. timestamp: Date.now(),
  830. }
  831. console.log(`📊 [价格API] ${symbol}: $${price.toFixed(2)}`)
  832. }
  833. }
  834. }
  835. } catch (error) {
  836. logger.debug('更新价格API数据失败', { symbol, error: error.message })
  837. }
  838. }
  839. /**
  840. * 从历史orderbook缓存获取价格
  841. */
  842. getHistoricalPrice(symbol) {
  843. try {
  844. // 检查所有缓存的orderbook数据,即使过期的也可以作为最后的参考
  845. const cacheKey = symbol
  846. const cached = this.orderbookPriceCache[cacheKey]
  847. if (cached && cached.bid > 0 && cached.ask > 0) {
  848. const midPrice = (cached.bid + cached.ask) / 2
  849. const ageMinutes = (Date.now() - cached.timestamp) / 1000 / 60
  850. console.log(`📊 [历史缓存] ${symbol}: $${midPrice.toFixed(2)} (${ageMinutes.toFixed(1)}分钟前)`)
  851. return midPrice
  852. }
  853. return 0
  854. } catch (error) {
  855. return 0
  856. }
  857. }
  858. getLastKnownPrice(symbol) {
  859. try {
  860. // 检查是否有缓存的orderbook价格
  861. const cacheKey = symbol
  862. const cached = this.orderbookPriceCache[cacheKey]
  863. if (cached && Date.now() - cached.timestamp < 120 * 1000) {
  864. // 延长到2分钟有效期
  865. const midPrice = (cached.bid + cached.ask) / 2
  866. return midPrice
  867. }
  868. // 如果没有缓存,触发异步获取
  869. this.updateOrderbookPriceAsync(symbol)
  870. // 返回BTC的保守估算价格,避免订单金额为0
  871. if (symbol.includes('BTC')) {
  872. console.log(`🔄 [估算价格] ${symbol}: $65000 (缓存过期)`)
  873. return 65000
  874. }
  875. return 0
  876. } catch (error) {
  877. // 错误时也返回估算价格
  878. if (symbol.includes('BTC')) {
  879. console.log(`⚠️ [错误价格] ${symbol}: $65000 (获取失败)`)
  880. return 65000
  881. }
  882. return 0
  883. }
  884. }
  885. async updateOrderbookPriceAsync(symbol) {
  886. console.log(`🔄 [价格获取] 开始获取 ${symbol} orderbook数据`)
  887. try {
  888. const client = this.accountManager.getFirstAvailableClient()
  889. console.log(`🔄 [价格获取] 获取到客户端:`, !!client, '有getOrderBook方法:', !!client?.getOrderBook)
  890. if (!client || !client.getOrderBook) {
  891. console.log(
  892. `⚠️ [价格获取] 客户端或getOrderBook方法不可用 - client: ${!!client}, getOrderBook: ${!!client?.getOrderBook}`,
  893. )
  894. return
  895. }
  896. // 调用正确的方法名
  897. const response = await client.getOrderBook(symbol, 5)
  898. if (!response) {
  899. console.log(`⚠️ [价格获取] orderbook响应为空`)
  900. return
  901. }
  902. // 原始orderbook数据已获取
  903. // 解析Pacifica API的orderbook格式
  904. const orderbook = this.parseOrderbookData(response)
  905. if (
  906. !orderbook ||
  907. !orderbook.bids ||
  908. !orderbook.asks ||
  909. orderbook.bids.length === 0 ||
  910. orderbook.asks.length === 0
  911. ) {
  912. console.log(`⚠️ [价格获取] orderbook数据解析后无效:`, orderbook)
  913. return
  914. }
  915. const bestBid = parseFloat(orderbook.bids[0].price)
  916. const bestAsk = parseFloat(orderbook.asks[0].price)
  917. if (!isNaN(bestBid) && !isNaN(bestAsk) && bestBid > 0 && bestAsk > 0) {
  918. this.orderbookPriceCache[symbol] = {
  919. bid: bestBid,
  920. ask: bestAsk,
  921. timestamp: Date.now(),
  922. }
  923. const midPrice = (bestBid + bestAsk) / 2
  924. console.log(`📊 [价格] ${symbol}: $${midPrice.toFixed(2)}`)
  925. }
  926. } catch (error) {
  927. logger.debug('更新orderbook价格失败', { symbol, error: error.message })
  928. }
  929. }
  930. /**
  931. * 预热orderbook价格缓存
  932. */
  933. async preloadOrderbookPrices() {
  934. const symbols = ['BTC-USD', 'BTCUSDT']
  935. for (const symbol of symbols) {
  936. try {
  937. await this.updateOrderbookPriceAsync(symbol)
  938. // 避免请求过快
  939. await new Promise(resolve => setTimeout(resolve, 200))
  940. } catch (error) {
  941. logger.debug('预热价格缓存失败', { symbol })
  942. }
  943. }
  944. console.log('📊 [价格缓存] orderbook价格缓存已预热')
  945. }
  946. /**
  947. * 解析Pacifica API的orderbook数据格式
  948. */
  949. parseOrderbookData(response) {
  950. try {
  951. const payload = response?.data ?? response
  952. // 格式1: payload.l = [bids[], asks[]] - 每个元素是 {p: price, a: amount, n: numOrders}
  953. if (Array.isArray(payload?.l) && payload.l.length >= 2) {
  954. const rawBids = Array.isArray(payload.l[0]) ? payload.l[0] : []
  955. const rawAsks = Array.isArray(payload.l[1]) ? payload.l[1] : []
  956. const bids = rawBids
  957. .map(lvl => ({
  958. price: String(lvl?.p ?? lvl?.price ?? ''),
  959. qty: String(lvl?.a ?? lvl?.amount ?? ''),
  960. }))
  961. .filter(x => x.price && x.qty && parseFloat(x.price) > 0)
  962. const asks = rawAsks
  963. .map(lvl => ({
  964. price: String(lvl?.p ?? lvl?.price ?? ''),
  965. qty: String(lvl?.a ?? lvl?.amount ?? ''),
  966. }))
  967. .filter(x => x.price && x.qty && parseFloat(x.price) > 0)
  968. if (bids.length > 0 && asks.length > 0) {
  969. return { bids, asks }
  970. }
  971. }
  972. // 格式2: Fallback - bids/asks 作为 [price, qty] 数组
  973. if (payload?.bids || payload?.asks) {
  974. const bids = (payload?.bids ?? [])
  975. .map(x => ({ price: String(x[0] ?? ''), qty: String(x[1] ?? '') }))
  976. .filter(x => x.price && x.qty && parseFloat(x.price) > 0)
  977. const asks = (payload?.asks ?? [])
  978. .map(x => ({ price: String(x[0] ?? ''), qty: String(x[1] ?? '') }))
  979. .filter(x => x.price && x.qty && parseFloat(x.price) > 0)
  980. if (bids.length > 0 && asks.length > 0) {
  981. return { bids, asks }
  982. }
  983. }
  984. // 格式3: 直接的bids/asks对象数组格式
  985. if (Array.isArray(payload?.bids) && Array.isArray(payload?.asks)) {
  986. const bids = payload.bids
  987. .map(x => ({
  988. price: String(x?.price ?? x?.p ?? ''),
  989. qty: String(x?.qty ?? x?.q ?? x?.amount ?? x?.a ?? ''),
  990. }))
  991. .filter(x => x.price && x.qty && parseFloat(x.price) > 0)
  992. const asks = payload.asks
  993. .map(x => ({
  994. price: String(x?.price ?? x?.p ?? ''),
  995. qty: String(x?.qty ?? x?.q ?? x?.amount ?? x?.a ?? ''),
  996. }))
  997. .filter(x => x.price && x.qty && parseFloat(x.price) > 0)
  998. if (bids.length > 0 && asks.length > 0) {
  999. return { bids, asks }
  1000. }
  1001. }
  1002. console.log(`⚠️ [价格解析] 无法识别的orderbook格式:`, JSON.stringify(payload, null, 2))
  1003. return null
  1004. } catch (error) {
  1005. console.log(`❌ [价格解析] orderbook解析出错:`, error.message)
  1006. return null
  1007. }
  1008. }
  1009. generateBalanceOrders(signal) {
  1010. if (!signal.targetAccount) return []
  1011. const amount = parseFloat(signal.amount)
  1012. const side = signal.side === 'buy' ? 'bid' : 'ask'
  1013. return [
  1014. {
  1015. accountId: signal.targetAccount,
  1016. symbol: 'BTC-USD',
  1017. amount,
  1018. side: side,
  1019. orderType: 'market',
  1020. reason: `敞口平衡-${signal.side}-${amount}`,
  1021. },
  1022. ]
  1023. }
  1024. processHedgeResults(results, orders) {
  1025. let successCount = 0
  1026. results.forEach((result, index) => {
  1027. if (result.success) {
  1028. successCount++
  1029. this.stats.successfulTrades++
  1030. const order = orders[index]
  1031. this.accountManager.updateTradeState(order.accountId, {
  1032. side: order.side,
  1033. amount: order.amount.toString(),
  1034. success: true,
  1035. })
  1036. } else {
  1037. this.stats.failedTrades++
  1038. }
  1039. })
  1040. this.stats.totalTrades += results.length
  1041. this.stats.lastTradeTime = Date.now()
  1042. console.log(`🔄 对冲执行完成: ${successCount}/${results.length} 成功`)
  1043. }
  1044. processVolumeBoostResults(results, orders, amount) {
  1045. let successCount = 0
  1046. results.forEach((result, index) => {
  1047. if (result.success) {
  1048. successCount++
  1049. console.log(`✅ 刷量订单${index + 1}成功: ${result.orderId || result.order_id || 'N/A'}`)
  1050. this.stats.totalVolume += parseFloat(amount)
  1051. const order = orders[index]
  1052. this.accountManager.updateTradeState(order.accountId, {
  1053. side: order.side,
  1054. amount: order.amount.toString(),
  1055. success: true,
  1056. })
  1057. } else {
  1058. console.log(`❌ 刷量订单${index + 1}失败: ${result.error}`)
  1059. }
  1060. })
  1061. this.stats.totalTrades += results.length
  1062. this.stats.successfulTrades += successCount
  1063. this.stats.failedTrades += results.length - successCount
  1064. this.stats.lastTradeTime = Date.now()
  1065. console.log(
  1066. `⚡ 刷量执行完成: ${successCount}/${results.length} 成功 - 增加交易量: ${(
  1067. parseFloat(amount) * successCount
  1068. ).toFixed(4)} BTC`,
  1069. )
  1070. }
  1071. /**
  1072. * 动态使用率平衡机制 - 保持70-80%目标使用率
  1073. */
  1074. async executeUtilizationBalancing(accounts, maxUtilizationRate) {
  1075. const targetMinUtilization = 0.7
  1076. const targetMaxUtilization = 0.8
  1077. try {
  1078. // 检查是否需要动态平衡
  1079. if (maxUtilizationRate >= targetMinUtilization && maxUtilizationRate <= targetMaxUtilization) {
  1080. console.log(`🎯 [动态平衡] 使用率${(maxUtilizationRate * 100).toFixed(1)}%在目标范围(70-80%)内,保持当前状态`)
  1081. return
  1082. }
  1083. if (maxUtilizationRate > targetMaxUtilization && maxUtilizationRate < 0.9) {
  1084. // 超出目标上限但未达到危险级别:适度减仓
  1085. console.log(`📉 [动态平衡] 使用率${(maxUtilizationRate * 100).toFixed(1)}%超出目标上限,执行适度减仓`)
  1086. await this.executeModerateReduction(accounts, maxUtilizationRate)
  1087. } else if (maxUtilizationRate < targetMinUtilization) {
  1088. // 低于目标下限:适度增仓
  1089. console.log(`📈 [动态平衡] 使用率${(maxUtilizationRate * 100).toFixed(1)}%低于目标下限,执行适度增仓`)
  1090. await this.executeModerateIncrease(accounts, maxUtilizationRate)
  1091. }
  1092. } catch (error) {
  1093. logger.error('动态使用率平衡失败', { error: error.message, maxUtilizationRate })
  1094. }
  1095. }
  1096. /**
  1097. * 执行双向同步减仓
  1098. */
  1099. async executeModerateReduction(accounts, utilizationRate) {
  1100. if (!this.hedgeManager) return
  1101. console.log(`🔄 [双向减仓] 开始执行双向同步减仓,当前使用率: ${(utilizationRate * 100).toFixed(1)}%`)
  1102. // 获取两个主要账户状态
  1103. const account1State = this.accountManager.getAccountState('pacifica-1')
  1104. const account2State = this.accountManager.getAccountState('pacifica-2')
  1105. if (!account1State || !account2State) {
  1106. console.log('⚠️ [双向减仓] 账户状态不可用,跳过减仓')
  1107. return
  1108. }
  1109. // 检查两个账户是否都有仓位
  1110. const pos1 = Math.abs(account1State.netPosition)
  1111. const pos2 = Math.abs(account2State.netPosition)
  1112. if (pos1 < 0.0001 && pos2 < 0.0001) {
  1113. console.log('ℹ️ [双向减仓] 当前无仓位,无需减仓')
  1114. return
  1115. }
  1116. // 计算减仓数量 - 根据使用率动态调整
  1117. let reductionRatio = 0.15 // 基础减仓比例15%
  1118. if (utilizationRate >= 0.95) {
  1119. reductionRatio = 0.3 // 高使用率时加大减仓比例
  1120. } else if (utilizationRate >= 0.9) {
  1121. reductionRatio = 0.2
  1122. }
  1123. const reduceAmount = Math.min(Math.max(pos1, pos2) * reductionRatio, 0.002)
  1124. if (reduceAmount < 0.0001) {
  1125. console.log('⚠️ [双向减仓] 计算的减仓数量过小,跳过')
  1126. return
  1127. }
  1128. console.log(`📊 [双向减仓] 减仓数量: ${reduceAmount.toFixed(4)} BTC (比例: ${(reductionRatio * 100).toFixed(1)}%)`)
  1129. // 同步双向减仓
  1130. const reductionOrders = []
  1131. // 账户1减仓
  1132. if (pos1 > 0.0001) {
  1133. const side1 = account1State.netPosition > 0 ? 'ask' : 'bid' // 平多仓用ask,平空仓用bid
  1134. reductionOrders.push({
  1135. accountId: 'pacifica-1',
  1136. symbol: 'BTC-USD',
  1137. amount: reduceAmount,
  1138. side: side1,
  1139. orderType: 'market',
  1140. reason: `双向减仓-降低使用率`,
  1141. })
  1142. }
  1143. // 账户2减仓
  1144. if (pos2 > 0.0001) {
  1145. const side2 = account2State.netPosition > 0 ? 'ask' : 'bid' // 平多仓用ask,平空仓用bid
  1146. reductionOrders.push({
  1147. accountId: 'pacifica-2',
  1148. symbol: 'BTC-USD',
  1149. amount: reduceAmount,
  1150. side: side2,
  1151. orderType: 'market',
  1152. reason: `双向减仓-降低使用率`,
  1153. })
  1154. }
  1155. if (reductionOrders.length > 0) {
  1156. const results = await this.hedgeManager.executeBatchHedge(reductionOrders)
  1157. console.log(`📉 [适度减仓] 执行${results.length}笔减仓订单,目标:降低使用率至70-80%`)
  1158. }
  1159. }
  1160. /**
  1161. * 执行适度增仓
  1162. */
  1163. async executeModerateIncrease(accounts, utilizationRate) {
  1164. if (!this.hedgeManager) return
  1165. // 找到有可用余额的账户进行增仓
  1166. const availableAccounts = accounts
  1167. .filter(account => account.availableBalance > 5) // 至少有5美元可用余额
  1168. .sort((a, b) => b.availableBalance - a.availableBalance)
  1169. .slice(0, 2) // 取前两个最有资金的账户
  1170. const increaseOrders = []
  1171. for (const account of availableAccounts) {
  1172. const maxIncrease = Math.min((account.availableBalance * 0.1) / 65000, 0.0008) // 使用10%可用余额,最多0.0008 BTC
  1173. if (maxIncrease >= 0.0001) {
  1174. const increaseAction = Math.random() > 0.5 ? 'bid' : 'ask'
  1175. increaseOrders.push({
  1176. accountId: account.id || 'pacifica-1',
  1177. symbol: 'BTC-USD',
  1178. amount: maxIncrease,
  1179. side: increaseAction,
  1180. orderType: 'market',
  1181. reason: `适度增仓-提升使用率至目标范围`,
  1182. })
  1183. }
  1184. }
  1185. if (increaseOrders.length > 0) {
  1186. const results = await this.hedgeManager.executeBatchHedge(increaseOrders)
  1187. console.log(`📈 [适度增仓] 执行${results.length}笔增仓订单,目标:提升使用率至70-80%`)
  1188. }
  1189. }
  1190. /**
  1191. * 紧急双向减仓
  1192. */
  1193. async executeEmergencyReduction(accounts, utilizationRate) {
  1194. if (!this.hedgeManager) return
  1195. console.log(`🚨 [紧急减仓] 执行大幅双向减仓,使用率: ${(utilizationRate * 100).toFixed(1)}%`)
  1196. // 获取两个主要账户状态
  1197. const account1State = this.accountManager.getAccountState('pacifica-1')
  1198. const account2State = this.accountManager.getAccountState('pacifica-2')
  1199. if (!account1State || !account2State) {
  1200. console.log('⚠️ [紧急减仓] 账户状态不可用,跳过减仓')
  1201. return
  1202. }
  1203. const pos1 = Math.abs(account1State.netPosition)
  1204. const pos2 = Math.abs(account2State.netPosition)
  1205. if (pos1 < 0.0001 && pos2 < 0.0001) {
  1206. console.log('ℹ️ [紧急减仓] 当前无仓位,无需减仓')
  1207. return
  1208. }
  1209. // 紧急减仓:减少50%仓位
  1210. const reductionRatio = 0.5
  1211. const reduceAmount = Math.min(Math.max(pos1, pos2) * reductionRatio, 0.003)
  1212. if (reduceAmount < 0.0001) {
  1213. console.log('⚠️ [紧急减仓] 计算的减仓数量过小,跳过')
  1214. return
  1215. }
  1216. console.log(
  1217. `🚨 [紧急减仓] 大幅减仓数量: ${reduceAmount.toFixed(4)} BTC (比例: ${(reductionRatio * 100).toFixed(1)}%)`,
  1218. )
  1219. // 同步双向减仓
  1220. const emergencyOrders = []
  1221. // 账户1减仓
  1222. if (pos1 > 0.0001) {
  1223. const side1 = account1State.netPosition > 0 ? 'ask' : 'bid'
  1224. emergencyOrders.push({
  1225. accountId: 'pacifica-1',
  1226. symbol: 'BTC-USD',
  1227. amount: reduceAmount,
  1228. side: side1,
  1229. orderType: 'market',
  1230. reason: `紧急双向减仓-降低高使用率`,
  1231. })
  1232. }
  1233. // 账户2减仓
  1234. if (pos2 > 0.0001) {
  1235. const side2 = account2State.netPosition > 0 ? 'ask' : 'bid'
  1236. emergencyOrders.push({
  1237. accountId: 'pacifica-2',
  1238. symbol: 'BTC-USD',
  1239. amount: reduceAmount,
  1240. side: side2,
  1241. orderType: 'market',
  1242. reason: `紧急双向减仓-降低高使用率`,
  1243. })
  1244. }
  1245. if (emergencyOrders.length > 0) {
  1246. try {
  1247. const results = await this.hedgeManager.executeBatchHedge(emergencyOrders)
  1248. console.log(`🚨 [紧急减仓] 执行${results.length}笔紧急减仓订单`)
  1249. } catch (error) {
  1250. logger.error('紧急减仓执行失败', { error: error.message })
  1251. }
  1252. }
  1253. }
  1254. /**
  1255. * 执行降低使用率策略
  1256. */
  1257. async executeUtilizationReduction(account, utilizationRate) {
  1258. if (!account || !this.hedgeManager) return
  1259. try {
  1260. console.log(`📉 [紧急降低使用率] 开始处理账户使用率: ${(utilizationRate * 100).toFixed(1)}%`)
  1261. // 紧急情况下执行大幅双向减仓
  1262. const accounts = Array.from(this.accountManager.getAllAccountStates().values())
  1263. await this.executeEmergencyReduction(accounts, utilizationRate)
  1264. // 策略2: 暂时降低交易频率
  1265. if (utilizationRate >= 0.95) {
  1266. console.log(`⏸️ [使用率控制] 极高使用率(${(utilizationRate * 100).toFixed(1)}%),暂停10秒交易`)
  1267. await new Promise(resolve => setTimeout(resolve, 10000))
  1268. } else if (utilizationRate >= 0.9) {
  1269. console.log(`⏸️ [使用率控制] 高使用率(${(utilizationRate * 100).toFixed(1)}%),暂停5秒交易`)
  1270. await new Promise(resolve => setTimeout(resolve, 5000))
  1271. }
  1272. // 策略3: 动态调整交易规模
  1273. await this.adjustTradingSizeForUtilization(utilizationRate)
  1274. } catch (error) {
  1275. logger.error('执行降低使用率策略失败', { error: error.message, utilizationRate })
  1276. }
  1277. }
  1278. /**
  1279. * 根据使用率动态调整交易规模和策略
  1280. */
  1281. async adjustTradingSizeForUtilization(utilizationRate) {
  1282. // 目标使用率范围:70-80%
  1283. const targetMinUtilization = 0.7
  1284. const targetMaxUtilization = 0.8
  1285. if (utilizationRate >= 0.9) {
  1286. // 过高使用率:强制减仓
  1287. this.riskLimits.maxPositionSize = 0.002
  1288. console.log(`📊 [使用率控制] 过高使用率${(utilizationRate * 100).toFixed(1)}%,执行强制减仓`)
  1289. await this.executeUtilizationRebalance('reduce', utilizationRate)
  1290. } else if (utilizationRate > targetMaxUtilization) {
  1291. // 超出目标上限:适度减仓
  1292. this.riskLimits.maxPositionSize = 0.005
  1293. console.log(`📊 [使用率控制] 超出目标上限${(utilizationRate * 100).toFixed(1)}%,执行适度减仓`)
  1294. await this.executeUtilizationRebalance('reduce', utilizationRate)
  1295. } else if (utilizationRate >= targetMinUtilization && utilizationRate <= targetMaxUtilization) {
  1296. // 目标范围内:保持平衡
  1297. this.riskLimits.maxPositionSize = 0.007
  1298. console.log(`🎯 [动态平衡] 使用率${(utilizationRate * 100).toFixed(1)}%在目标范围(70-80%)内`)
  1299. } else if (utilizationRate >= 0.5) {
  1300. // 低于目标:适度增仓
  1301. this.riskLimits.maxPositionSize = 0.008
  1302. console.log(`📈 [使用率控制] 使用率${(utilizationRate * 100).toFixed(1)}%偏低,执行适度增仓`)
  1303. await this.executeUtilizationRebalance('increase', utilizationRate)
  1304. } else {
  1305. // 使用率过低:积极增仓
  1306. this.riskLimits.maxPositionSize = 0.01
  1307. console.log(`🚀 [使用率控制] 使用率${(utilizationRate * 100).toFixed(1)}%过低,执行积极增仓`)
  1308. await this.executeUtilizationRebalance('increase', utilizationRate)
  1309. }
  1310. }
  1311. /**
  1312. * 执行使用率再平衡操作
  1313. */
  1314. async executeUtilizationRebalance(action, currentUtilization) {
  1315. try {
  1316. const accounts = await this.accountManager.getAllAccountStates()
  1317. const targetUtilization = 0.75 // 目标使用率75%
  1318. for (const [accountId, state] of Object.entries(accounts)) {
  1319. if (state.lastBalance <= 0) continue
  1320. const utilizationRate = Math.abs(state.lastBalance - state.availableBalance) / state.lastBalance
  1321. if (action === 'reduce' && utilizationRate > 0.8) {
  1322. // 减仓:通过平仓部分仓位来降低使用率
  1323. const targetReduction = (utilizationRate - targetUtilization) * state.lastBalance
  1324. const positionToClose = Math.min(
  1325. targetReduction / (await this.getCurrentPriceSync('BTC-USD')),
  1326. Math.abs(state.netPosition) * 0.3,
  1327. )
  1328. if (positionToClose > 0.00001) {
  1329. // 至少0.00001 BTC才值得平仓
  1330. console.log(
  1331. `📉 [减仓操作] ${accountId}: 当前使用率${(utilizationRate * 100).toFixed(
  1332. 1,
  1333. )}% → 目标75%, 平仓${positionToClose.toFixed(5)} BTC`,
  1334. )
  1335. const signal = await this.generateTradeSignal({
  1336. accountId,
  1337. symbol: 'BTC-USD',
  1338. action: 'close',
  1339. amount: positionToClose.toFixed(5),
  1340. side: state.netPosition > 0 ? 'ask' : 'bid',
  1341. reason: `使用率控制平仓 - 当前${(utilizationRate * 100).toFixed(1)}%`,
  1342. })
  1343. if (signal) {
  1344. await this.executeHedgeSignal(signal)
  1345. }
  1346. }
  1347. } else if (action === 'increase' && utilizationRate < 0.7) {
  1348. // 增仓:通过开仓来提高使用率
  1349. const targetIncrease = (targetUtilization - utilizationRate) * state.lastBalance
  1350. const positionToOpen = targetIncrease / (await this.getCurrentPriceSync('BTC-USD'))
  1351. if (positionToOpen > 0.00001 && state.availableBalance > 15) {
  1352. // 确保有足够余额
  1353. console.log(
  1354. `📈 [增仓操作] ${accountId}: 当前使用率${(utilizationRate * 100).toFixed(
  1355. 1,
  1356. )}% → 目标75%, 开仓${positionToOpen.toFixed(5)} BTC`,
  1357. )
  1358. const signal = await this.generateTradeSignal({
  1359. accountId,
  1360. symbol: 'BTC-USD',
  1361. action: 'open',
  1362. amount: positionToOpen.toFixed(5),
  1363. side: Math.random() > 0.5 ? 'bid' : 'ask',
  1364. reason: `使用率控制开仓 - 当前${(utilizationRate * 100).toFixed(1)}%`,
  1365. })
  1366. if (signal) {
  1367. await this.executeHedgeSignal(signal)
  1368. }
  1369. }
  1370. }
  1371. }
  1372. } catch (error) {
  1373. logger.error('执行使用率再平衡失败', { action, currentUtilization, error: error.message })
  1374. }
  1375. }
  1376. /**
  1377. * 检查是否因使用率过高需要跳过交易
  1378. */
  1379. shouldSkipTradeForUtilization() {
  1380. const accounts = Array.from(this.accountManager.getAllAccountStates().values())
  1381. let maxUtilizationRate = 0
  1382. let highUtilizationAccount = null
  1383. accounts.forEach(account => {
  1384. if (account.lastBalance > 0) {
  1385. const utilizationRate = Math.abs(account.lastBalance - account.availableBalance) / account.lastBalance
  1386. if (utilizationRate > maxUtilizationRate) {
  1387. maxUtilizationRate = utilizationRate
  1388. highUtilizationAccount = account
  1389. }
  1390. }
  1391. })
  1392. if (maxUtilizationRate >= 0.95) {
  1393. console.log(`🚨 [使用率控制] 使用率${(maxUtilizationRate * 100).toFixed(1)}%过高,启动主动降低机制`)
  1394. // 主动执行降低使用率操作,而不是仅仅跳过交易
  1395. this.executeEmergencyUtilizationReduction(highUtilizationAccount, maxUtilizationRate).catch(error => {
  1396. logger.error('紧急降低使用率失败', { error: error.message })
  1397. })
  1398. return true // 仍然跳过常规交易,但已触发降低机制
  1399. }
  1400. return false
  1401. }
  1402. /**
  1403. * 紧急降低使用率机制 - 当使用率>=95%时触发
  1404. */
  1405. async executeEmergencyUtilizationReduction(account, utilizationRate) {
  1406. console.log(`🚨 [紧急使用率控制] 启动主动降低机制,当前使用率: ${(utilizationRate * 100).toFixed(1)}%`)
  1407. // 直接调用现有的强力降低使用率方法
  1408. await this.executeUtilizationReduction(account, utilizationRate)
  1409. console.log(`✅ [紧急使用率控制] 主动降低机制执行完成`)
  1410. }
  1411. getStats() {
  1412. return { ...this.stats }
  1413. }
  1414. }