This commit is contained in:
薇薇安 2026-01-23 09:08:35 +08:00
parent ae953d119e
commit 2ba8d69ee0
2 changed files with 131 additions and 52 deletions

View File

@ -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

View File

@ -285,9 +285,93 @@ class RiskManager:
if not actual_leverage or actual_leverage <= 0:
actual_leverage = 10
# 根据涨跌幅调整仓位大小(涨跌幅越大,保证金占比可以适当增加)
base_position_percent = config.TRADING_CONFIG['MAX_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
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已经计算好了
# 如果未使用固定风险计算,使用原来的方法
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 上限
@ -307,41 +391,27 @@ class RiskManager:
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():