| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- 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,
- }
- }
- }
- }
|