import 'dotenv/config' import AccountManager from '../src/accounts/accountManager' import { PacificaClient } from '../src/exchanges/pacifica/PacificaClient' import { PacificaAdapter } from '../src/exchanges/pacifica/PacificaAdapter' import { AsterAdapter } from '../src/exchanges/aster/asterAdapter' import { AsterExchangeAdapter } from '../src/exchanges/aster/AsterExchangeAdapter' async function main() { const am = new AccountManager() const short = (x: string) => (x?.length > 12 ? `${x.slice(0, 6)}…${x.slice(-6)}` : x) // Pacifica const pacClient = new PacificaClient({ baseUrl: process.env.PACIFICA_BASE_URL || 'https://api.pacifica.fi', wsUrl: process.env.PACIFICA_WS_URL || 'wss://ws.pacifica.fi/ws', apiKey: process.env.PACIFICA_API_KEY, privateKey: process.env.PACIFICA_ACCOUNT_PRIVATE_KEY || process.env.PACIFICA_PRIVATE_KEY, account: process.env.PACIFICA_ACCOUNT, agentWallet: process.env.PACIFICA_AGENT_WALLET, agentPrivateKey: process.env.PACIFICA_AGENT_PRIVATE_KEY, }) const pacAdapter = new PacificaAdapter(pacClient) // Aster const asterAdapter = new AsterAdapter({ rpcUrl: '', chainId: 0, routerAddress: '', httpBase: process.env.ASTER_HTTP_BASE || 'https://fapi.asterdex.com', defaultUser: process.env.ASTER_ORDER_USER || '', defaultSigner: process.env.ASTER_ORDER_SIGNER || '', apiKey: process.env.ASTER_API_KEY || '', apiSecret: process.env.ASTER_API_SECRET || '', }) const asterEx = new AsterExchangeAdapter(asterAdapter) // Register if (process.env.PACIFICA_ACCOUNT) { am.register({ exchange: 'pacifica', accountId: process.env.PACIFICA_ACCOUNT, adapter: pacAdapter }) // Subscribe WS channels for live state try { ;(pacAdapter as any).subscribePrices() } catch {} try { pacAdapter.subscribeAccountInfo() } catch {} try { pacAdapter.subscribeAccountPositions() } catch {} try { pacAdapter.subscribeBalance() } catch {} try { pacAdapter.subscribeTrades('BTC') } catch {} try { pacAdapter.subscribeDepth('BTC') } catch {} } if (process.env.ASTER_ORDER_USER) { am.register({ exchange: 'aster', accountId: String(process.env.ASTER_ORDER_USER), adapter: asterEx }) // 启动 Aster 账户 user stream(需要 ASTER_API_KEY/ASTER_API_SECRET) try { const lkInfo = await asterAdapter.ensureListenKey() console.log(`【Aster】listenKey 准备 | key=${lkInfo.listenKey} source=${lkInfo.source}`) await (asterEx as any).startAccountUserStream(process.env.ASTER_WS_BASE || 'wss://fstream.asterdex.com') console.log('【Aster】账户WS已连接') } catch (e: any) { console.warn('【Aster】账户WS启动失败:', e?.message || e) } } // Init check (soft) try { const res = await am.initCheck({ symbolHints: { aster: 'BTCUSDT', pacifica: 'BTC' }, soft: true, timeSkewWarnMs: 3000, }) const okAll = res.every(r => r.ok) console.log(`【自检】总体=${okAll ? '通过' : '有异常'}`) for (const r of res) { const tag = `${r.key.exchange}::${short(String(r.key.accountId))}` const detail = r.steps.map(s => `${s.name}:${s.ok ? 'ok' : 'fail'}`).join(', ') console.log(`【自检】${tag} => ${r.ok ? '通过' : '失败'} | ${detail}`) } } catch (e: any) { console.warn('【自检】失败', e?.message || e) } // Snapshot try { const snap = await am.snapshot({ cacheMs: 0 }) console.log(`【快照】余额=${snap.balances.length} 持仓=${snap.positions.length}`) } catch (e: any) { console.warn('【快照】失败', e?.message || e) } // Listen live account states const lastLineByKey = new Map() am.ws().on('am:account_state', (s: any) => { const ex = s.__am?.exchange || '?' const id = s.__am?.accountId || '?' if (ex === '?' || id === '?') return // 跳过无标签事件 const idShort = short(String(id || '')) const hasPos = Array.isArray(s.positionSummary) && s.positionSummary.length if (s.accountEquity || s.marginUsed || hasPos) { const pos = hasPos ? s.positionSummary.map((p: any) => `${p.symbol}:${p.netQty}`).join(',') : '-' const line = `【账户状态】[${ex}] ${idShort} | 权益=${s.accountEquity ?? '-'} 保证金占用=${ s.marginUsed ?? '-' } 仓位数=${s.posCount ?? (hasPos ? s.positionSummary.length : '-')} 挂单数=${ s.openOrderCount ?? '-' } | 净持仓=${pos}` const key = `${ex}::${id}` if (lastLineByKey.get(key) === line) return // 去重 lastLineByKey.set(key, line) console.log(line) } }) const secs = Number(process.env.ACCOUNT_SMOKE_DURATION_SEC || '20') console.log(`【运行】AccountManager smoke ~${secs}s ...`) // 设置超时和清理 const timeout = setTimeout(() => { console.log('【测试】时间限制到达,开始清理资源...') process.exit(0) }, secs * 1000) // 处理进程退出 process.on('SIGINT', () => { console.log('【测试】收到中断信号,清理资源...') clearTimeout(timeout) process.exit(0) }) await new Promise(r => setTimeout(r, secs * 1000)) clearTimeout(timeout) console.log('【测试】完成,清理资源...') } main().catch(e => { console.error('account manager smoke error', e?.message || e) process.exit(1) })