From 9be1c5777d07c0d16a1809ab6667bfad6f69f417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Wed, 4 Feb 2026 16:07:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8E=A8=E8=8D=90=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- trading_system/risk_manager.py | 115 +++++++++++++++++----------- trading_system/trade_recommender.py | 30 ++++++-- 2 files changed, 94 insertions(+), 51 deletions(-) diff --git a/trading_system/risk_manager.py b/trading_system/risk_manager.py index 45dbb35..57a22aa 100644 --- a/trading_system/risk_manager.py +++ b/trading_system/risk_manager.py @@ -228,6 +228,69 @@ class RiskManager: logger.error(f"检查总仓位失败: {e}", exc_info=True) return False + async def calculate_dynamic_leverage(self, symbol: str, entry_price: float, stop_loss_price: Optional[float] = None, atr: Optional[float] = None, side: Optional[str] = None) -> int: + """ + 计算动态杠杆 + + Args: + symbol: 交易对 + entry_price: 入场价格 + stop_loss_price: 止损价格 + atr: ATR值 + side: 交易方向 + + Returns: + 建议杠杆倍数 + """ + # 默认使用配置杠杆 + default_leverage = config.TRADING_CONFIG.get('LEVERAGE', 10) + + if not config.TRADING_CONFIG.get('USE_DYNAMIC_LEVERAGE', False): + return default_leverage + + # 尝试获取止损价格(如果没有传入,尝试估算) + target_stop_loss = stop_loss_price + + # 如果没有传入止损价,尝试使用ATR估算(仅用于计算杠杆建议) + if target_stop_loss is None and atr and atr > 0 and entry_price: + atr_multiplier = config.TRADING_CONFIG.get('ATR_STOP_LOSS_MULTIPLIER', 2.5) + if side == 'BUY': + target_stop_loss = entry_price - (atr * atr_multiplier) + elif side == 'SELL': + target_stop_loss = entry_price + (atr * atr_multiplier) + + if target_stop_loss and entry_price: + # 计算止损宽度比例 + stop_loss_width = abs(entry_price - target_stop_loss) / entry_price + + if stop_loss_width > 0: + # 获取最大单笔亏损率限制 (默认20%) + max_loss_pct = config.TRADING_CONFIG.get('MAX_SINGLE_TRADE_LOSS_PERCENT', 20.0) / 100.0 + + # 计算建议杠杆 + # 理论杠杆 = 最大允许亏损比例 / 止损宽度比例 + theoretical_leverage = max_loss_pct / stop_loss_width + + # 向下取整 + suggested_leverage = int(theoretical_leverage) + + # 限制在最大杠杆范围内 + max_config_leverage = config.TRADING_CONFIG.get('MAX_LEVERAGE', 10) + final_dynamic_leverage = min(suggested_leverage, max_config_leverage) + + # 至少为1倍 + final_dynamic_leverage = max(1, final_dynamic_leverage) + + logger.info(f" ⚖️ 动态杠杆计算 ({symbol}):") + logger.info(f" 止损价格: {target_stop_loss:.4f} (宽度: {stop_loss_width*100:.2f}%)") + logger.info(f" 最大单笔亏损限制: {max_loss_pct*100:.1f}%") + logger.info(f" 理论最大杠杆: {theoretical_leverage:.2f}x") + logger.info(f" -> 最终建议杠杆: {final_dynamic_leverage}x") + + return final_dynamic_leverage + + return default_leverage + async def calculate_position_size( self, symbol: str, @@ -283,53 +346,15 @@ class RiskManager: # ------------------------------------------------------------------------- # 动态杠杆计算逻辑 (针对 ZROUSDT 亏损案例优化) # ------------------------------------------------------------------------- - # 如果启用了动态杠杆,根据止损宽度自动调整杠杆 - # 公式: 建议杠杆 = 目标最大单单亏损率 / 止损宽度 - # 例如: 目标亏损20%,止损宽度10%,则建议杠杆 = 20% / 10% = 2倍 - # ------------------------------------------------------------------------- calculated_leverage = None if config.TRADING_CONFIG.get('USE_DYNAMIC_LEVERAGE', False): - # 尝试获取止损价格(如果没有传入,尝试估算) - target_stop_loss = stop_loss_price - - # 如果没有传入止损价,尝试使用ATR估算(仅用于计算杠杆建议) - if target_stop_loss is None and atr and atr > 0 and entry_price: - atr_multiplier = config.TRADING_CONFIG.get('ATR_STOP_LOSS_MULTIPLIER', 2.5) - if side == 'BUY': - target_stop_loss = entry_price - (atr * atr_multiplier) - else: - target_stop_loss = entry_price + (atr * atr_multiplier) - - if target_stop_loss and entry_price: - # 计算止损宽度比例 - stop_loss_width = abs(entry_price - target_stop_loss) / entry_price - - if stop_loss_width > 0: - # 获取最大单笔亏损率限制 (默认20%) - max_loss_pct = config.TRADING_CONFIG.get('MAX_SINGLE_TRADE_LOSS_PERCENT', 20.0) / 100.0 - - # 计算建议杠杆 - # 理论杠杆 = 最大允许亏损比例 / 止损宽度比例 - theoretical_leverage = max_loss_pct / stop_loss_width - - # 向下取整 - suggested_leverage = int(theoretical_leverage) - - # 限制在最大杠杆范围内 - max_config_leverage = config.TRADING_CONFIG.get('MAX_LEVERAGE', 10) - final_dynamic_leverage = min(suggested_leverage, max_config_leverage) - - # 至少为1倍 - final_dynamic_leverage = max(1, final_dynamic_leverage) - - logger.info(f" ⚖️ 动态杠杆计算:") - logger.info(f" 止损价格: {target_stop_loss:.4f} (宽度: {stop_loss_width*100:.2f}%)") - logger.info(f" 最大单笔亏损限制: {max_loss_pct*100:.1f}%") - logger.info(f" 理论最大杠杆: {theoretical_leverage:.2f}x") - logger.info(f" 配置最大杠杆: {max_config_leverage}x") - logger.info(f" -> 最终建议杠杆: {final_dynamic_leverage}x") - - calculated_leverage = final_dynamic_leverage + calculated_leverage = await self.calculate_dynamic_leverage( + symbol=symbol, + entry_price=entry_price if entry_price else current_price, + stop_loss_price=stop_loss_price, + atr=atr, + side=side + ) # 确定最终使用的杠杆 # 优先级: 动态计算杠杆 > 传入的leverage参数 > 配置的默认LEVERAGE diff --git a/trading_system/trade_recommender.py b/trading_system/trade_recommender.py index 1582730..29604fc 100644 --- a/trading_system/trade_recommender.py +++ b/trading_system/trade_recommender.py @@ -489,14 +489,23 @@ class TradeRecommender: entry_price = suggested_limit_price # 估算仓位数量和杠杆(用于计算止损止盈) + # 1. 优先计算动态杠杆(基于波动率和风险) + atr = symbol_info.get('atr') + leverage = await self.risk_manager.calculate_dynamic_leverage( + symbol, + entry_price, + stop_loss_price=None, # 让内部使用ATR自动估算止损来计算杠杆 + atr=atr, + side=direction + ) + # 重要语义:suggested_position_pct 表示“保证金占用比例” - account_balance = symbol_info.get('account_balance', 1000) + # 使用传入的 account_balance (默认为1000用于展示) suggested_position_pct = symbol_info.get('suggested_position_pct', 0.05) - leverage = config.TRADING_CONFIG.get('LEVERAGE', 10) # 估算保证金与名义价值 estimated_margin = account_balance * suggested_position_pct - estimated_notional = estimated_margin * leverage if leverage and leverage > 0 else estimated_margin + estimated_notional = estimated_margin * leverage estimated_quantity = estimated_notional / entry_price if entry_price > 0 else 0 # 计算基于保证金的止损止盈 @@ -593,7 +602,8 @@ class TradeRecommender: # 生成用户指南(人话版计划) user_guide = self._generate_user_guide( symbol, direction, suggested_limit_price, stop_loss_price, take_profit_1, take_profit_2, - simple_reason, expected_hold_time, risk_warning, recommendation_category, current_price + simple_reason, expected_hold_time, risk_warning, recommendation_category, current_price, + leverage, suggested_position_pct ) # 基础推荐数据 @@ -626,7 +636,7 @@ class TradeRecommender: # 计划入场价(限价挂单价)作为止损/止盈计算基准 'planned_entry_price': entry_price, 'suggested_position_percent': suggested_position_pct, - 'suggested_leverage': config.TRADING_CONFIG.get('LEVERAGE', 10), + 'suggested_leverage': leverage, 'volume_24h': symbol_info.get('volume24h'), 'volatility': symbol_info.get('volatility'), 'estimated_win_rate': round(estimated_win_rate, 1), @@ -869,7 +879,9 @@ class TradeRecommender: expected_hold_time: str, risk_warning: str, category: str, - current_price: float + current_price: float, + leverage: int, + position_pct: float ) -> str: """ 生成用户指南(人话版计划) @@ -886,6 +898,8 @@ class TradeRecommender: risk_warning: 风险警告 category: 推荐分类 current_price: 当前价格(用于计算反向波动) + leverage: 建议杠杆 + position_pct: 建议仓位(保证金比例) Returns: 用户指南文本 @@ -910,6 +924,10 @@ class TradeRecommender: 【明确的入场价】 建议在 {limit_price:.4f} USDT 附近{direction_action} +【资金管理】 + • 建议杠杆: {leverage}x + • 建议仓位: 保证金占用总资金的 {position_pct*100:.1f}% + 【具体点位】 • 建议挂单价: {limit_price:.4f} USDT • 止损价: {stop_loss:.4f} USDT