AccountManager.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. import { logger } from '../../utils/logger.js'
  2. /**
  3. * 账户管理器 - 管理多个交易账户的状态和余额
  4. */
  5. export class AccountManager {
  6. constructor(cacheManager) {
  7. this.cacheManager = cacheManager
  8. this.accounts = []
  9. this.clients = new Map()
  10. this.accountStates = new Map()
  11. }
  12. async initialize() {
  13. logger.info('AccountManager初始化')
  14. }
  15. async start() {
  16. logger.info('AccountManager启动')
  17. // 启动定期余额更新
  18. this.startBalanceUpdates()
  19. }
  20. async stop() {
  21. logger.info('AccountManager停止')
  22. // 停止余额更新定时器
  23. if (this.balanceUpdateInterval) {
  24. clearInterval(this.balanceUpdateInterval)
  25. this.balanceUpdateInterval = undefined
  26. }
  27. this.clients.clear()
  28. this.accountStates.clear()
  29. }
  30. getStatus() {
  31. return {
  32. name: 'AccountManager',
  33. status: 'running',
  34. lastUpdate: Date.now(),
  35. details: {
  36. accountCount: this.accounts.length,
  37. activeClients: this.clients.size,
  38. accountStates: this.accountStates.size,
  39. },
  40. }
  41. }
  42. /**
  43. * 设置账户和客户端
  44. */
  45. setAccounts(accounts) {
  46. this.accounts = accounts
  47. this.initializeAccountStates()
  48. }
  49. /**
  50. * 获取账户列表
  51. */
  52. getAccounts() {
  53. return this.accounts
  54. }
  55. /**
  56. * 添加客户端
  57. */
  58. addClient(clientId, client) {
  59. this.clients.set(clientId, client)
  60. }
  61. /**
  62. * 获取客户端
  63. */
  64. getClient(clientId) {
  65. return this.clients.get(clientId)
  66. }
  67. /**
  68. * 获取所有客户端
  69. */
  70. getClients() {
  71. return this.clients
  72. }
  73. /**
  74. * 获取第一个可用的客户端
  75. */
  76. getFirstAvailableClient() {
  77. return this.clients.values().next().value
  78. }
  79. /**
  80. * 初始化账户状态
  81. */
  82. initializeAccountStates() {
  83. this.accounts.forEach((account, index) => {
  84. const accountId = `pacifica-${index + 1}`
  85. if (!this.accountStates.has(accountId)) {
  86. this.accountStates.set(accountId, {
  87. totalTrades: 0,
  88. netPosition: 0,
  89. totalVolume: 0,
  90. lastBalance: 0,
  91. availableBalance: 0,
  92. marginUsed: 0,
  93. needsRebalance: false,
  94. })
  95. }
  96. })
  97. }
  98. /**
  99. * 获取账户状态
  100. */
  101. getAccountState(accountId) {
  102. return this.accountStates.get(accountId)
  103. }
  104. /**
  105. * 获取所有账户状态
  106. */
  107. getAllAccountStates() {
  108. return this.accountStates
  109. }
  110. /**
  111. * 更新账户状态
  112. */
  113. updateAccountState(accountId, update) {
  114. const state = this.accountStates.get(accountId)
  115. if (state) {
  116. Object.assign(state, update)
  117. this.accountStates.set(accountId, state)
  118. }
  119. }
  120. /**
  121. * 获取账户余额(带缓存)
  122. */
  123. async getAccountBalance(accountId) {
  124. try {
  125. const client = this.clients.get(accountId)
  126. if (!client) {
  127. logger.warn(`客户端 ${accountId} 不存在`)
  128. return 0
  129. }
  130. const accountIndex = parseInt(accountId.split('-')[1]) - 1
  131. const account = this.accounts[accountIndex]
  132. if (!account) {
  133. logger.warn(`账户信息 ${accountId} 不存在,accountIndex: ${accountIndex}, 总账户数: ${this.accounts.length}`)
  134. logger.debug(
  135. `可用账户:`,
  136. this.accounts.map((acc, idx) => ({ index: idx, id: acc.id, account: acc.account })),
  137. )
  138. return 0
  139. }
  140. // 使用缓存的余额数据
  141. const cacheKey = `balance_${accountId}`
  142. const cachedBalance = this.cacheManager.get(cacheKey, this.cacheManager.getTTL('balance'))
  143. let result = cachedBalance
  144. if (!result) {
  145. // 缓存未命中,进行API调用
  146. logger.debug(`调用API获取余额: ${accountId}, 账户地址: ${account.account}`)
  147. result = await client.getBalances(account.account)
  148. this.cacheManager.set(cacheKey, result, this.cacheManager.getTTL('balance'))
  149. }
  150. if (result.success && result.data) {
  151. const accountEquity = parseFloat(result.data.account_equity || '0')
  152. const availableToSpend = parseFloat(result.data.available_to_spend || '0')
  153. const marginUsed = parseFloat(result.data.total_margin_used || '0')
  154. // 更新账户状态
  155. this.updateAccountState(accountId, {
  156. lastBalance: accountEquity,
  157. availableBalance: Math.max(0, availableToSpend),
  158. marginUsed: marginUsed,
  159. })
  160. // 同时更新仓位信息
  161. await this.updateAccountPosition(accountId)
  162. logger.debug(
  163. `账户 ${accountId} 财务更新: 权益$${accountEquity}, 可用$${availableToSpend}, 保证金$${marginUsed}`,
  164. )
  165. return accountEquity
  166. } else {
  167. logger.warn(`获取账户余额失败: ${result.error}`)
  168. return this.accountStates.get(accountId)?.lastBalance || 0
  169. }
  170. } catch (error) {
  171. logger.error(`获取账户余额出错: ${accountId}`, { error: error.message })
  172. return this.accountStates.get(accountId)?.lastBalance || 0
  173. }
  174. }
  175. /**
  176. * 更新账户仓位信息
  177. */
  178. async updateAccountPosition(accountId) {
  179. try {
  180. const client = this.clients.get(accountId)
  181. if (!client) {
  182. logger.warn(`客户端 ${accountId} 不存在`)
  183. return
  184. }
  185. const accountIndex = parseInt(accountId.split('-')[1]) - 1
  186. const account = this.accounts[accountIndex]
  187. if (!account) {
  188. logger.warn(`账户信息 ${accountId} 不存在`)
  189. return
  190. }
  191. const result = await client.getPositions(account.account)
  192. if (result.success && result.data && Array.isArray(result.data)) {
  193. let totalNetPosition = 0
  194. result.data.forEach(position => {
  195. if (position.symbol && position.symbol.includes('BTC')) {
  196. const amount = parseFloat(position.amount || '0')
  197. const side = position.side
  198. if (side === 'bid') {
  199. totalNetPosition += amount
  200. } else if (side === 'ask') {
  201. totalNetPosition -= amount
  202. }
  203. }
  204. })
  205. this.updateAccountState(accountId, { netPosition: totalNetPosition })
  206. logger.debug(`账户 ${accountId} 仓位更新: ${totalNetPosition.toFixed(4)} BTC`)
  207. }
  208. } catch (error) {
  209. logger.warn(`更新账户 ${accountId} 仓位失败`, { error: error.message })
  210. }
  211. }
  212. /**
  213. * 更新所有账户余额
  214. */
  215. async updateAllAccountBalances() {
  216. const balancePromises = Array.from(this.accountStates.keys()).map(accountId => this.getAccountBalance(accountId))
  217. try {
  218. await Promise.all(balancePromises)
  219. logger.debug('账户余额更新完成')
  220. } catch (error) {
  221. logger.warn('更新账户余额时部分失败', { error: error.message })
  222. }
  223. }
  224. /**
  225. * 更新交易状态
  226. */
  227. updateTradeState(accountId, trade) {
  228. const state = this.accountStates.get(accountId)
  229. if (!state) return
  230. state.totalTrades++
  231. if (trade.success) {
  232. const btcAmount = parseFloat(trade.amount)
  233. state.totalVolume += btcAmount
  234. // 更新净仓位
  235. if (trade.side === 'bid') {
  236. state.netPosition += btcAmount
  237. } else if (trade.side === 'ask') {
  238. state.netPosition -= btcAmount
  239. }
  240. }
  241. this.accountStates.set(accountId, state)
  242. }
  243. /**
  244. * 获取账户统计信息
  245. */
  246. getAccountStats() {
  247. const states = Array.from(this.accountStates.values())
  248. return {
  249. totalBalance: states.reduce((sum, state) => sum + state.lastBalance, 0),
  250. totalVolume: states.reduce((sum, state) => sum + state.totalVolume, 0),
  251. totalTrades: states.reduce((sum, state) => sum + state.totalTrades, 0),
  252. totalNetPosition: states.reduce((sum, state) => sum + state.netPosition, 0),
  253. }
  254. }
  255. /**
  256. * 启动定期余额更新
  257. */
  258. startBalanceUpdates() {
  259. // 每30秒更新一次余额
  260. this.balanceUpdateInterval = setInterval(async () => {
  261. try {
  262. await this.updateAllAccountBalances()
  263. } catch (error) {
  264. logger.error('定期余额更新失败', { error: error.message })
  265. }
  266. }, 30000)
  267. // 立即执行一次更新
  268. this.updateAllAccountBalances().catch(error => {
  269. logger.error('初始余额更新失败', { error: error.message })
  270. })
  271. logger.info('定期余额更新已启动,间隔30秒')
  272. }
  273. }