account_manager_smoke.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import 'dotenv/config'
  2. import AccountManager from '../src/accounts/accountManager'
  3. import { PacificaClient } from '../src/exchanges/pacifica/PacificaClient'
  4. import { PacificaAdapter } from '../src/exchanges/pacifica/PacificaAdapter'
  5. import { AsterAdapter } from '../src/exchanges/aster/asterAdapter'
  6. import { AsterExchangeAdapter } from '../src/exchanges/aster/AsterExchangeAdapter'
  7. async function main() {
  8. const am = new AccountManager()
  9. const short = (x: string) => (x?.length > 12 ? `${x.slice(0, 6)}…${x.slice(-6)}` : x)
  10. // Pacifica
  11. const pacClient = new PacificaClient({
  12. baseUrl: process.env.PACIFICA_BASE_URL || 'https://api.pacifica.fi',
  13. wsUrl: process.env.PACIFICA_WS_URL || 'wss://ws.pacifica.fi/ws',
  14. apiKey: process.env.PACIFICA_API_KEY,
  15. privateKey: process.env.PACIFICA_ACCOUNT_PRIVATE_KEY || process.env.PACIFICA_PRIVATE_KEY,
  16. account: process.env.PACIFICA_ACCOUNT,
  17. agentWallet: process.env.PACIFICA_AGENT_WALLET,
  18. agentPrivateKey: process.env.PACIFICA_AGENT_PRIVATE_KEY,
  19. })
  20. const pacAdapter = new PacificaAdapter(pacClient)
  21. // Aster
  22. const asterAdapter = new AsterAdapter({
  23. rpcUrl: '',
  24. chainId: 0,
  25. routerAddress: '',
  26. httpBase: process.env.ASTER_HTTP_BASE || 'https://fapi.asterdex.com',
  27. defaultUser: process.env.ASTER_ORDER_USER || '',
  28. defaultSigner: process.env.ASTER_ORDER_SIGNER || '',
  29. apiKey: process.env.ASTER_API_KEY || '',
  30. apiSecret: process.env.ASTER_API_SECRET || '',
  31. })
  32. const asterEx = new AsterExchangeAdapter(asterAdapter)
  33. // Register
  34. if (process.env.PACIFICA_ACCOUNT) {
  35. am.register({ exchange: 'pacifica', accountId: process.env.PACIFICA_ACCOUNT, adapter: pacAdapter })
  36. // Subscribe WS channels for live state
  37. try {
  38. ;(pacAdapter as any).subscribePrices()
  39. } catch {}
  40. try {
  41. pacAdapter.subscribeAccountInfo()
  42. } catch {}
  43. try {
  44. pacAdapter.subscribeAccountPositions()
  45. } catch {}
  46. try {
  47. pacAdapter.subscribeBalance()
  48. } catch {}
  49. try {
  50. pacAdapter.subscribeTrades('BTC')
  51. } catch {}
  52. try {
  53. pacAdapter.subscribeDepth('BTC')
  54. } catch {}
  55. }
  56. if (process.env.ASTER_ORDER_USER) {
  57. am.register({ exchange: 'aster', accountId: String(process.env.ASTER_ORDER_USER), adapter: asterEx })
  58. // 启动 Aster 账户 user stream(需要 ASTER_API_KEY/ASTER_API_SECRET)
  59. try {
  60. const lkInfo = await asterAdapter.ensureListenKey()
  61. console.log(`【Aster】listenKey 准备 | key=${lkInfo.listenKey} source=${lkInfo.source}`)
  62. await (asterEx as any).startAccountUserStream(process.env.ASTER_WS_BASE || 'wss://fstream.asterdex.com')
  63. console.log('【Aster】账户WS已连接')
  64. } catch (e: any) {
  65. console.warn('【Aster】账户WS启动失败:', e?.message || e)
  66. }
  67. }
  68. // Init check (soft)
  69. try {
  70. const res = await am.initCheck({
  71. symbolHints: { aster: 'BTCUSDT', pacifica: 'BTC' },
  72. soft: true,
  73. timeSkewWarnMs: 3000,
  74. })
  75. const okAll = res.every(r => r.ok)
  76. console.log(`【自检】总体=${okAll ? '通过' : '有异常'}`)
  77. for (const r of res) {
  78. const tag = `${r.key.exchange}::${short(String(r.key.accountId))}`
  79. const detail = r.steps.map(s => `${s.name}:${s.ok ? 'ok' : 'fail'}`).join(', ')
  80. console.log(`【自检】${tag} => ${r.ok ? '通过' : '失败'} | ${detail}`)
  81. }
  82. } catch (e: any) {
  83. console.warn('【自检】失败', e?.message || e)
  84. }
  85. // Snapshot
  86. try {
  87. const snap = await am.snapshot({ cacheMs: 0 })
  88. console.log(`【快照】余额=${snap.balances.length} 持仓=${snap.positions.length}`)
  89. } catch (e: any) {
  90. console.warn('【快照】失败', e?.message || e)
  91. }
  92. // Listen live account states
  93. const lastLineByKey = new Map<string, string>()
  94. am.ws().on('am:account_state', (s: any) => {
  95. const ex = s.__am?.exchange || '?'
  96. const id = s.__am?.accountId || '?'
  97. if (ex === '?' || id === '?') return // 跳过无标签事件
  98. const idShort = short(String(id || ''))
  99. const hasPos = Array.isArray(s.positionSummary) && s.positionSummary.length
  100. if (s.accountEquity || s.marginUsed || hasPos) {
  101. const pos = hasPos ? s.positionSummary.map((p: any) => `${p.symbol}:${p.netQty}`).join(',') : '-'
  102. const line = `【账户状态】[${ex}] ${idShort} | 权益=${s.accountEquity ?? '-'} 保证金占用=${
  103. s.marginUsed ?? '-'
  104. } 仓位数=${s.posCount ?? (hasPos ? s.positionSummary.length : '-')} 挂单数=${
  105. s.openOrderCount ?? '-'
  106. } | 净持仓=${pos}`
  107. const key = `${ex}::${id}`
  108. if (lastLineByKey.get(key) === line) return // 去重
  109. lastLineByKey.set(key, line)
  110. console.log(line)
  111. }
  112. })
  113. const secs = Number(process.env.ACCOUNT_SMOKE_DURATION_SEC || '20')
  114. console.log(`【运行】AccountManager smoke ~${secs}s ...`)
  115. // 设置超时和清理
  116. const timeout = setTimeout(() => {
  117. console.log('【测试】时间限制到达,开始清理资源...')
  118. process.exit(0)
  119. }, secs * 1000)
  120. // 处理进程退出
  121. process.on('SIGINT', () => {
  122. console.log('【测试】收到中断信号,清理资源...')
  123. clearTimeout(timeout)
  124. process.exit(0)
  125. })
  126. await new Promise(r => setTimeout(r, secs * 1000))
  127. clearTimeout(timeout)
  128. console.log('【测试】完成,清理资源...')
  129. }
  130. main().catch(e => {
  131. console.error('account manager smoke error', e?.message || e)
  132. process.exit(1)
  133. })