From c2a78f1f7d90aaa078bbc4b7324335296fba9e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Fri, 16 Jan 2026 23:55:02 +0800 Subject: [PATCH] a --- frontend/src/components/StatsDashboard.jsx | 61 ++++-- trading_system/position_manager.py | 244 +++++++++++++-------- trading_system/risk_manager.py | 152 ++++++++----- trading_system/trade_recommender.py | 56 +++-- 4 files changed, 339 insertions(+), 174 deletions(-) diff --git a/frontend/src/components/StatsDashboard.jsx b/frontend/src/components/StatsDashboard.jsx index c7652f0..537dc41 100644 --- a/frontend/src/components/StatsDashboard.jsx +++ b/frontend/src/components/StatsDashboard.jsx @@ -201,23 +201,31 @@ const StatsDashboard = () => { } } - // 从配置获取止损止盈比例(优先使用dashboard返回的配置,其次使用单独加载的配置) + // 计算保证金(用于基于保证金的止损止盈) + const quantity = parseFloat(trade.quantity || 0) + const leverage = parseFloat(trade.leverage || 10) + const entryValue = trade.entry_value_usdt !== undefined + ? parseFloat(trade.entry_value_usdt) + : (quantity * entryPrice) + const margin = leverage > 0 ? entryValue / leverage : entryValue + + // 从配置获取止损止盈比例(相对于保证金) const configSource = dashboardData?.trading_config || tradingConfig const stopLossConfig = configSource?.STOP_LOSS_PERCENT const takeProfitConfig = configSource?.TAKE_PROFIT_PERCENT - // 配置值是小数形式(0.03表示3%),需要转换为百分比 - let defaultStopLossPercent = 3.0 // 默认3% - let defaultTakeProfitPercent = 5.0 // 默认5% + // 配置值是小数形式(0.03表示3%),相对于保证金 + let stopLossPercentMargin = 0.03 // 默认3%(相对于保证金) + let takeProfitPercentMargin = 0.05 // 默认5%(相对于保证金) if (stopLossConfig) { const configValue = stopLossConfig.value if (typeof configValue === 'number') { - defaultStopLossPercent = configValue * 100 + stopLossPercentMargin = configValue } else { const parsed = parseFloat(configValue) if (!isNaN(parsed)) { - defaultStopLossPercent = parsed * 100 + stopLossPercentMargin = parsed } } } @@ -225,32 +233,45 @@ const StatsDashboard = () => { if (takeProfitConfig) { const configValue = takeProfitConfig.value if (typeof configValue === 'number') { - defaultTakeProfitPercent = configValue * 100 + takeProfitPercentMargin = configValue } else { const parsed = parseFloat(configValue) if (!isNaN(parsed)) { - defaultTakeProfitPercent = parsed * 100 + takeProfitPercentMargin = parsed } } } - // 计算止损价和止盈价(基于默认比例) + // 计算止损止盈金额(相对于保证金) + const stopLossAmount = margin * stopLossPercentMargin + const takeProfitAmount = margin * takeProfitPercentMargin + + // 计算止损价和止盈价(基于保证金金额) + // 止损金额 = (开仓价 - 止损价) × 数量 或 (止损价 - 开仓价) × 数量 let stopLossPrice = 0 let takeProfitPrice = 0 if (side === 'BUY') { - stopLossPrice = entryPrice * (1 - defaultStopLossPercent / 100) - takeProfitPrice = entryPrice * (1 + defaultTakeProfitPercent / 100) + // 做多:止损价 = 开仓价 - (止损金额 / 数量) + stopLossPrice = entryPrice - (stopLossAmount / quantity) + // 做多:止盈价 = 开仓价 + (止盈金额 / 数量) + takeProfitPrice = entryPrice + (takeProfitAmount / quantity) } else { - stopLossPrice = entryPrice * (1 + defaultStopLossPercent / 100) - takeProfitPrice = entryPrice * (1 - defaultTakeProfitPercent / 100) + // 做空:止损价 = 开仓价 + (止损金额 / 数量) + stopLossPrice = entryPrice + (stopLossAmount / quantity) + // 做空:止盈价 = 开仓价 - (止盈金额 / 数量) + takeProfitPrice = entryPrice - (takeProfitAmount / quantity) } - // 计算止损比例和止盈比例(相对于入场价) - const stopLossPercent = side === 'BUY' + // 计算止损比例和止盈比例(相对于保证金,用于显示) + const stopLossPercent = stopLossPercentMargin * 100 // 相对于保证金 + const takeProfitPercent = takeProfitPercentMargin * 100 // 相对于保证金 + + // 也计算价格百分比(用于参考) + const stopLossPercentPrice = side === 'BUY' ? ((entryPrice - stopLossPrice) / entryPrice) * 100 : ((stopLossPrice - entryPrice) / entryPrice) * 100 - const takeProfitPercent = side === 'BUY' + const takeProfitPercentPrice = side === 'BUY' ? ((takeProfitPrice - entryPrice) / entryPrice) * 100 : ((entryPrice - takeProfitPrice) / entryPrice) * 100 @@ -303,12 +324,16 @@ const StatsDashboard = () => {
- 止损比例: -{stopLossPercent.toFixed(2)}% + 止损: -{stopLossPercent.toFixed(2)}% + (of margin) (价: {stopLossPrice.toFixed(4)}) + (金额: -{stopLossAmount.toFixed(2)} USDT)
- 止盈比例: +{takeProfitPercent.toFixed(2)}% + 止盈: +{takeProfitPercent.toFixed(2)}% + (of margin) (价: {takeProfitPrice.toFixed(4)}) + (金额: +{takeProfitAmount.toFixed(2)} USDT)
diff --git a/trading_system/position_manager.py b/trading_system/position_manager.py index 34a06c5..4dff62d 100644 --- a/trading_system/position_manager.py +++ b/trading_system/position_manager.py @@ -145,30 +145,31 @@ class PositionManager: logger.debug(f"获取K线数据失败,使用固定止损: {e}") klines = None - # 使用基于支撑/阻力的动态止损 - if klines or bollinger or atr: - stop_loss_price = self.risk_manager.get_stop_loss_price( - entry_price, side, - klines=klines, - bollinger=bollinger, - atr=atr - ) - - # 计算动态止损百分比(用于计算止盈) - if side == 'BUY': - stop_loss_pct = (entry_price - stop_loss_price) / entry_price - else: - stop_loss_pct = (stop_loss_price - entry_price) / entry_price - - # 止盈为止损的2-2.5倍(提高盈亏比) - take_profit_pct = stop_loss_pct * 2.5 - take_profit_price = self.risk_manager.get_take_profit_price( - entry_price, side, take_profit_pct=take_profit_pct - ) - else: - # 回退到固定止损止盈 - stop_loss_price = self.risk_manager.get_stop_loss_price(entry_price, side) - take_profit_price = self.risk_manager.get_take_profit_price(entry_price, side) + # 使用基于保证金的止损止盈(结合技术分析) + # 计算保证金和仓位价值 + position_value = entry_price * quantity + margin = position_value / leverage if leverage > 0 else position_value + + # 获取止损止盈百分比(相对于保证金) + stop_loss_pct_margin = self.risk_manager.config.get('STOP_LOSS_PERCENT', 0.03) + take_profit_pct_margin = self.risk_manager.config.get('TAKE_PROFIT_PERCENT', 0.05) + + # 计算基于保证金的止损止盈 + stop_loss_price = self.risk_manager.get_stop_loss_price( + entry_price, side, quantity, leverage, + stop_loss_pct=stop_loss_pct_margin, + klines=klines, + bollinger=bollinger, + atr=atr + ) + + # 计算止盈(基于保证金,为止损的倍数) + # 如果止损是保证金的3%,止盈可以是保证金的7.5%(2.5倍) + take_profit_pct_margin = stop_loss_pct_margin * 2.5 + take_profit_price = self.risk_manager.get_take_profit_price( + entry_price, side, quantity, leverage, + take_profit_pct=take_profit_pct_margin + ) # 下单 order = await self.client.place_order( @@ -598,51 +599,70 @@ class PositionManager: else: current_price = entry_price - # 计算当前盈亏 + # 计算当前盈亏(基于保证金) + leverage = position_info.get('leverage', 10) + position_value = entry_price * quantity + margin = position_value / leverage if leverage > 0 else position_value + + # 计算盈亏金额 if position_info['side'] == 'BUY': - pnl_percent = ((current_price - entry_price) / entry_price) * 100 + pnl_amount = (current_price - entry_price) * quantity else: - pnl_percent = ((entry_price - current_price) / entry_price) * 100 + pnl_amount = (entry_price - current_price) * quantity - # 更新最大盈利 - if pnl_percent > position_info.get('maxProfit', 0): - position_info['maxProfit'] = pnl_percent + # 计算盈亏百分比(相对于保证金,与币安一致) + pnl_percent_margin = (pnl_amount / margin * 100) if margin > 0 else 0 - # 移动止损逻辑(盈利后保护利润) + # 也计算价格百分比(用于显示和移动止损) + if position_info['side'] == 'BUY': + pnl_percent_price = ((current_price - entry_price) / entry_price) * 100 + else: + pnl_percent_price = ((entry_price - current_price) / entry_price) * 100 + + # 更新最大盈利(基于保证金) + if pnl_percent_margin > position_info.get('maxProfit', 0): + position_info['maxProfit'] = pnl_percent_margin + + # 移动止损逻辑(盈利后保护利润,基于保证金) use_trailing = config.TRADING_CONFIG.get('USE_TRAILING_STOP', True) if use_trailing: - trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01) - trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01) + trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01) # 相对于保证金 + trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01) # 相对于保证金 if not position_info.get('trailingStopActivated', False): - # 盈利超过阈值后,激活移动止损 - if pnl_percent > trailing_activation * 100: + # 盈利超过阈值后(相对于保证金),激活移动止损 + if pnl_percent_margin > trailing_activation * 100: position_info['trailingStopActivated'] = True # 将止损移至成本价(保本) position_info['stopLoss'] = entry_price logger.info( f"{symbol} 移动止损激活: 止损移至成本价 {entry_price:.4f} " - f"(盈利: {pnl_percent:.2f}%)" + f"(盈利: {pnl_percent_margin:.2f}% of margin)" ) else: - # 盈利超过2%后,止损移至保护利润位 - if pnl_percent > 2.0: - if position_info['side'] == 'BUY': - new_stop_loss = entry_price * (1 + trailing_protect) - if new_stop_loss > position_info['stopLoss']: - position_info['stopLoss'] = new_stop_loss - logger.info( - f"{symbol} 移动止损更新: {new_stop_loss:.4f} " - f"(保护{trailing_protect*100:.1f}%利润)" - ) - else: - new_stop_loss = entry_price * (1 - trailing_protect) - if new_stop_loss < position_info['stopLoss']: - position_info['stopLoss'] = new_stop_loss - logger.info( - f"{symbol} 移动止损更新: {new_stop_loss:.4f} " - f"(保护{trailing_protect*100:.1f}%利润)" - ) + # 盈利超过阈值后,止损移至保护利润位(基于保证金) + # 计算需要保护的利润金额 + protect_amount = margin * trailing_protect + # 计算对应的止损价 + if position_info['side'] == 'BUY': + # 保护利润:当前盈亏 - 保护金额 = (止损价 - 开仓价) × 数量 + # 所以:止损价 = 开仓价 + (当前盈亏 - 保护金额) / 数量 + new_stop_loss = entry_price + (pnl_amount - protect_amount) / quantity + if new_stop_loss > position_info['stopLoss']: + position_info['stopLoss'] = new_stop_loss + logger.info( + f"{symbol} 移动止损更新: {new_stop_loss:.4f} " + f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)" + ) + else: + # 做空:止损价 = 开仓价 - (当前盈亏 - 保护金额) / 数量 + new_stop_loss = entry_price - (pnl_amount - protect_amount) / quantity + if new_stop_loss < position_info['stopLoss']: + position_info['stopLoss'] = new_stop_loss + logger.info( + f"{symbol} 移动止损更新: {new_stop_loss:.4f} " + f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)" + ) # 检查止损(使用更新后的止损价) stop_loss = position_info.get('stopLoss') @@ -1064,9 +1084,21 @@ class PositionManager: ticker = await self.client.get_ticker_24h(symbol) current_price = ticker['price'] if ticker else entry_price - # 计算止损止盈 - stop_loss_price = self.risk_manager.get_stop_loss_price(entry_price, side) - take_profit_price = self.risk_manager.get_take_profit_price(entry_price, side) + # 计算止损止盈(基于保证金) + leverage = binance_position.get('leverage', 10) + stop_loss_pct_margin = self.risk_manager.config.get('STOP_LOSS_PERCENT', 0.03) + take_profit_pct_margin = self.risk_manager.config.get('TAKE_PROFIT_PERCENT', 0.05) + # 止盈为止损的2.5倍 + take_profit_pct_margin = stop_loss_pct_margin * 2.5 + + stop_loss_price = self.risk_manager.get_stop_loss_price( + entry_price, side, quantity, leverage, + stop_loss_pct=stop_loss_pct_margin + ) + take_profit_price = self.risk_manager.get_take_profit_price( + entry_price, side, quantity, leverage, + take_profit_pct=take_profit_pct_margin + ) position_info = { 'symbol': symbol, @@ -1079,7 +1111,7 @@ class PositionManager: 'stopLoss': stop_loss_price, 'takeProfit': take_profit_price, 'initialStopLoss': stop_loss_price, - 'leverage': binance_position.get('leverage', 10), + 'leverage': leverage, 'entryReason': 'manual_entry', 'atr': None, 'maxProfit': 0.0, @@ -1146,8 +1178,19 @@ class PositionManager: ticker = await self.client.get_ticker_24h(symbol) current_price = ticker['price'] if ticker else entry_price - stop_loss_price = self.risk_manager.get_stop_loss_price(entry_price, side) - take_profit_price = self.risk_manager.get_take_profit_price(entry_price, side) + # 计算止损止盈(基于保证金) + leverage = position.get('leverage', 10) + stop_loss_pct_margin = self.risk_manager.config.get('STOP_LOSS_PERCENT', 0.03) + take_profit_pct_margin = stop_loss_pct_margin * 2.5 + + stop_loss_price = self.risk_manager.get_stop_loss_price( + entry_price, side, quantity, leverage, + stop_loss_pct=stop_loss_pct_margin + ) + take_profit_price = self.risk_manager.get_take_profit_price( + entry_price, side, quantity, leverage, + take_profit_pct=take_profit_pct_margin + ) position_info = { 'symbol': symbol, @@ -1160,7 +1203,7 @@ class PositionManager: 'stopLoss': stop_loss_price, 'takeProfit': take_profit_price, 'initialStopLoss': stop_loss_price, - 'leverage': position.get('leverage', 10), + 'leverage': leverage, 'entryReason': 'manual_entry_temp', 'atr': None, 'maxProfit': 0.0, @@ -1326,51 +1369,70 @@ class PositionManager: quantity = float(position_info['quantity']) current_price_float = float(current_price) - # 计算当前盈亏 + # 计算当前盈亏(基于保证金) + leverage = position_info.get('leverage', 10) + position_value = entry_price * quantity + margin = position_value / leverage if leverage > 0 else position_value + + # 计算盈亏金额 if position_info['side'] == 'BUY': - pnl_percent = ((current_price_float - entry_price) / entry_price) * 100 + pnl_amount = (current_price_float - entry_price) * quantity else: # SELL - pnl_percent = ((entry_price - current_price_float) / entry_price) * 100 + pnl_amount = (entry_price - current_price_float) * quantity - # 更新最大盈利 - if pnl_percent > position_info.get('maxProfit', 0): - position_info['maxProfit'] = pnl_percent + # 计算盈亏百分比(相对于保证金,与币安一致) + pnl_percent_margin = (pnl_amount / margin * 100) if margin > 0 else 0 - # 移动止损逻辑(盈利后保护利润) + # 也计算价格百分比(用于显示) + if position_info['side'] == 'BUY': + pnl_percent_price = ((current_price_float - entry_price) / entry_price) * 100 + else: # SELL + pnl_percent_price = ((entry_price - current_price_float) / entry_price) * 100 + + # 更新最大盈利(基于保证金) + if pnl_percent_margin > position_info.get('maxProfit', 0): + position_info['maxProfit'] = pnl_percent_margin + + # 移动止损逻辑(盈利后保护利润,基于保证金) use_trailing = config.TRADING_CONFIG.get('USE_TRAILING_STOP', True) if use_trailing: - trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01) - trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01) + trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01) # 相对于保证金 + trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01) # 相对于保证金 if not position_info.get('trailingStopActivated', False): - # 盈利超过阈值后,激活移动止损 - if pnl_percent > trailing_activation * 100: + # 盈利超过阈值后(相对于保证金),激活移动止损 + if pnl_percent_margin > trailing_activation * 100: position_info['trailingStopActivated'] = True # 将止损移至成本价(保本) position_info['stopLoss'] = entry_price logger.info( f"{symbol} [实时监控] 移动止损激活: 止损移至成本价 {entry_price:.4f} " - f"(盈利: {pnl_percent:.2f}%)" + f"(盈利: {pnl_percent_margin:.2f}% of margin)" ) else: - # 盈利超过2%后,止损移至保护利润位 - if pnl_percent > 2.0: - if position_info['side'] == 'BUY': - new_stop_loss = entry_price * (1 + trailing_protect) - if new_stop_loss > position_info['stopLoss']: - position_info['stopLoss'] = new_stop_loss - logger.info( - f"{symbol} [实时监控] 移动止损更新: {new_stop_loss:.4f} " - f"(保护{trailing_protect*100:.1f}%利润)" - ) - else: # SELL - new_stop_loss = entry_price * (1 - trailing_protect) - if new_stop_loss < position_info['stopLoss']: - position_info['stopLoss'] = new_stop_loss - logger.info( - f"{symbol} [实时监控] 移动止损更新: {new_stop_loss:.4f} " - f"(保护{trailing_protect*100:.1f}%利润)" - ) + # 盈利超过阈值后,止损移至保护利润位(基于保证金) + # 计算需要保护的利润金额 + protect_amount = margin * trailing_protect + # 计算对应的止损价 + if position_info['side'] == 'BUY': + # 保护利润:当前盈亏 - 保护金额 = (止损价 - 开仓价) × 数量 + # 所以:止损价 = 开仓价 + (当前盈亏 - 保护金额) / 数量 + new_stop_loss = entry_price + (pnl_amount - protect_amount) / quantity + if new_stop_loss > position_info['stopLoss']: + position_info['stopLoss'] = new_stop_loss + logger.info( + f"{symbol} [实时监控] 移动止损更新: {new_stop_loss:.4f} " + f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)" + ) + else: # SELL + # 做空:止损价 = 开仓价 - (当前盈亏 - 保护金额) / 数量 + new_stop_loss = entry_price - (pnl_amount - protect_amount) / quantity + if new_stop_loss < position_info['stopLoss']: + position_info['stopLoss'] = new_stop_loss + logger.info( + f"{symbol} [实时监控] 移动止损更新: {new_stop_loss:.4f} " + f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)" + ) # 检查止损 stop_loss = position_info['stopLoss'] diff --git a/trading_system/risk_manager.py b/trading_system/risk_manager.py index 63a3eaa..e1bc81c 100644 --- a/trading_system/risk_manager.py +++ b/trading_system/risk_manager.py @@ -399,26 +399,48 @@ class RiskManager: self, entry_price: float, side: str, + quantity: float, + leverage: int, stop_loss_pct: Optional[float] = None, klines: Optional[List] = None, bollinger: Optional[Dict] = None, atr: Optional[float] = None ) -> float: """ - 计算止损价格(基于支撑/阻力的动态止损) + 计算止损价格(基于保证金的盈亏金额) Args: entry_price: 入场价格 side: 方向 'BUY' 或 'SELL' - stop_loss_pct: 止损百分比,如果为None则使用配置值 - klines: K线数据,用于计算支撑/阻力位 - bollinger: 布林带数据,用于计算动态止损 - atr: 平均真实波幅,用于计算动态止损 + quantity: 持仓数量 + leverage: 杠杆倍数 + stop_loss_pct: 止损百分比(相对于保证金),如果为None则使用配置值 + klines: K线数据,用于计算支撑/阻力位(作为辅助参考) + bollinger: 布林带数据,用于计算动态止损(作为辅助参考) + atr: 平均真实波幅,用于计算动态止损(作为辅助参考) Returns: 止损价格 """ - # 优先使用基于技术结构的动态止损 + # 计算保证金和仓位价值 + position_value = entry_price * quantity + margin = position_value / leverage if leverage > 0 else position_value + + # 获取止损百分比(相对于保证金) + stop_loss_percent = stop_loss_pct or self.config['STOP_LOSS_PERCENT'] + + # 计算止损金额(相对于保证金) + stop_loss_amount = margin * stop_loss_percent + + # 计算止损价(基于止损金额) + # 止损金额 = (开仓价 - 止损价) × 数量 + # 所以:止损价 = 开仓价 - (止损金额 / 数量) + if side == 'BUY': # 做多,止损价低于入场价 + stop_loss_price = entry_price - (stop_loss_amount / quantity) + else: # 做空,止损价高于入场价 + stop_loss_price = entry_price + (stop_loss_amount / quantity) + + # 如果提供了技术分析数据,可以调整止损价(但不能超过基于保证金的止损范围) if klines and len(klines) >= 10: # 计算支撑/阻力位 low_prices = [float(k[3]) for k in klines[-20:]] # 最近20根K线的最低价 @@ -427,85 +449,115 @@ class RiskManager: if side == 'BUY': # 做多,止损放在支撑位下方 # 找到近期波段低点 recent_low = min(low_prices) - # 止损放在低点下方0.5-1% + # 止损放在低点下方0.5% buffer = entry_price * 0.005 # 0.5%缓冲 - dynamic_stop = recent_low - buffer + technical_stop = recent_low - buffer # 如果布林带可用,也可以考虑布林带下轨 if bollinger and bollinger.get('lower'): bollinger_stop = bollinger['lower'] * 0.995 # 布林带下轨下方0.5% - dynamic_stop = max(dynamic_stop, bollinger_stop) + technical_stop = max(technical_stop, bollinger_stop) - # 确保止损不超过固定止损范围(1%-5%) - fixed_stop_pct = stop_loss_pct or self.config['STOP_LOSS_PERCENT'] - max_stop = entry_price * (1 - 0.01) # 最多1%止损 - min_stop = entry_price * (1 - 0.05) # 最少5%止损(极端情况) - - dynamic_stop = max(min_stop, min(max_stop, dynamic_stop)) - - logger.info( - f"动态止损计算 (BUY): 入场价={entry_price:.4f}, " - f"近期低点={recent_low:.4f}, 动态止损={dynamic_stop:.4f}, " - f"止损比例={((entry_price - dynamic_stop) / entry_price * 100):.2f}%" - ) - return dynamic_stop + # 使用技术止损,但不能超过基于保证金的止损范围(允许更紧的止损) + # 如果技术止损更紧(价格更低),使用技术止损;否则使用基于保证金的止损 + if technical_stop < stop_loss_price: + # 技术止损更紧,使用技术止损 + logger.info( + f"动态止损计算 (BUY): 使用技术止损 {technical_stop:.4f} " + f"(基于保证金的止损: {stop_loss_price:.4f}, 止损金额: {stop_loss_amount:.2f} USDT)" + ) + return technical_stop + else: + logger.info( + f"动态止损计算 (BUY): 使用基于保证金的止损 {stop_loss_price:.4f} " + f"(技术止损: {technical_stop:.4f}, 止损金额: {stop_loss_amount:.2f} USDT, " + f"止损比例: {stop_loss_percent*100:.1f}% of margin)" + ) + return stop_loss_price else: # 做空,止损放在阻力位上方 # 找到近期波段高点 recent_high = max(high_prices) - # 止损放在高点上方0.5-1% + # 止损放在高点上方0.5% buffer = entry_price * 0.005 # 0.5%缓冲 - dynamic_stop = recent_high + buffer + technical_stop = recent_high + buffer # 如果布林带可用,也可以考虑布林带上轨 if bollinger and bollinger.get('upper'): bollinger_stop = bollinger['upper'] * 1.005 # 布林带上轨上方0.5% - dynamic_stop = min(dynamic_stop, bollinger_stop) + technical_stop = min(technical_stop, bollinger_stop) - # 确保止损不超过固定止损范围(1%-5%) - fixed_stop_pct = stop_loss_pct or self.config['STOP_LOSS_PERCENT'] - min_stop = entry_price * (1 + 0.01) # 最少1%止损 - max_stop = entry_price * (1 + 0.05) # 最多5%止损(极端情况) - - dynamic_stop = min(max_stop, max(min_stop, dynamic_stop)) - - logger.info( - f"动态止损计算 (SELL): 入场价={entry_price:.4f}, " - f"近期高点={recent_high:.4f}, 动态止损={dynamic_stop:.4f}, " - f"止损比例={((dynamic_stop - entry_price) / entry_price * 100):.2f}%" - ) - return dynamic_stop + # 使用技术止损,但不能超过基于保证金的止损范围(允许更紧的止损) + # 如果技术止损更紧(价格更高),使用技术止损;否则使用基于保证金的止损 + if technical_stop > stop_loss_price: + # 技术止损更紧,使用技术止损 + logger.info( + f"动态止损计算 (SELL): 使用技术止损 {technical_stop:.4f} " + f"(基于保证金的止损: {stop_loss_price:.4f}, 止损金额: {stop_loss_amount:.2f} USDT)" + ) + return technical_stop + else: + logger.info( + f"动态止损计算 (SELL): 使用基于保证金的止损 {stop_loss_price:.4f} " + f"(技术止损: {technical_stop:.4f}, 止损金额: {stop_loss_amount:.2f} USDT, " + f"止损比例: {stop_loss_percent*100:.1f}% of margin)" + ) + return stop_loss_price - # 回退到固定百分比止损 - stop_loss_percent = stop_loss_pct or self.config['STOP_LOSS_PERCENT'] - - if side == 'BUY': # 做多,止损价低于入场价 - return entry_price * (1 - stop_loss_percent) - else: # 做空,止损价高于入场价 - return entry_price * (1 + stop_loss_percent) + # 没有技术分析数据,直接使用基于保证金的止损 + logger.info( + f"止损计算 ({side}): 入场价={entry_price:.4f}, 数量={quantity:.4f}, " + f"杠杆={leverage}x, 保证金={margin:.4f} USDT, " + f"止损金额={stop_loss_amount:.4f} USDT ({stop_loss_percent*100:.1f}% of margin), " + f"止损价={stop_loss_price:.4f}" + ) + return stop_loss_price def get_take_profit_price( self, entry_price: float, - side: str, + side: str, + quantity: float, + leverage: int, take_profit_pct: Optional[float] = None ) -> float: """ - 计算止盈价格 + 计算止盈价格(基于保证金的盈亏金额) Args: entry_price: 入场价格 side: 方向 'BUY' 或 'SELL' - take_profit_pct: 止盈百分比,如果为None则使用配置值 + quantity: 持仓数量 + leverage: 杠杆倍数 + take_profit_pct: 止盈百分比(相对于保证金),如果为None则使用配置值 Returns: 止盈价格 """ + # 计算保证金和仓位价值 + position_value = entry_price * quantity + margin = position_value / leverage if leverage > 0 else position_value + + # 获取止盈百分比(相对于保证金) take_profit_percent = take_profit_pct or self.config['TAKE_PROFIT_PERCENT'] + # 计算止盈金额(相对于保证金) + take_profit_amount = margin * take_profit_percent + + # 计算止盈价(基于止盈金额) + # 止盈金额 = (止盈价 - 开仓价) × 数量 + # 所以:止盈价 = 开仓价 + (止盈金额 / 数量) if side == 'BUY': # 做多,止盈价高于入场价 - return entry_price * (1 + take_profit_percent) + take_profit_price = entry_price + (take_profit_amount / quantity) else: # 做空,止盈价低于入场价 - return entry_price * (1 - take_profit_percent) + take_profit_price = entry_price - (take_profit_amount / quantity) + + logger.info( + f"止盈计算 ({side}): 入场价={entry_price:.4f}, 数量={quantity:.4f}, " + f"杠杆={leverage}x, 保证金={margin:.4f} USDT, " + f"止盈金额={take_profit_amount:.4f} USDT ({take_profit_percent*100:.1f}% of margin), " + f"止盈价={take_profit_price:.4f}" + ) + return take_profit_price async def calculate_dynamic_leverage(self, signal_strength: int, symbol: str = None) -> int: """ diff --git a/trading_system/trade_recommender.py b/trading_system/trade_recommender.py index 7066e65..49e8c76 100644 --- a/trading_system/trade_recommender.py +++ b/trading_system/trade_recommender.py @@ -358,32 +358,54 @@ class TradeRecommender: current_price = symbol_info['price'] direction = trade_signal['direction'] - # 计算建议的止损止盈 + # 计算建议的止损止盈(基于保证金) entry_price = current_price + + # 估算仓位数量和杠杆(用于计算止损止盈) + # 使用建议的仓位比例和账户余额来估算 + account_balance = symbol_info.get('account_balance', 1000) + suggested_position_pct = symbol_info.get('suggested_position_pct', 0.05) + leverage = config.TRADING_CONFIG.get('LEVERAGE', 10) + + # 估算仓位价值 + estimated_position_value = account_balance * suggested_position_pct + estimated_quantity = estimated_position_value / entry_price if entry_price > 0 else 0 + + # 计算基于保证金的止损止盈 + stop_loss_pct_margin = config.TRADING_CONFIG.get('STOP_LOSS_PERCENT', 0.03) + take_profit_pct_margin = config.TRADING_CONFIG.get('TAKE_PROFIT_PERCENT', 0.05) + stop_loss_price = self.risk_manager.get_stop_loss_price( entry_price, direction, + estimated_quantity, + leverage, + stop_loss_pct=stop_loss_pct_margin, klines=symbol_info.get('klines'), bollinger=symbol_info.get('bollinger'), atr=symbol_info.get('atr') ) - # 计算止损百分比 + # 计算止损百分比(相对于保证金,用于显示) + estimated_margin = estimated_position_value / leverage if leverage > 0 else estimated_position_value + stop_loss_amount = estimated_margin * stop_loss_pct_margin if direction == 'BUY': stop_loss_pct = (entry_price - stop_loss_price) / entry_price else: stop_loss_pct = (stop_loss_price - entry_price) / entry_price - # 第一目标:盈亏比1:1 - if direction == 'BUY': - take_profit_1 = entry_price + (entry_price - stop_loss_price) - else: - take_profit_1 = entry_price - (stop_loss_price - entry_price) + # 第一目标:盈亏比1:1(相对于保证金) + take_profit_1_pct_margin = stop_loss_pct_margin * 1.0 # 1:1 盈亏比 + take_profit_1 = self.risk_manager.get_take_profit_price( + entry_price, direction, estimated_quantity, leverage, + take_profit_pct=take_profit_1_pct_margin + ) - # 第二目标:止损的2.5倍 - take_profit_2_pct = stop_loss_pct * 2.5 + # 第二目标:止损的2.5倍(相对于保证金) + take_profit_2_pct_margin = stop_loss_pct_margin * 2.5 take_profit_2 = self.risk_manager.get_take_profit_price( - entry_price, direction, take_profit_pct=take_profit_2_pct + entry_price, direction, estimated_quantity, leverage, + take_profit_pct=take_profit_2_pct_margin ) # 建议仓位(根据信号强度调整) @@ -421,11 +443,15 @@ class TradeRecommender: base_win_rate -= 2 estimated_win_rate = max(35, min(75, base_win_rate)) - # 计算盈亏USDT评估 - position_value = account_balance * suggested_position_pct * config.TRADING_CONFIG.get('LEVERAGE', 10) - stop_loss_usdt = position_value * stop_loss_pct - take_profit_1_usdt = position_value * stop_loss_pct - take_profit_2_usdt = position_value * take_profit_2_pct + # 计算盈亏USDT评估(基于保证金) + position_value = account_balance * suggested_position_pct + margin = position_value / leverage if leverage > 0 else position_value + + # 止损止盈金额(相对于保证金) + stop_loss_usdt = margin * stop_loss_pct_margin + take_profit_1_usdt = margin * take_profit_1_pct_margin + take_profit_2_usdt = margin * take_profit_2_pct_margin + expected_pnl_1 = (estimated_win_rate / 100) * take_profit_1_usdt - ((100 - estimated_win_rate) / 100) * stop_loss_usdt expected_pnl_2 = (estimated_win_rate / 100) * take_profit_2_usdt - ((100 - estimated_win_rate) / 100) * stop_loss_usdt