From 2ba8d69ee052dc6376a2ba8cf8c7ee083a5ffd73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Fri, 23 Jan 2026 09:08:35 +0800 Subject: [PATCH] a --- trading_system/position_manager.py | 9 ++ trading_system/risk_manager.py | 174 ++++++++++++++++++++--------- 2 files changed, 131 insertions(+), 52 deletions(-) diff --git a/trading_system/position_manager.py b/trading_system/position_manager.py index 37f0715..5ec2155 100644 --- a/trading_system/position_manager.py +++ b/trading_system/position_manager.py @@ -1149,6 +1149,11 @@ class PositionManager: current_price=current_price, working_type="MARK_PRICE", ) + if sl_order: + logger.info(f"{symbol} ✓ 止损单已成功挂到交易所: {sl_order.get('algoId', 'N/A')}") + else: + logger.error(f"{symbol} ❌ 止损单挂单失败!将依赖WebSocket监控,但可能无法及时止损") + tp_order = await self.client.place_trigger_close_position_order( symbol=symbol, position_direction=side, @@ -1157,6 +1162,10 @@ class PositionManager: current_price=current_price, working_type="MARK_PRICE", ) + if tp_order: + logger.info(f"{symbol} ✓ 止盈单已成功挂到交易所: {tp_order.get('algoId', 'N/A')}") + else: + logger.warning(f"{symbol} ⚠️ 止盈单挂单失败,将依赖WebSocket监控") try: # Algo 接口返回 algoId diff --git a/trading_system/risk_manager.py b/trading_system/risk_manager.py index 450c5bc..b347390 100644 --- a/trading_system/risk_manager.py +++ b/trading_system/risk_manager.py @@ -285,63 +285,133 @@ class RiskManager: if not actual_leverage or actual_leverage <= 0: actual_leverage = 10 - # 根据涨跌幅调整仓位大小(涨跌幅越大,保证金占比可以适当增加) - base_position_percent = config.TRADING_CONFIG['MAX_POSITION_PERCENT'] - max_position_percent = config.TRADING_CONFIG['MAX_POSITION_PERCENT'] - min_position_percent = config.TRADING_CONFIG['MIN_POSITION_PERCENT'] + # ⚠️ 优化3:固定风险百分比仓位计算(凯利公式) + # 公式:仓位大小 = (总资金 * 每笔单子承受的风险%) / (入场价 - 止损价) + use_fixed_risk = config.TRADING_CONFIG.get('USE_FIXED_RISK_SIZING', True) + fixed_risk_percent = config.TRADING_CONFIG.get('FIXED_RISK_PERCENT', 0.02) # 默认2% + quantity = None - # 涨跌幅超过5%时,可以适当增加保证金占比,但必须遵守 MAX_POSITION_PERCENT 上限 - if abs(change_percent) > 5: - position_percent = min( - base_position_percent * 1.5, - max_position_percent - ) - logger.info(f" 涨跌幅 {change_percent:.2f}% > 5%,使用增强仓位比例: {position_percent*100:.1f}%") - else: - position_percent = base_position_percent - logger.info(f" 涨跌幅 {change_percent:.2f}%,使用标准仓位比例: {position_percent*100:.1f}%") + if use_fixed_risk and entry_price and side: + # 如果未提供止损价格,先估算 + if stop_loss_price is None: + # 尝试使用ATR估算止损距离 + if atr and atr > 0: + atr_multiplier = config.TRADING_CONFIG.get('ATR_STOP_LOSS_MULTIPLIER', 1.8) + if side == 'BUY': + estimated_stop_loss = entry_price - (atr * atr_multiplier) + else: # SELL + estimated_stop_loss = entry_price + (atr * atr_multiplier) + stop_loss_price = estimated_stop_loss + else: + # 使用固定百分比估算(基于保证金) + stop_loss_pct = config.TRADING_CONFIG.get('STOP_LOSS_PERCENT', 0.10) + # 先估算一个临时仓位来计算止损距离 + temp_margin = total_balance * 0.05 # 临时使用5%保证金 + temp_notional = temp_margin * actual_leverage + temp_quantity = temp_notional / entry_price + stop_loss_amount = temp_margin * stop_loss_pct + if side == 'BUY': + estimated_stop_loss = entry_price - (stop_loss_amount / temp_quantity) + else: # SELL + estimated_stop_loss = entry_price + (stop_loss_amount / temp_quantity) + stop_loss_price = estimated_stop_loss + + # 计算止损距离 + if side == 'BUY': + stop_distance = entry_price - stop_loss_price + else: # SELL + stop_distance = stop_loss_price - entry_price + + if stop_distance > 0: + # 固定风险金额 + risk_amount = total_balance * fixed_risk_percent + + # 根据止损距离反算仓位 + # 风险金额 = (入场价 - 止损价) × 数量 + # 所以:数量 = 风险金额 / (入场价 - 止损价) + quantity = risk_amount / stop_distance + + # 计算对应的保证金和名义价值 + notional_value = quantity * entry_price + margin_value = notional_value / actual_leverage + + logger.info(f" ⚠️ 使用固定风险百分比计算仓位:") + logger.info(f" 固定风险: {fixed_risk_percent*100:.2f}% = {risk_amount:.4f} USDT") + logger.info(f" 止损距离: {stop_distance:.4f} USDT ({stop_distance/entry_price*100:.2f}%)") + logger.info(f" 计算数量: {quantity:.4f}") + logger.info(f" 名义价值: {notional_value:.2f} USDT") + logger.info(f" 保证金: {margin_value:.4f} USDT ({margin_value/total_balance*100:.2f}%)") + + # 检查是否超过最大仓位限制 + max_position_percent = config.TRADING_CONFIG['MAX_POSITION_PERCENT'] + max_margin_value = available_balance * max_position_percent + + if margin_value > max_margin_value: + # 如果超过最大仓位,使用最大仓位 + logger.warning(f" ⚠️ 固定风险计算的保证金 {margin_value:.4f} USDT > 最大限制 {max_margin_value:.2f} USDT") + logger.info(f" ✓ 调整为最大仓位限制: {max_margin_value:.2f} USDT") + margin_value = max_margin_value + notional_value = margin_value * actual_leverage + quantity = notional_value / entry_price + else: + # 使用固定风险计算的仓位 + pass # quantity已经计算好了 - # 计算保证金与名义价值 - margin_value = available_balance * position_percent - notional_value = margin_value * actual_leverage - logger.info(f" 计算保证金: {margin_value:.4f} USDT ({position_percent*100:.1f}% of {available_balance:.2f})") - logger.info(f" 计算名义价值: {notional_value:.2f} USDT (保证金 {margin_value:.4f} × 杠杆 {actual_leverage}x)") + # 如果未使用固定风险计算,使用原来的方法 + if quantity is None: + # ⚠️ 优化3:信号强度分级 - 9-10分高权重,8分轻仓 + signal_multiplier = 1.0 + if signal_strength is not None: + signal_multipliers = config.TRADING_CONFIG.get('SIGNAL_STRENGTH_POSITION_MULTIPLIER', {8: 0.5, 9: 1.0, 10: 1.0}) + signal_multiplier = signal_multipliers.get(signal_strength, 1.0) + if signal_strength == 8: + logger.info(f" ⚠️ 信号强度8分,使用50%仓位(轻仓试探)") + elif signal_strength >= 9: + logger.info(f" ✓ 信号强度{signal_strength}分,使用100%仓位(高质量信号)") + + # 根据涨跌幅调整仓位大小(涨跌幅越大,保证金占比可以适当增加) + base_position_percent = config.TRADING_CONFIG['MAX_POSITION_PERCENT'] * signal_multiplier + max_position_percent = config.TRADING_CONFIG['MAX_POSITION_PERCENT'] * signal_multiplier + min_position_percent = config.TRADING_CONFIG['MIN_POSITION_PERCENT'] + + # 涨跌幅超过5%时,可以适当增加保证金占比,但必须遵守 MAX_POSITION_PERCENT 上限 + if abs(change_percent) > 5: + position_percent = min( + base_position_percent * 1.5, + max_position_percent + ) + logger.info(f" 涨跌幅 {change_percent:.2f}% > 5%,使用增强仓位比例: {position_percent*100:.1f}%") + else: + position_percent = base_position_percent + logger.info(f" 涨跌幅 {change_percent:.2f}%,使用标准仓位比例: {position_percent*100:.1f}%") + + # 计算保证金与名义价值 + margin_value = available_balance * position_percent + notional_value = margin_value * actual_leverage + logger.info(f" 计算保证金: {margin_value:.4f} USDT ({position_percent*100:.1f}% of {available_balance:.2f})") + logger.info(f" 计算名义价值: {notional_value:.2f} USDT (保证金 {margin_value:.4f} × 杠杆 {actual_leverage}x)") + + # 计算数量 + quantity = notional_value / current_price + + logger.info(f" 计算数量: {quantity:.4f}") + + # 计算名义价值和保证金(如果还未计算) + if 'notional_value' not in locals() or 'margin_value' not in locals(): + notional_value = quantity * current_price + margin_value = notional_value / actual_leverage # 确保仓位价值满足最小名义价值要求(币安要求至少5 USDT) min_notional = 5.0 # 币安合约最小名义价值 - if notional_value < min_notional: - logger.warning(f" ⚠ 名义价值 {notional_value:.2f} USDT < 最小名义价值 {min_notional:.2f} USDT") - - # 计算需要的最小保证金来满足最小名义价值:margin >= min_notional / leverage - required_margin = min_notional / actual_leverage - required_margin_percent = required_margin / available_balance - logger.info( - f" 需要的最小保证金: {required_margin:.4f} USDT " - f"(占比 {required_margin_percent*100:.2f}%)" - ) - - # 检查是否可以使用更大的保证金占比(但不超过最大保证金限制) - if required_margin_percent <= max_position_percent: - position_percent = required_margin_percent - margin_value = required_margin - notional_value = min_notional - logger.info(f" ✓ 调整保证金占比到 {position_percent*100:.2f}% 以满足最小名义价值: {notional_value:.2f} USDT") - else: - # 即使使用最大仓位比例也无法满足最小名义价值 - max_allowed_margin = available_balance * max_position_percent - max_allowed_notional = max_allowed_margin * actual_leverage - logger.warning( - f" ❌ 无法满足最小名义价值要求: " - f"需要 {min_notional:.2f} USDT (需要保证金 {required_margin:.4f} USDT)," - f"但最大允许名义 {max_allowed_notional:.2f} USDT (最大保证金 {max_allowed_margin:.2f} USDT)" - ) - logger.warning(f" 💡 建议: 提高 MAX_POSITION_PERCENT 或降低杠杆/更换币种,确保最小名义价值可满足") - return None - - # quantity 应该已经计算好了(固定风险或传统方法) - if quantity is None: - logger.error(f" ❌ {symbol} 仓位计算失败:quantity为None") - return None + calculated_notional = quantity * current_price + if calculated_notional < min_notional: + # 如果计算出的名义价值仍然不足,增加数量 + required_quantity = min_notional / current_price + logger.warning(f" ⚠ 计算出的名义价值 {calculated_notional:.2f} USDT < {min_notional:.2f} USDT") + logger.info(f" ✓ 调整数量从 {quantity:.4f} 到 {required_quantity:.4f}") + quantity = required_quantity + notional_value = required_quantity * current_price + margin_value = notional_value / actual_leverage # 计算名义价值和保证金(如果还未计算) if 'notional_value' not in locals() or 'margin_value' not in locals():