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