import { logger } from '../../utils/logger.js' /** * 止盈止损服务 - 管理止损订单和价格监控 */ export class StopLossService { constructor(accountManager, cacheManager) { this.accountManager = accountManager this.cacheManager = cacheManager this.activeStopLossOrders = new Map() this.activeTakeProfitOrders = new Map() this.lastPriceCheck = new Map() this.monitoringIntervals = new Map() this.stopLossConfig = { defaultStopLoss: 0.015, defaultTakeProfit: 0.025, enableTrailing: true, trailingPercent: 0.005, // 0.5% 追踪止损 } } async initialize() { logger.info('StopLossService初始化') } async start() { logger.info('StopLossService启动') } async stop() { logger.info('StopLossService停止') // 清理所有监控间隔 this.monitoringIntervals.forEach(interval => clearInterval(interval)) this.monitoringIntervals.clear() this.activeStopLossOrders.clear() this.activeTakeProfitOrders.clear() } getStatus() { return { name: 'StopLossService', status: 'running', lastUpdate: Date.now(), details: { activeStopLoss: this.activeStopLossOrders.size, activeTakeProfit: this.activeTakeProfitOrders.size, monitoringSymbols: this.monitoringIntervals.size, }, } } /** * 设置止盈止损订单 */ async setupStopLossAndTakeProfit(params) { const { parentOrderId, symbol, amount, side, currentPrice, clientId, stopLoss, takeProfit, enableTrailing = false, } = params try { // 计算止损价格 let stopLossPrice if (stopLoss) { stopLossPrice = stopLoss } else { const stopLossPercent = this.stopLossConfig.defaultStopLoss if (side === 'buy') { stopLossPrice = currentPrice * (1 - stopLossPercent) } else { stopLossPrice = currentPrice * (1 + stopLossPercent) } } // 计算止盈价格 let takeProfitPrice if (takeProfit) { takeProfitPrice = takeProfit } else { const takeProfitPercent = this.stopLossConfig.defaultTakeProfit if (side === 'buy') { takeProfitPrice = currentPrice * (1 + takeProfitPercent) } else { takeProfitPrice = currentPrice * (1 - takeProfitPercent) } } // 创建止损订单 if (stopLossPrice) { const stopLossOrder = { orderId: `sl_${parentOrderId}_${Date.now()}`, symbol, amount, stopPrice: stopLossPrice, side: side === 'buy' ? 'sell' : 'buy', accountId: clientId, timestamp: Date.now(), isActive: true, } this.activeStopLossOrders.set(stopLossOrder.orderId, stopLossOrder) logger.info('止损订单已设置', { orderId: stopLossOrder.orderId, stopPrice: stopLossPrice, enableTrailing, }) } // 创建止盈订单 if (takeProfitPrice) { const takeProfitOrder = { orderId: `tp_${parentOrderId}_${Date.now()}`, symbol, amount, targetPrice: takeProfitPrice, side: side === 'buy' ? 'sell' : 'buy', accountId: clientId, timestamp: Date.now(), isActive: true, } this.activeTakeProfitOrders.set(takeProfitOrder.orderId, takeProfitOrder) logger.info('止盈订单已设置', { orderId: takeProfitOrder.orderId, targetPrice: takeProfitPrice, }) } // 启动价格监控 if (stopLossPrice || takeProfitPrice) { this.startPriceMonitoring(symbol) } console.log( `🎯 止盈止损已设置: ${stopLossPrice ? `止损@$${stopLossPrice.toFixed(2)}` : ''} ${ takeProfitPrice ? `止盈@$${takeProfitPrice.toFixed(2)}` : '' }`, ) } catch (error) { logger.error('设置止盈止损失败', { error: error.message, params }) } } /** * 启动价格监控 */ startPriceMonitoring(symbol) { // 避免重复启动监控 if (this.monitoringIntervals.has(symbol)) { return } this.lastPriceCheck.set(symbol, { price: 0, timestamp: Date.now() }) // 每5秒检查一次价格触发 const monitoringInterval = setInterval(async () => { try { await this.checkStopLossAndTakeProfitTriggers(symbol) // 如果没有活跃的止盈止损订单,停止监控 const hasActiveOrders = Array.from(this.activeStopLossOrders.values()).some(order => order.symbol === symbol && order.isActive) || Array.from(this.activeTakeProfitOrders.values()).some(order => order.symbol === symbol && order.isActive) if (!hasActiveOrders) { clearInterval(monitoringInterval) this.monitoringIntervals.delete(symbol) this.lastPriceCheck.delete(symbol) logger.info(`价格监控已停止: ${symbol}`) } } catch (error) { logger.error('价格监控检查失败', { symbol, error: error.message }) } }, 5000) this.monitoringIntervals.set(symbol, monitoringInterval) logger.info(`价格监控已启动: ${symbol}`) } /** * 检查止盈止损触发条件 */ async checkStopLossAndTakeProfitTriggers(symbol) { try { const currentPrice = await this.getCurrentPrice(symbol) // 检查止损触发 for (const [orderId, stopLossOrder] of this.activeStopLossOrders) { if (!stopLossOrder.isActive || stopLossOrder.symbol !== symbol) continue const shouldTrigger = stopLossOrder.side === 'sell' ? currentPrice <= stopLossOrder.stopPrice : currentPrice >= stopLossOrder.stopPrice if (shouldTrigger) { await this.executeTrigger('stop_loss', stopLossOrder, currentPrice) } } // 检查止盈触发 for (const [orderId, takeProfitOrder] of this.activeTakeProfitOrders) { if (!takeProfitOrder.isActive || takeProfitOrder.symbol !== symbol) continue const shouldTrigger = takeProfitOrder.side === 'sell' ? currentPrice >= takeProfitOrder.targetPrice : currentPrice <= takeProfitOrder.targetPrice if (shouldTrigger) { await this.executeTrigger('take_profit', takeProfitOrder, currentPrice) } } // 更新价格检查记录 this.lastPriceCheck.set(symbol, { price: currentPrice, timestamp: Date.now() }) } catch (error) { logger.error('检查止盈止损触发失败', { symbol, error: error.message }) } } /** * 执行止盈止损触发 */ async executeTrigger(type, order, currentPrice) { try { const client = this.accountManager.getClient(order.accountId) if (!client) { logger.error(`客户端 ${order.accountId} 不可用,无法执行${type}`) return } const side = order.side === 'sell' ? 'ask' : 'bid' const triggerPrice = 'stopPrice' in order ? order.stopPrice : order.targetPrice logger.info(`执行${type}触发`, { orderId: order.orderId, symbol: order.symbol, triggerPrice, currentPrice, side: order.side, }) // 执行平仓订单 const accounts = this.accountManager.getAllAccountStates() const firstAccount = Array.from(accounts.keys())[0] const accountData = this.accountManager.getAccountState(firstAccount) if (!accountData) { logger.error('无法获取账户数据执行止损') return } const result = await client.createMarketOrder({ account: accountData.toString(), symbol: 'BTCUSDT', amount: order.amount, side: side, reduceOnly: true, slippagePercent: '5.0', }) if (result.success) { // 标记订单为已执行 if (type === 'stop_loss') { order.isActive = false } else { order.isActive = false } const emoji = type === 'stop_loss' ? '🛑' : '🎯' const typeText = type === 'stop_loss' ? '止损' : '止盈' console.log( `${emoji} ${typeText}触发成功: ${order.symbol} @ $${currentPrice.toFixed(2)} (目标: $${triggerPrice.toFixed( 2, )})`, ) // 更新账户状态 this.accountManager.updateTradeState(order.accountId, { side: order.side, amount: order.amount, success: true, }) } else { logger.error(`${type}执行失败`, { error: result.error, order }) } } catch (error) { logger.error(`执行${type}触发失败`, { error: error.message, order }) } } /** * 获取当前市场价格 */ async getCurrentPrice(symbol) { try { // 使用缓存的价格数据 const cachedPrice = this.cacheManager.get(`price_${symbol}`, this.cacheManager.getTTL('ticker')) if (cachedPrice && typeof cachedPrice === 'number') { return cachedPrice } // 缓存未命中,使用估算价格 const estimatedPrice = symbol.includes('BTC') ? 65000 : 3500 this.cacheManager.set(`price_${symbol}`, estimatedPrice, this.cacheManager.getTTL('ticker')) logger.warn(`无法获取${symbol}实时价格,使用估算价格: $${estimatedPrice}`) return estimatedPrice } catch (error) { logger.error('获取当前价格失败', { symbol, error: error.message }) return symbol.includes('BTC') ? 65000 : 3500 } } /** * 获取活跃订单统计 */ getActiveOrdersStats() { return { stopLossOrders: this.activeStopLossOrders.size, takeProfitOrders: this.activeTakeProfitOrders.size, monitoringSymbols: this.monitoringIntervals.size, } } }