diff --git a/trading_system/binance_client.py b/trading_system/binance_client.py index 9518e84..ffc89c6 100644 --- a/trading_system/binance_client.py +++ b/trading_system/binance_client.py @@ -1295,6 +1295,13 @@ class BinanceClient: logger.info(f"取消订单成功: {symbol} {order_id}") return True except BinanceAPIException as e: + # -2011 Unknown order sent:订单可能已成交/已撤销/已过期,这是典型幂等场景 + # 取消操作应视为“已达成目标”(不再继续报错刷屏) + code = getattr(e, "code", None) + msg = str(e) + if code == -2011 or "code=-2011" in msg or "Unknown order sent" in msg: + logger.info(f"取消订单幂等成功(订单可能已不存在): {symbol} {order_id} | {e}") + return True logger.error(f"取消订单失败: {e}") return False diff --git a/trading_system/position_manager.py b/trading_system/position_manager.py index bfd0c42..8c6ec93 100644 --- a/trading_system/position_manager.py +++ b/trading_system/position_manager.py @@ -968,14 +968,27 @@ class PositionManager: import traceback logger.error(f" 完整错误堆栈:\n{traceback.format_exc()}") - # 尝试清理本地记录(即使平仓失败) + # 关键:平仓失败时不要盲目清理本地持仓/停止监控,否则会导致“仍有仓位但不再监控/不再自动止损止盈” + # 仅当确认币安已无持仓(或实时持仓为0)时,才清理本地记录。 try: - await self._stop_position_monitoring(symbol) - if symbol in self.active_positions: - del self.active_positions[symbol] - logger.info(f"{symbol} [平仓] 已清理本地持仓记录") - except Exception as cleanup_error: - logger.warning(f"{symbol} [平仓] 清理本地记录时出错: {cleanup_error}") + amt0 = await self._get_live_position_amt(symbol, position_side=None) + except Exception: + amt0 = None + if amt0 is not None and abs(amt0) <= 0: + try: + await self._stop_position_monitoring(symbol) + except Exception: + pass + try: + if symbol in self.active_positions: + del self.active_positions[symbol] + except Exception: + pass + logger.warning(f"{symbol} [平仓] 异常后检查:币安持仓已为0,已清理本地记录") + else: + logger.warning( + f"{symbol} [平仓] 异常后检查:币安持仓仍可能存在(amt={amt0}),保留本地记录与监控,等待下次同步/重试" + ) return False diff --git a/trading_system/risk_manager.py b/trading_system/risk_manager.py index 8daa989..5be66af 100644 --- a/trading_system/risk_manager.py +++ b/trading_system/risk_manager.py @@ -517,12 +517,14 @@ class RiskManager: if stop_loss_price_price is not None: candidate_prices.append(('价格百分比', stop_loss_price_price)) - # 对做多取最大的值(最宽松),对做空取最小的值(最宽松) + # 选择“更宽松/更远”的止损: + # - 做多(BUY):止损越低越宽松 → 取最小值 + # - 做空(SELL):止损越高越宽松 → 取最大值 if side == 'BUY': - stop_loss_price = max(p[1] for p in candidate_prices) + stop_loss_price = min(p[1] for p in candidate_prices) selected_method = [p[0] for p in candidate_prices if p[1] == stop_loss_price][0] else: - stop_loss_price = min(p[1] for p in candidate_prices) + stop_loss_price = max(p[1] for p in candidate_prices) selected_method = [p[0] for p in candidate_prices if p[1] == stop_loss_price][0] # 如果提供了技术分析数据,计算技术止损(允许更紧的止损,但需要在保证金止损范围内) @@ -574,11 +576,12 @@ class RiskManager: ) # 重新选择最终的止损价(包括技术止损) + # 仍保持“更宽松/更远”的选择规则 if side == 'BUY': - final_stop_loss = max(p[1] for p in candidate_prices) + final_stop_loss = min(p[1] for p in candidate_prices) selected_method = [p[0] for p in candidate_prices if p[1] == final_stop_loss][0] else: - final_stop_loss = min(p[1] for p in candidate_prices) + final_stop_loss = max(p[1] for p in candidate_prices) selected_method = [p[0] for p in candidate_prices if p[1] == final_stop_loss][0] logger.info(