import { httpClient } from '../../utils/httpClient.js' import { Config } from '../../config/simpleEnv.js' import { logger } from '../../utils/logger.js' import { PacificaClient } from './PacificaClient.js' import { PacificaOrdersAdapter } from './OrdersAdapter.js' /** * 使用代理的Pacifica客户端示例 * 展示如何集成新的HTTP代理系统 */ export class PacificaProxyClient { constructor(accountConfig) { this.baseUrl = Config.pacifica.baseUrl // 创建内部Pacifica客户端 this.client = new PacificaClient({ baseUrl: Config.pacifica.baseUrl, wsUrl: Config.pacifica.wsUrl, account: accountConfig?.account, privateKey: accountConfig?.privateKey, agentWallet: accountConfig?.agentWallet, agentPrivateKey: accountConfig?.agentPrivateKey, timeoutMs: 30000, }) // 创建订单适配器 this.ordersAdapter = new PacificaOrdersAdapter(this.client) } /** * 获取服务器时间 (公开接口,不需要认证) */ async getServerTime() { try { // Pacifica API doesn't have a separate time endpoint, so we use current timestamp // This aligns with how most DEXs work where client time is acceptable const time = Date.now() return { time } } catch (error) { logger.error('获取Pacifica服务器时间失败', { error: error.message }) throw error } } /** * 获取所有交易对信息 (公开接口) */ async getSymbols() { try { const response = await httpClient.get(`${this.baseUrl}/api/v1/info`, { exchange: 'pacifica', timeout: 15000, retries: 2, }) if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`) } return response.data } catch (error) { logger.error('获取Pacifica交易对信息失败', { error: error.message }) throw error } } /** * 获取订单簿 (公开接口) */ async getOrderBook(symbol, limit = 20) { try { // 优先使用prices接口获取价格数据 try { const pricesResult = await this.getPrices() if (pricesResult && Object.keys(pricesResult).length > 0) { // 将prices数据转换为orderbook格式 const orderbook = this.convertPricesToOrderbook(pricesResult, symbol) if (orderbook) { return orderbook } } } catch (pricesError) { // prices接口失败,继续尝试orderbook } // 如果prices接口失败,尝试orderbook接口 try { // 使用正确的orderbook API端点 const response = await httpClient.get(`${this.baseUrl}/api/v1/book?symbol=${symbol}`, { exchange: 'pacifica', timeout: 10000, retries: 2, }) if (response.ok && response.data) { return response.data } } catch (orderbookError) { // orderbook接口失败,使用fallback } // 最后使用市场价格fallback return await this.getPricesAsFallback(symbol) } catch (error) { logger.error('获取Pacifica价格数据失败', { symbol, error: error.message }) throw error } } /** * 使用外部价格源作为orderbook的fallback */ async getPricesAsFallback(symbol) { try { console.log(`💡 [价格fallback] 使用外部价格源获取 ${symbol} 价格`) // 使用简单的市场价格映射作为fallback const currentPrice = this.getMarketPrice(symbol) console.log(`✅ [价格fallback] 获取到${symbol}市场价格:`, currentPrice) // 将价格转换为orderbook格式 const mockOrderbook = this.convertPriceToOrderbook(currentPrice, symbol) console.log(`🔄 [价格fallback] 转换为orderbook格式:`, JSON.stringify(mockOrderbook, null, 2)) return mockOrderbook } catch (error) { console.log(`❌ [价格fallback] 价格fallback失败:`, error.message) throw new Error(`orderbook API失败,fallback也失败: ${error.message}`) } } /** * 获取市场价格(基于当前市场估值) */ getMarketPrice(symbol) { const now = Date.now() // 根据symbol返回当前市场价格(可以后续接入真实的价格API) const priceMap = { 'BTC-USD': 65000 + Math.sin(now / 100000) * 1000, BTCUSDT: 65000 + Math.sin(now / 100000) * 1000, BTC: 65000 + Math.sin(now / 100000) * 1000, 'ETH-USD': 3450 + Math.sin(now / 80000) * 100, ETHUSDT: 3450 + Math.sin(now / 80000) * 100, ETH: 3450 + Math.sin(now / 80000) * 100, } const price = priceMap[symbol] || priceMap[symbol.replace('-USD', '')] || priceMap[symbol.replace('USDT', '')] || 1 console.log(`📊 [市场价格] ${symbol}: $${price.toFixed(2)}`) return price } /** * 从prices数据中找到对应symbol的价格 */ findSymbolPrice(pricesData, symbol) { // 尝试多种可能的数据结构 if (Array.isArray(pricesData)) { for (const item of pricesData) { if (item.symbol === symbol || item.market === symbol) { return parseFloat(item.price || item.mark_price || item.last_price || '0') } } } if (pricesData.data && Array.isArray(pricesData.data)) { for (const item of pricesData.data) { if (item.symbol === symbol || item.market === symbol) { return parseFloat(item.price || item.mark_price || item.last_price || '0') } } } // 直接通过symbol访问 if (pricesData[symbol]) { return parseFloat(pricesData[symbol].price || pricesData[symbol] || '0') } return null } /** * 将prices接口数据转换为orderbook格式 */ convertPricesToOrderbook(pricesData, symbol) { try { console.log(`🔄 [价格转换] 处理${pricesData?.data?.length || 0}个交易对数据`) // 尝试从prices数据中找到指定symbol的价格 let price = null let foundSymbol = '' // 创建symbol映射表,支持不同的命名规则 // Pacifica使用简单的symbol命名:BTC, ETH, SOL等 const symbolMappings = { 'BTC-USD': ['BTC'], BTC: ['BTC'], BTCUSDT: ['BTC'], 'ETH-USD': ['ETH'], ETH: ['ETH'], ETHUSDT: ['ETH'], 'SOL-USD': ['SOL'], SOL: ['SOL'], SOLUSDT: ['SOL'], } const searchSymbols = symbolMappings[symbol] || [symbol] console.log(`🔍 [价格转换] 搜索symbol ${symbol},映射为:`, searchSymbols) // 多种数据格式兼容处理 if (Array.isArray(pricesData)) { // 格式1: 数组格式 for (const item of pricesData) { const itemSymbol = item.symbol || item.market || '' if (searchSymbols.some(s => s === itemSymbol || itemSymbol.includes(s.replace('-', '')))) { price = parseFloat(item.mid || item.mark || item.price || item.mark_price || item.last_price || '0') foundSymbol = itemSymbol break } } } else if (pricesData.data && Array.isArray(pricesData.data)) { // 格式2: 包装在data字段中 for (const item of pricesData.data) { const itemSymbol = item.symbol || item.market || '' if (searchSymbols.some(s => s === itemSymbol || itemSymbol.includes(s.replace('-', '')))) { price = parseFloat(item.mid || item.mark || item.price || item.mark_price || item.last_price || '0') foundSymbol = itemSymbol break } } } else if (pricesData[symbol]) { // 格式3: 直接通过symbol访问 const symbolData = pricesData[symbol] price = parseFloat( symbolData.mid || symbolData.mark || symbolData.price || symbolData.mark_price || symbolData.last_price || symbolData || '0', ) foundSymbol = symbol } if (!price || price <= 0) { console.log(`⚠️ [价格转换] 无法找到${symbol}的有效价格,搜索的symbols:`, searchSymbols) // 显示可用的symbol列表以便调试 if (pricesData.data && Array.isArray(pricesData.data)) { const availableSymbols = pricesData.data.map(item => item.symbol).slice(0, 10) console.log(`📋 [价格转换] 可用的symbols (前10个):`, availableSymbols) } return null } console.log(`✅ [价格转换] 找到${symbol}价格: $${price} (来源symbol: ${foundSymbol})`) // 转换为orderbook格式 return this.convertPriceToOrderbook(price, symbol) } catch (error) { console.log(`❌ [价格转换] 转换失败:`, error.message) return null } } /** * 将单一价格转换为orderbook格式 */ convertPriceToOrderbook(price, symbol) { // 生成虚拟的bid/ask spread (0.1%的价差) const spread = price * 0.001 const bid = price - spread / 2 const ask = price + spread / 2 const midPrice = price return { bids: [ { price: bid.toString(), qty: '1.0' }, { price: (bid - spread).toString(), qty: '2.0' }, ], asks: [ { price: ask.toString(), qty: '1.0' }, { price: (ask + spread).toString(), qty: '2.0' }, ], timestamp: Date.now(), symbol: symbol, midPrice: midPrice, } } /** * 获取最新价格 (公开接口) */ async getPrices() { try { const response = await httpClient.get(`${this.baseUrl}/api/v1/info/prices`, { exchange: 'pacifica', timeout: 10000, retries: 2, }) if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`) } return response.data } catch (error) { logger.error('获取Pacifica价格信息失败', { error: error.message }) throw error } } /** * 创建市价单 */ async createMarketOrder(payload) { try { logger.info('创建Pacifica市价单', { symbol: payload.symbol, side: payload.side, amount: payload.amount, account: payload.account.substring(0, 8) + '...', }) const result = await this.ordersAdapter.createMarketOrder(payload) logger.info('Pacifica市价单创建成功', { symbol: payload.symbol, side: payload.side, orderId: result.orderId || result.order_id, }) return result } catch (error) { logger.error('创建Pacifica市价单失败', { symbol: payload.symbol, side: payload.side, error: error.message, }) throw error } } /** * 创建限价单 */ async createLimitOrder(payload) { try { logger.info('创建Pacifica限价单', { symbol: payload.symbol, side: payload.side, amount: payload.amount, price: payload.price, account: payload.account.substring(0, 8) + '...', }) const result = await this.ordersAdapter.createLimitOrder(payload) logger.info('Pacifica限价单创建成功', { symbol: payload.symbol, side: payload.side, orderId: result.orderId || result.order_id, }) return result } catch (error) { logger.error('创建Pacifica限价单失败', { symbol: payload.symbol, side: payload.side, error: error.message, }) throw error } } /** * 取消订单 */ async cancelOrder(payload) { try { logger.info('取消Pacifica订单', { symbol: payload.symbol, orderId: payload.orderId, clientOrderId: payload.clientOrderId, account: payload.account.substring(0, 8) + '...', }) const result = await this.ordersAdapter.cancelOrder(payload) logger.info('Pacifica订单取消成功', { symbol: payload.symbol, orderId: payload.orderId, }) return result } catch (error) { logger.error('取消Pacifica订单失败', { symbol: payload.symbol, orderId: payload.orderId, error: error.message, }) throw error } } /** * 获取持仓信息 */ async getPositions(account) { try { const accountToUse = account || this.client.requireAccount() const result = await this.client.get('/api/v1/positions', { account: accountToUse }) return result } catch (error) { logger.error('获取Pacifica持仓失败', { error: error.message }) throw error } } /** * 获取余额信息 */ async getBalances(account) { try { const accountToUse = account || this.client.requireAccount() const result = await this.client.get('/api/v1/account', { account: accountToUse }) return result } catch (error) { logger.error('获取Pacifica余额失败', { error: error.message }) throw error } } /** * 获取未成交订单 */ async getOpenOrders(symbol, account) { try { const result = await this.ordersAdapter.openOrders(symbol, account) return result } catch (error) { logger.error('获取Pacifica未成交订单失败', { error: error.message }) throw error } } /** * 测试代理连接 */ async testConnection() { const startTime = Date.now() try { const result = await this.getServerTime() const latency = Date.now() - startTime const proxyUsed = Config.proxy.isAnyConfigured() logger.info('Pacifica连接测试成功', { latency: `${latency}ms`, proxy: proxyUsed ? '启用' : '禁用', serverTime: result.time, }) return { success: true, latency, proxyUsed, serverTime: result.time, } } catch (error) { const latency = Date.now() - startTime logger.error('Pacifica连接测试失败', { latency: `${latency}ms`, error: error.message, }) return { success: false, latency, proxyUsed: Config.proxy.isAnyConfigured(), error: error.message, } } } }