/** * 交易所适配器工厂 * 统一管理适配器的创建、配置和初始化 */ import { ExchangeAdapter } from './ExchangeAdapter' import { BinanceAdapter } from './binance/BinanceAdapter' import { FutureConnector } from './binance/FutureConnector' import { PacificaAdapter } from './pacifica/PacificaAdapter' import { PacificaClient } from './pacifica/PacificaClient' import { AsterExchangeAdapter } from './aster/AsterExchangeAdapter' import { AsterAdapter } from './aster/asterAdapter' export type SupportedExchange = 'binance' | 'pacifica' | 'aster' export interface ExchangeConfig { exchange: SupportedExchange apiKey?: string apiSecret?: string baseUrl?: string wsUrl?: string privateKey?: string accountId?: string [key: string]: any } export interface AdapterFactoryResult { adapter: ExchangeAdapter config: ExchangeConfig initialized: boolean } export class AdapterFactory { private static instances = new Map() /** * 创建适配器实例 */ static async create(config: ExchangeConfig): Promise { const key = this.getInstanceKey(config) if (this.instances.has(key)) { return this.instances.get(key)! } const result = await this.createAdapter(config) this.instances.set(key, result) return result } /** * 从环境变量创建适配器 */ static async createFromEnv(exchange: SupportedExchange, accountId?: string): Promise { const config = this.loadConfigFromEnv(exchange, accountId) return this.create(config) } /** * 创建具体的适配器实例 */ private static async createAdapter(config: ExchangeConfig): Promise { let adapter: ExchangeAdapter let initialized = false try { switch (config.exchange) { case 'binance': adapter = await this.createBinanceAdapter(config) break case 'pacifica': adapter = await this.createPacificaAdapter(config) break case 'aster': adapter = await this.createAsterAdapter(config) break default: throw new Error(`不支持的交易所: ${config.exchange}`) } // 执行初始化测试 initialized = await this.testAdapter(adapter, config.exchange) if (!initialized && config.exchange === 'pacifica') { // Pacifica 可能有网络问题,但仍然允许创建适配器 console.warn(`⚠️ ${config.exchange} 适配器测试失败,但仍然创建实例`) initialized = true } return { adapter, config, initialized } } catch (error) { console.error(`❌ 创建 ${config.exchange} 适配器失败:`, error) // 对 Pacifica 的特殊处理 if (config.exchange === 'pacifica' && error.message.includes('HTTP 500')) { console.warn(`⚠️ Pacifica 可能存在临时网络问题,跳过严格验证`) try { const adapter = await this.createPacificaAdapter(config) return { adapter, config, initialized: true } } catch (retryError) { console.error('❌ Pacifica 重试失败:', retryError) } } throw error } } /** * 创建币安适配器 */ private static async createBinanceAdapter(config: ExchangeConfig): Promise { if (!config.apiKey || !config.apiSecret) { throw new Error('Binance 适配器需要 apiKey 和 apiSecret') } const futureConnector = new FutureConnector(config.apiKey, config.apiSecret) return new BinanceAdapter(futureConnector) } /** * 创建Pacifica适配器 */ private static async createPacificaAdapter(config: ExchangeConfig): Promise { if (!config.privateKey || !config.accountId) { throw new Error('Pacifica 适配器需要 privateKey 和 accountId') } const client = new PacificaClient({ baseUrl: config.baseUrl || 'https://api.pacifica.fi', wsUrl: config.wsUrl || 'wss://ws.pacifica.fi', account: config.accountId, privateKey: config.privateKey, agentWallet: config.agentWallet, agentPrivateKey: config.agentPrivateKey, }) return new PacificaAdapter(client) } /** * 创建Aster适配器 */ private static async createAsterAdapter(config: ExchangeConfig): Promise { if (!config.user || !config.signer || !config.privateKey) { throw new Error('Aster 适配器需要 user、signer 和 privateKey') } const asterConfig = { httpBase: config.baseUrl || 'https://fapi.asterdex.com', defaultUser: config.user, defaultSigner: config.signer, apiKey: config.apiKey, apiSecret: config.apiSecret, } const asterClient = new AsterAdapter(asterConfig) return new AsterExchangeAdapter(asterClient) } /** * 从环境变量加载配置 */ private static loadConfigFromEnv(exchange: SupportedExchange, accountId?: string): ExchangeConfig { switch (exchange) { case 'binance': return { exchange: 'binance', apiKey: process.env.BINANCE_API_KEY, apiSecret: process.env.BINANCE_SECRET_KEY, } case 'pacifica': return { exchange: 'pacifica', accountId: accountId || process.env.PACIFICA_ACCOUNT, privateKey: process.env.PACIFICA_ACCOUNT_PRIVATE_KEY || process.env.PACIFICA_PRIVATE_KEY, baseUrl: process.env.PACIFICA_BASE_URL, wsUrl: process.env.PACIFICA_WS_URL, agentWallet: process.env.PACIFICA_AGENT_WALLET, agentPrivateKey: process.env.PACIFICA_AGENT_PRIVATE_KEY, } case 'aster': return { exchange: 'aster', user: process.env.ASTER_ORDER_USER, signer: process.env.ASTER_ORDER_SIGNER || process.env.ASTER_API_KEY, privateKey: process.env.PRIVATE_KEY || process.env.PRIVATE_KEY2 || process.env.ASTER_API_SECRET, apiKey: process.env.ASTER_API_KEY, apiSecret: process.env.ASTER_API_SECRET, baseUrl: process.env.ASTER_HTTP_BASE, } default: throw new Error(`不支持的交易所: ${exchange}`) } } /** * 测试适配器基本功能 */ private static async testAdapter(adapter: ExchangeAdapter, exchange: string): Promise { try { console.info(`🧪 测试 ${exchange} 适配器连接...`) // 基础连接测试 const time = await adapter.time() if (!time || time <= 0) { console.warn(`⚠️ ${exchange} 时间同步测试失败`) return false } // 符号列表测试 const symbols = await adapter.symbols() if (!Array.isArray(symbols)) { console.warn(`⚠️ ${exchange} 符号列表获取失败`) return false } console.info(`✅ ${exchange} 适配器测试通过 (获取到 ${symbols.length} 个交易对)`) return true } catch (error) { console.error(`❌ ${exchange} 适配器测试失败:`, error) return false } } /** * 生成实例缓存键 */ private static getInstanceKey(config: ExchangeConfig): string { const keyParts = [config.exchange] if (config.accountId) keyParts.push(config.accountId) if (config.user) keyParts.push(config.user) return keyParts.join('::') } /** * 清理所有适配器实例 */ static clearInstances(): void { this.instances.clear() } /** * 获取所有已创建的适配器 */ static getAllAdapters(): AdapterFactoryResult[] { return Array.from(this.instances.values()) } /** * 健康检查所有适配器 */ static async healthCheck(): Promise<{ [exchange: string]: boolean }> { const results: { [exchange: string]: boolean } = {} for (const [key, { adapter, config }] of this.instances) { try { const isHealthy = await this.testAdapter(adapter, config.exchange) results[key] = isHealthy } catch { results[key] = false } } return results } }