import { EventEmitter } from 'events' import { logger } from '../../utils/logger.js' import { PacificaProxyClient } from '../../exchanges/pacifica/PacificaProxyClient.js' import { OrderCreatePayload } from '../../exchanges/pacifica/OrdersAdapter.js' /** * 止盈止损管理器 - 高级止损策略管理 */ export class StopLossManager extends EventEmitter { private stopOrders: Map = new Map() private clients: Map = new Map() private config: StopLossConfig private isActive: boolean = false private monitoringInterval?: NodeJS.Timeout constructor(config?: Partial) { super() this.config = { checkIntervalMs: config?.checkIntervalMs || 2000, // 2秒检查间隔 defaultStopLossPercent: config?.defaultStopLossPercent || 0.01, // 1% 止损 defaultTakeProfitPercent: config?.defaultTakeProfitPercent || 0.02, // 2% 止盈 trailingStopPercent: config?.trailingStopPercent || 0.005, // 0.5% 追踪止损 partialClosePercent: config?.partialClosePercent || 0.5, // 50% 部分平仓 enableBreakEvenStop: config?.enableBreakEvenStop || true, // 启用盈亏平衡止损 breakEvenTriggerPercent: config?.breakEvenTriggerPercent || 0.01, // 1% 触发盈亏平衡 enableScaleOut: config?.enableScaleOut || true, // 启用分批止盈 scaleOutLevels: config?.scaleOutLevels || [0.01, 0.02, 0.03], // 分批止盈点位 maxSlippagePercent: config?.maxSlippagePercent || 0.01, // 最大滑点 } } /** * 注册交易客户端 */ registerClient(account: string, client: PacificaProxyClient): void { this.clients.set(account, client) logger.info('注册止损管理客户端', { account }) } /** * 为仓位设置止盈止损 */ async setStopLossAndTakeProfit( account: string, symbol: string, positionSize: number, entryPrice: number, isLong: boolean, customConfig?: Partial, ): Promise { const orderId = this.generateOrderId(account, symbol) const stopOrder: StopOrder = { id: orderId, account, symbol, originalSize: Math.abs(positionSize), remainingSize: Math.abs(positionSize), entryPrice, isLong, status: 'active', createdAt: Date.now(), lastUpdate: Date.now(), config: { stopLossPercent: customConfig?.stopLossPercent || this.config.defaultStopLossPercent, takeProfitPercent: customConfig?.takeProfitPercent || this.config.defaultTakeProfitPercent, enableTrailingStop: customConfig?.enableTrailingStop || false, trailingStopPercent: customConfig?.trailingStopPercent || this.config.trailingStopPercent, enablePartialClose: customConfig?.enablePartialClose || false, partialClosePercent: customConfig?.partialClosePercent || this.config.partialClosePercent, stopType: customConfig?.stopType || 'fixed', }, trailingStopPrice: null, highestPrice: entryPrice, lowestPrice: entryPrice, executedOrders: [], partialCloseCount: 0, } // 计算初始止损止盈价格 this.updateStopPrices(stopOrder, entryPrice) this.stopOrders.set(orderId, stopOrder) logger.info('设置止盈止损', { orderId, account, symbol, positionSize, entryPrice, isLong, stopLossPrice: stopOrder.stopLossPrice, takeProfitPrice: stopOrder.takeProfitPrice, }) this.emit('stopOrderCreated', stopOrder) return orderId } /** * 启动止损监控 */ startMonitoring(): void { if (this.isActive) return this.isActive = true this.monitoringInterval = setInterval(() => { this.performStopChecks() }, this.config.checkIntervalMs) logger.info('止损监控已启动', { interval: this.config.checkIntervalMs, activeOrders: this.stopOrders.size, }) this.emit('monitoringStarted') } /** * 停止止损监控 */ stopMonitoring(): void { if (!this.isActive) return this.isActive = false if (this.monitoringInterval) { clearInterval(this.monitoringInterval) this.monitoringInterval = undefined } logger.info('止损监控已停止') this.emit('monitoringStopped') } /** * 执行止损检查 */ private async performStopChecks(): Promise { const activeOrders = Array.from(this.stopOrders.values()).filter(order => order.status === 'active') for (const order of activeOrders) { try { await this.checkStopOrder(order) } catch (error: any) { logger.error('止损检查失败', { orderId: order.id, account: order.account, symbol: order.symbol, error: error.message, }) } } } /** * 检查单个止损订单 */ private async checkStopOrder(order: StopOrder): Promise { const client = this.clients.get(order.account) if (!client) { logger.warn('未找到交易客户端', { account: order.account }) return } // 获取当前价格 const currentPrice = await this.getCurrentPrice(client, order.symbol) if (!currentPrice) return // 更新价格追踪 this.updatePriceTracking(order, currentPrice) // 检查是否触发止损或止盈 const triggerResult = this.checkTriggers(order, currentPrice) if (triggerResult.shouldTrigger) { await this.executeTrigger(order, triggerResult, currentPrice) } order.lastUpdate = Date.now() } /** * 更新价格追踪 */ private updatePriceTracking(order: StopOrder, currentPrice: number): void { // 更新最高价和最低价 if (currentPrice > order.highestPrice) { order.highestPrice = currentPrice } if (currentPrice < order.lowestPrice) { order.lowestPrice = currentPrice } // 更新追踪止损价格 if (order.config.enableTrailingStop) { this.updateTrailingStop(order, currentPrice) } // 更新盈亏平衡止损 if (this.config.enableBreakEvenStop) { this.updateBreakEvenStop(order, currentPrice) } // 更新止损止盈价格 this.updateStopPrices(order, currentPrice) } /** * 更新追踪止损 */ private updateTrailingStop(order: StopOrder, currentPrice: number): void { if (!order.config.enableTrailingStop) return const trailingPercent = order.config.trailingStopPercent || this.config.trailingStopPercent if (order.isLong) { // 多头:追踪止损价格跟随最高价下移 const newTrailingStop = order.highestPrice * (1 - trailingPercent) if (!order.trailingStopPrice || newTrailingStop > order.trailingStopPrice) { order.trailingStopPrice = newTrailingStop logger.debug('更新追踪止损', { orderId: order.id, newTrailingStop, highestPrice: order.highestPrice, currentPrice, }) } } else { // 空头:追踪止损价格跟随最低价上移 const newTrailingStop = order.lowestPrice * (1 + trailingPercent) if (!order.trailingStopPrice || newTrailingStop < order.trailingStopPrice) { order.trailingStopPrice = newTrailingStop logger.debug('更新追踪止损', { orderId: order.id, newTrailingStop, lowestPrice: order.lowestPrice, currentPrice, }) } } } /** * 更新盈亏平衡止损 */ private updateBreakEvenStop(order: StopOrder, currentPrice: number): void { const triggerPercent = this.config.breakEvenTriggerPercent const profitPercent = order.isLong ? (currentPrice - order.entryPrice) / order.entryPrice : (order.entryPrice - currentPrice) / order.entryPrice // 如果盈利超过触发阈值,将止损移动到盈亏平衡点 if (profitPercent >= triggerPercent && order.config.stopType === 'fixed') { order.config.stopType = 'break_even' logger.info('激活盈亏平衡止损', { orderId: order.id, profitPercent: profitPercent.toFixed(4), entryPrice: order.entryPrice, currentPrice, }) } } /** * 更新止损止盈价格 */ private updateStopPrices(order: StopOrder, referencePrice?: number): void { const price = referencePrice || order.entryPrice if (order.config.stopType === 'break_even') { // 盈亏平衡止损 order.stopLossPrice = order.entryPrice } else if (order.config.enableTrailingStop && order.trailingStopPrice) { // 追踪止损 order.stopLossPrice = order.trailingStopPrice } else { // 固定止损 const stopLossPercent = order.config.stopLossPercent order.stopLossPrice = order.isLong ? order.entryPrice * (1 - stopLossPercent) : order.entryPrice * (1 + stopLossPercent) } // 止盈价格 const takeProfitPercent = order.config.takeProfitPercent order.takeProfitPrice = order.isLong ? order.entryPrice * (1 + takeProfitPercent) : order.entryPrice * (1 - takeProfitPercent) } /** * 检查触发条件 */ private checkTriggers(order: StopOrder, currentPrice: number): TriggerResult { // 检查止损 if (order.stopLossPrice) { const stopLossTriggered = order.isLong ? currentPrice <= order.stopLossPrice : currentPrice >= order.stopLossPrice if (stopLossTriggered) { return { shouldTrigger: true, triggerType: 'stop_loss', triggerPrice: order.stopLossPrice, closePercent: 1.0, // 全部平仓 } } } // 检查止盈 if (order.takeProfitPrice) { const takeProfitTriggered = order.isLong ? currentPrice >= order.takeProfitPrice : currentPrice <= order.takeProfitPrice if (takeProfitTriggered) { const closePercent = order.config.enablePartialClose ? order.config.partialClosePercent || this.config.partialClosePercent : 1.0 return { shouldTrigger: true, triggerType: 'take_profit', triggerPrice: order.takeProfitPrice, closePercent, } } } // 检查分批止盈 if (this.config.enableScaleOut) { const scaleOutTrigger = this.checkScaleOutTriggers(order, currentPrice) if (scaleOutTrigger.shouldTrigger) { return scaleOutTrigger } } return { shouldTrigger: false } } /** * 检查分批止盈触发 */ private checkScaleOutTriggers(order: StopOrder, currentPrice: number): TriggerResult { const scaleOutLevels = this.config.scaleOutLevels for (let i = 0; i < scaleOutLevels.length; i++) { const level = scaleOutLevels[i] const targetPrice = order.isLong ? order.entryPrice * (1 + level) : order.entryPrice * (1 - level) const triggered = order.isLong ? currentPrice >= targetPrice : currentPrice <= targetPrice if (triggered && !order.scaleOutExecuted?.includes(i)) { if (!order.scaleOutExecuted) { order.scaleOutExecuted = [] } return { shouldTrigger: true, triggerType: 'scale_out', triggerPrice: targetPrice, closePercent: 0.25, // 25% 分批平仓 scaleOutLevel: i, } } } return { shouldTrigger: false } } /** * 执行触发操作 */ private async executeTrigger(order: StopOrder, trigger: TriggerResult, currentPrice: number): Promise { const client = this.clients.get(order.account) if (!client) return const closeSize = order.remainingSize * trigger.closePercent const side = order.isLong ? 'ask' : 'bid' // 平仓方向 try { const payload: OrderCreatePayload = { account: order.account, symbol: order.symbol, amount: closeSize.toString(), side, reduceOnly: true, slippagePercent: (this.config.maxSlippagePercent * 100).toString(), } let result if (trigger.triggerType === 'stop_loss') { // 止损使用市价单确保成交 result = await client.createMarketOrder(payload) } else { // 止盈可以使用限价单获得更好价格 payload.orderType = 'limit' payload.price = trigger.triggerPrice?.toString() result = await client.createLimitOrder(payload) } // 记录执行结果 const execution: StopExecution = { orderId: result.orderId || result.order_id || '', triggerType: trigger.triggerType, executedSize: closeSize, executedPrice: currentPrice, timestamp: Date.now(), scaleOutLevel: trigger.scaleOutLevel, } order.executedOrders.push(execution) order.remainingSize -= closeSize // 更新分批执行状态 if (trigger.triggerType === 'scale_out' && trigger.scaleOutLevel !== undefined) { if (!order.scaleOutExecuted) { order.scaleOutExecuted = [] } order.scaleOutExecuted.push(trigger.scaleOutLevel) order.partialCloseCount++ } // 检查是否完全平仓 if (order.remainingSize <= 0.0001) { order.status = 'completed' } else if (trigger.triggerType === 'take_profit' && order.config.enablePartialClose) { // 部分止盈后,调整剩余止盈价格 this.adjustRemainingTakeProfit(order) } logger.info('触发执行成功', { orderId: order.id, triggerType: trigger.triggerType, executedSize: closeSize, remainingSize: order.remainingSize, executedPrice: currentPrice, resultOrderId: result.orderId || result.order_id, }) this.emit('stopTriggered', { order, execution, trigger, currentPrice, }) } catch (error: any) { logger.error('触发执行失败', { orderId: order.id, triggerType: trigger.triggerType, error: error.message, }) this.emit('stopTriggerFailed', { order, trigger, error: error.message, }) } } /** * 调整剩余止盈价格 */ private adjustRemainingTakeProfit(order: StopOrder): void { // 部分止盈后,可以将剩余仓位的止盈点位适当调高 const adjustmentFactor = 1.5 // 调高50% const originalPercent = order.config.takeProfitPercent const newPercent = originalPercent * adjustmentFactor order.takeProfitPrice = order.isLong ? order.entryPrice * (1 + newPercent) : order.entryPrice * (1 - newPercent) logger.info('调整剩余止盈价格', { orderId: order.id, newTakeProfitPrice: order.takeProfitPrice, adjustmentFactor, }) } /** * 获取当前价格 */ private async getCurrentPrice(client: PacificaProxyClient, symbol: string): Promise { try { const ticker = await client.getTicker(symbol) return ticker?.price || null } catch (error: any) { logger.error('获取当前价格失败', { symbol, error: error.message, }) return null } } /** * 生成订单ID */ private generateOrderId(account: string, symbol: string): string { return `stop_${account}_${symbol}_${Date.now()}_${Math.random().toString(36).substr(2, 6)}` } /** * 取消止损订单 */ cancelStopOrder(orderId: string): boolean { const order = this.stopOrders.get(orderId) if (!order) return false order.status = 'cancelled' logger.info('取消止损订单', { orderId }) this.emit('stopOrderCancelled', order) return true } /** * 获取活跃止损订单 */ getActiveStopOrders(account?: string): StopOrder[] { const orders = Array.from(this.stopOrders.values()).filter(order => order.status === 'active') return account ? orders.filter(order => order.account === account) : orders } /** * 获取止损统计 */ getStopOrderStats(): StopOrderStats { const allOrders = Array.from(this.stopOrders.values()) return { totalOrders: allOrders.length, activeOrders: allOrders.filter(o => o.status === 'active').length, completedOrders: allOrders.filter(o => o.status === 'completed').length, cancelledOrders: allOrders.filter(o => o.status === 'cancelled').length, totalExecutions: allOrders.reduce((sum, o) => sum + o.executedOrders.length, 0), successfulStopLosses: allOrders.filter(o => o.executedOrders.some(e => e.triggerType === 'stop_loss')).length, successfulTakeProfits: allOrders.filter(o => o.executedOrders.some(e => e.triggerType === 'take_profit')).length, } } } // 类型定义 export interface StopOrder { id: string account: string symbol: string originalSize: number remainingSize: number entryPrice: number isLong: boolean status: 'active' | 'completed' | 'cancelled' createdAt: number lastUpdate: number config: StopOrderConfig stopLossPrice?: number takeProfitPrice?: number trailingStopPrice?: number | null highestPrice: number lowestPrice: number executedOrders: StopExecution[] partialCloseCount: number scaleOutExecuted?: number[] } export interface StopOrderConfig { stopLossPercent: number takeProfitPercent: number enableTrailingStop: boolean trailingStopPercent: number enablePartialClose: boolean partialClosePercent: number stopType: 'fixed' | 'trailing' | 'break_even' } export interface StopLossConfig { checkIntervalMs: number defaultStopLossPercent: number defaultTakeProfitPercent: number trailingStopPercent: number partialClosePercent: number enableBreakEvenStop: boolean breakEvenTriggerPercent: number enableScaleOut: boolean scaleOutLevels: number[] maxSlippagePercent: number } export interface TriggerResult { shouldTrigger: boolean triggerType?: 'stop_loss' | 'take_profit' | 'scale_out' triggerPrice?: number closePercent?: number scaleOutLevel?: number } export interface StopExecution { orderId: string triggerType: 'stop_loss' | 'take_profit' | 'scale_out' executedSize: number executedPrice: number timestamp: number scaleOutLevel?: number } export interface StopOrderStats { totalOrders: number activeOrders: number completedOrders: number cancelledOrders: number totalExecutions: number successfulStopLosses: number successfulTakeProfits: number } export const stopLossManager = new StopLossManager()