This commit is contained in:
薇薇安 2026-01-27 15:56:59 +08:00
parent 8e365b3a9a
commit 8667c07134

View File

@ -2147,7 +2147,25 @@ class PositionManager:
pnl_percent = float(trade.get("pnl_percent", 0) or 0) pnl_percent = float(trade.get("pnl_percent", 0) or 0)
# 1. 优先检查止损价格匹配提高容忍度到10% # ⚠️ 2026-01-27关键修复优先检查盈亏情况避免盈利单被错误标记为止损
# 1. 优先检查盈亏情况
if pnl_percent > 0:
# 盈利单:优先检查止盈价格匹配
if tp is not None and _close_to(ep, float(tp), max_pct=0.10):
exit_reason = "take_profit"
elif tp1 is not None and _close_to(ep, float(tp1), max_pct=0.10):
exit_reason = "take_profit"
elif tp2 is not None and _close_to(ep, float(tp2), max_pct=0.10):
exit_reason = "take_profit"
# 如果盈利但没有匹配止盈价,可能是移动止损或手动平仓
elif exit_reason == "sync":
# 检查是否有移动止损标记
if is_reduce_only:
exit_reason = "trailing_stop" # 可能是移动止损
else:
exit_reason = "manual" # 可能是手动平仓
else:
# 亏损单:检查止损价格匹配
if sl is not None and entry_price_val > 0 and ep > 0: if sl is not None and entry_price_val > 0 and ep > 0:
sl_val = float(sl) sl_val = float(sl)
# 价格匹配:平仓价接近止损价 # 价格匹配:平仓价接近止损价
@ -2160,7 +2178,7 @@ class PositionManager:
exit_reason = "stop_loss" exit_reason = "stop_loss"
logger.info(f"{trade.get('symbol')} [同步] 价格方向匹配止损,且亏损{pnl_percent:.2f}%,标记为止损") logger.info(f"{trade.get('symbol')} [同步] 价格方向匹配止损,且亏损{pnl_percent:.2f}%,标记为止损")
# 2. 检查止盈价格匹配 # 2. 如果仍未确定,检查止盈价格匹配(作为备选)
if exit_reason == "sync" and ep > 0: if exit_reason == "sync" and ep > 0:
if tp is not None and _close_to(ep, float(tp), max_pct=0.10): if tp is not None and _close_to(ep, float(tp), max_pct=0.10):
exit_reason = "take_profit" exit_reason = "take_profit"
@ -2732,11 +2750,27 @@ class PositionManager:
# 盈利超过阈值后(相对于保证金),激活移动止损 # 盈利超过阈值后(相对于保证金),激活移动止损
if pnl_percent_margin > trailing_activation * 100: if pnl_percent_margin > trailing_activation * 100:
position_info['trailingStopActivated'] = True position_info['trailingStopActivated'] = True
# 将止损移至成本价(保本) # ⚠️ 2026-01-27修复移动止损激活时不应该将止损移至成本价
position_info['stopLoss'] = entry_price # 应该设置为"保护利润"的价格如盈利5%后保护2.5%利润)
# 计算需要保护的利润金额
protect_amount = margin * trailing_protect
# 计算对应的止损价(保护利润)
if position_info['side'] == 'BUY':
# 保护利润:当前盈亏 - 保护金额 = (止损价 - 开仓价) × 数量
# 所以:止损价 = 开仓价 + (当前盈亏 - 保护金额) / 数量
new_stop_loss = entry_price + (pnl_amount - protect_amount) / quantity
# 确保止损价不低于成本价(保本)
new_stop_loss = max(new_stop_loss, entry_price)
else: # SELL
# 做空:止损价 = 开仓价 + (当前盈亏 - 保护金额) / 数量
new_stop_loss = entry_price + (pnl_amount - protect_amount) / quantity
# 确保止损价不高于成本价(保本)
new_stop_loss = min(new_stop_loss, entry_price)
position_info['stopLoss'] = new_stop_loss
logger.info( logger.info(
f"{symbol} [实时监控] 移动止损激活: 止损移至成本价 {entry_price:.4f} " f"{symbol} [实时监控] 移动止损激活: 止损移至保护利润位 {new_stop_loss:.4f} "
f"(盈利: {pnl_percent_margin:.2f}% of margin)" f"(盈利: {pnl_percent_margin:.2f}% of margin, 保护: {trailing_protect*100:.1f}% of margin)"
) )
else: else:
# ⚠️ 优化:如果分步止盈第一目标已触发,移动止损不再更新剩余仓位的止损价 # ⚠️ 优化:如果分步止盈第一目标已触发,移动止损不再更新剩余仓位的止损价
@ -2806,8 +2840,27 @@ class PositionManager:
f"监控状态: {'运行中' if symbol in self._monitor_tasks else '未启动'}" f"监控状态: {'运行中' if symbol in self._monitor_tasks else '未启动'}"
) )
# ⚠️ 2026-01-27关键修复止损检查前先检查是否盈利
# 如果盈利,不应该触发止损(除非是移动止损或分步止盈后的剩余仓位)
# 直接比较当前盈亏百分比与止损目标(基于保证金) # 直接比较当前盈亏百分比与止损目标(基于保证金)
if pnl_percent_margin <= -stop_loss_pct_margin: if pnl_percent_margin <= -stop_loss_pct_margin:
# ⚠️ 额外检查如果盈利可能是移动止损触发应该标记为trailing_stop
if pnl_percent_margin > 0:
# 盈利单触发止损,应该是移动止损
if position_info.get('trailingStopActivated'):
exit_reason_sl = 'trailing_stop'
logger.warning(f"{symbol} [实时监控] ⚠️ 盈利单触发止损,标记为移动止损(盈利: {pnl_percent_margin:.2f}% of margin")
else:
# 盈利单但未激活移动止损,可能是分步止盈后的剩余仓位止损
if partial_profit_taken:
exit_reason_sl = 'take_profit_partial_then_stop'
logger.info(f"{symbol} [实时监控] 第一目标止盈后,剩余仓位触发止损(保本)")
else:
# 异常情况:盈利单触发止损但未激活移动止损
exit_reason_sl = 'trailing_stop' # 默认标记为移动止损
logger.warning(f"{symbol} [实时监控] ⚠️ 异常:盈利单触发止损但未激活移动止损,标记为移动止损")
else:
# 正常止损逻辑
should_close_due_to_sl = True should_close_due_to_sl = True
# ⚠️ 2026-01-27优化如果已部分止盈细分状态 # ⚠️ 2026-01-27优化如果已部分止盈细分状态
if partial_profit_taken: if partial_profit_taken: