AdapterFactory.ts 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /**
  2. * 交易所适配器工厂
  3. * 统一管理适配器的创建、配置和初始化
  4. */
  5. import { ExchangeAdapter } from './ExchangeAdapter'
  6. import { BinanceAdapter } from './binance/BinanceAdapter'
  7. import { FutureConnector } from './binance/FutureConnector'
  8. import { PacificaAdapter } from './pacifica/PacificaAdapter'
  9. import { PacificaClient } from './pacifica/PacificaClient'
  10. import { AsterExchangeAdapter } from './aster/AsterExchangeAdapter'
  11. import { AsterAdapter } from './aster/asterAdapter'
  12. export type SupportedExchange = 'binance' | 'pacifica' | 'aster'
  13. export interface ExchangeConfig {
  14. exchange: SupportedExchange
  15. apiKey?: string
  16. apiSecret?: string
  17. baseUrl?: string
  18. wsUrl?: string
  19. privateKey?: string
  20. accountId?: string
  21. [key: string]: any
  22. }
  23. export interface AdapterFactoryResult {
  24. adapter: ExchangeAdapter
  25. config: ExchangeConfig
  26. initialized: boolean
  27. }
  28. export class AdapterFactory {
  29. private static instances = new Map<string, AdapterFactoryResult>()
  30. /**
  31. * 创建适配器实例
  32. */
  33. static async create(config: ExchangeConfig): Promise<AdapterFactoryResult> {
  34. const key = this.getInstanceKey(config)
  35. if (this.instances.has(key)) {
  36. return this.instances.get(key)!
  37. }
  38. const result = await this.createAdapter(config)
  39. this.instances.set(key, result)
  40. return result
  41. }
  42. /**
  43. * 从环境变量创建适配器
  44. */
  45. static async createFromEnv(exchange: SupportedExchange, accountId?: string): Promise<AdapterFactoryResult> {
  46. const config = this.loadConfigFromEnv(exchange, accountId)
  47. return this.create(config)
  48. }
  49. /**
  50. * 创建具体的适配器实例
  51. */
  52. private static async createAdapter(config: ExchangeConfig): Promise<AdapterFactoryResult> {
  53. let adapter: ExchangeAdapter
  54. let initialized = false
  55. try {
  56. switch (config.exchange) {
  57. case 'binance':
  58. adapter = await this.createBinanceAdapter(config)
  59. break
  60. case 'pacifica':
  61. adapter = await this.createPacificaAdapter(config)
  62. break
  63. case 'aster':
  64. adapter = await this.createAsterAdapter(config)
  65. break
  66. default:
  67. throw new Error(`不支持的交易所: ${config.exchange}`)
  68. }
  69. // 执行初始化测试
  70. initialized = await this.testAdapter(adapter, config.exchange)
  71. if (!initialized && config.exchange === 'pacifica') {
  72. // Pacifica 可能有网络问题,但仍然允许创建适配器
  73. console.warn(`⚠️ ${config.exchange} 适配器测试失败,但仍然创建实例`)
  74. initialized = true
  75. }
  76. return { adapter, config, initialized }
  77. } catch (error) {
  78. console.error(`❌ 创建 ${config.exchange} 适配器失败:`, error)
  79. // 对 Pacifica 的特殊处理
  80. if (config.exchange === 'pacifica' && error.message.includes('HTTP 500')) {
  81. console.warn(`⚠️ Pacifica 可能存在临时网络问题,跳过严格验证`)
  82. try {
  83. const adapter = await this.createPacificaAdapter(config)
  84. return { adapter, config, initialized: true }
  85. } catch (retryError) {
  86. console.error('❌ Pacifica 重试失败:', retryError)
  87. }
  88. }
  89. throw error
  90. }
  91. }
  92. /**
  93. * 创建币安适配器
  94. */
  95. private static async createBinanceAdapter(config: ExchangeConfig): Promise<BinanceAdapter> {
  96. if (!config.apiKey || !config.apiSecret) {
  97. throw new Error('Binance 适配器需要 apiKey 和 apiSecret')
  98. }
  99. const futureConnector = new FutureConnector(config.apiKey, config.apiSecret)
  100. return new BinanceAdapter(futureConnector)
  101. }
  102. /**
  103. * 创建Pacifica适配器
  104. */
  105. private static async createPacificaAdapter(config: ExchangeConfig): Promise<PacificaAdapter> {
  106. if (!config.privateKey || !config.accountId) {
  107. throw new Error('Pacifica 适配器需要 privateKey 和 accountId')
  108. }
  109. const client = new PacificaClient({
  110. baseUrl: config.baseUrl || 'https://api.pacifica.fi',
  111. wsUrl: config.wsUrl || 'wss://ws.pacifica.fi',
  112. account: config.accountId,
  113. privateKey: config.privateKey,
  114. agentWallet: config.agentWallet,
  115. agentPrivateKey: config.agentPrivateKey,
  116. })
  117. return new PacificaAdapter(client)
  118. }
  119. /**
  120. * 创建Aster适配器
  121. */
  122. private static async createAsterAdapter(config: ExchangeConfig): Promise<AsterExchangeAdapter> {
  123. if (!config.user || !config.signer || !config.privateKey) {
  124. throw new Error('Aster 适配器需要 user、signer 和 privateKey')
  125. }
  126. const asterConfig = {
  127. httpBase: config.baseUrl || 'https://fapi.asterdex.com',
  128. defaultUser: config.user,
  129. defaultSigner: config.signer,
  130. apiKey: config.apiKey,
  131. apiSecret: config.apiSecret,
  132. }
  133. const asterClient = new AsterAdapter(asterConfig)
  134. return new AsterExchangeAdapter(asterClient)
  135. }
  136. /**
  137. * 从环境变量加载配置
  138. */
  139. private static loadConfigFromEnv(exchange: SupportedExchange, accountId?: string): ExchangeConfig {
  140. switch (exchange) {
  141. case 'binance':
  142. return {
  143. exchange: 'binance',
  144. apiKey: process.env.BINANCE_API_KEY,
  145. apiSecret: process.env.BINANCE_SECRET_KEY,
  146. }
  147. case 'pacifica':
  148. return {
  149. exchange: 'pacifica',
  150. accountId: accountId || process.env.PACIFICA_ACCOUNT,
  151. privateKey: process.env.PACIFICA_ACCOUNT_PRIVATE_KEY || process.env.PACIFICA_PRIVATE_KEY,
  152. baseUrl: process.env.PACIFICA_BASE_URL,
  153. wsUrl: process.env.PACIFICA_WS_URL,
  154. agentWallet: process.env.PACIFICA_AGENT_WALLET,
  155. agentPrivateKey: process.env.PACIFICA_AGENT_PRIVATE_KEY,
  156. }
  157. case 'aster':
  158. return {
  159. exchange: 'aster',
  160. user: process.env.ASTER_ORDER_USER,
  161. signer: process.env.ASTER_ORDER_SIGNER || process.env.ASTER_API_KEY,
  162. privateKey: process.env.PRIVATE_KEY || process.env.PRIVATE_KEY2 || process.env.ASTER_API_SECRET,
  163. apiKey: process.env.ASTER_API_KEY,
  164. apiSecret: process.env.ASTER_API_SECRET,
  165. baseUrl: process.env.ASTER_HTTP_BASE,
  166. }
  167. default:
  168. throw new Error(`不支持的交易所: ${exchange}`)
  169. }
  170. }
  171. /**
  172. * 测试适配器基本功能
  173. */
  174. private static async testAdapter(adapter: ExchangeAdapter, exchange: string): Promise<boolean> {
  175. try {
  176. console.info(`🧪 测试 ${exchange} 适配器连接...`)
  177. // 基础连接测试
  178. const time = await adapter.time()
  179. if (!time || time <= 0) {
  180. console.warn(`⚠️ ${exchange} 时间同步测试失败`)
  181. return false
  182. }
  183. // 符号列表测试
  184. const symbols = await adapter.symbols()
  185. if (!Array.isArray(symbols)) {
  186. console.warn(`⚠️ ${exchange} 符号列表获取失败`)
  187. return false
  188. }
  189. console.info(`✅ ${exchange} 适配器测试通过 (获取到 ${symbols.length} 个交易对)`)
  190. return true
  191. } catch (error) {
  192. console.error(`❌ ${exchange} 适配器测试失败:`, error)
  193. return false
  194. }
  195. }
  196. /**
  197. * 生成实例缓存键
  198. */
  199. private static getInstanceKey(config: ExchangeConfig): string {
  200. const keyParts = [config.exchange]
  201. if (config.accountId) keyParts.push(config.accountId)
  202. if (config.user) keyParts.push(config.user)
  203. return keyParts.join('::')
  204. }
  205. /**
  206. * 清理所有适配器实例
  207. */
  208. static clearInstances(): void {
  209. this.instances.clear()
  210. }
  211. /**
  212. * 获取所有已创建的适配器
  213. */
  214. static getAllAdapters(): AdapterFactoryResult[] {
  215. return Array.from(this.instances.values())
  216. }
  217. /**
  218. * 健康检查所有适配器
  219. */
  220. static async healthCheck(): Promise<{ [exchange: string]: boolean }> {
  221. const results: { [exchange: string]: boolean } = {}
  222. for (const [key, { adapter, config }] of this.instances) {
  223. try {
  224. const isHealthy = await this.testAdapter(adapter, config.exchange)
  225. results[key] = isHealthy
  226. } catch {
  227. results[key] = false
  228. }
  229. }
  230. return results
  231. }
  232. }