| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- 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<string, string>()
- 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)
- })
|