From efbd149f1fb2fee1611382bd574636b1c252be72 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 17:31:47 +0800 Subject: [PATCH] a --- trading_system/binance_client.py | 87 +++++++++++++++++++++++++++--- trading_system/position_manager.py | 36 ++++++++++++- 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/trading_system/binance_client.py b/trading_system/binance_client.py index e84c275..824ed62 100644 --- a/trading_system/binance_client.py +++ b/trading_system/binance_client.py @@ -1327,8 +1327,38 @@ class BinanceClient: # python-binance 内部会自动补 timestamp / signature res = await self.client._request_futures_api("post", "algoOrder", True, data=params) return res if isinstance(res, dict) else None + except BinanceAPIException as e: + error_code = e.code if hasattr(e, 'code') else None + error_msg = str(e) + symbol = params.get('symbol', 'UNKNOWN') + trigger_type = params.get('type', 'UNKNOWN') + + # 详细错误日志 + logger.error(f"{symbol} ❌ 创建 Algo 条件单失败({trigger_type}): {error_msg}") + logger.error(f" 错误代码: {error_code}") + logger.error(f" 参数: {params}") + + # 常见错误码处理 + if error_code == -4014: + logger.error(f" 原因: 价格步长错误,triggerPrice 需要调整到 tickSize 的倍数") + elif error_code == -4164: + logger.error(f" 原因: 订单名义价值不足(至少需要 5 USDT)") + elif error_code == -2022: + logger.error(f" 原因: ReduceOnly 订单被拒绝(可能没有持仓或持仓方向不对)") + elif error_code == -4120: + logger.error(f" 原因: 不支持的条件单类型(可能需要使用 Algo 接口)") + elif "immediately trigger" in error_msg.lower() or "would immediately trigger" in error_msg.lower(): + logger.error(f" 原因: 触发价格会导致立即触发(止损/止盈价不在正确一侧)") + elif "position" in error_msg.lower(): + logger.error(f" 原因: 持仓相关问题(可能没有持仓或持仓方向不匹配)") + + return None except Exception as e: - logger.warning(f"{params.get('symbol')} 创建 Algo 条件单失败: {e}") + symbol = params.get('symbol', 'UNKNOWN') + logger.error(f"{symbol} ❌ 创建 Algo 条件单失败: {type(e).__name__}: {e}") + logger.error(f" 参数: {params}") + import traceback + logger.debug(f" 堆栈跟踪: {traceback.format_exc()}") return None async def futures_get_open_algo_orders(self, symbol: Optional[str] = None, algo_type: str = "CONDITIONAL") -> List[Dict[str, Any]]: @@ -1522,22 +1552,67 @@ class BinanceClient: order = await self.futures_create_algo_order(params) if order: return order + # 兜底:对冲/单向模式可能导致 positionSide 误判,尝试切换一次 + logger.warning(f"{symbol} 首次挂保护单失败,尝试切换 positionSide 重试...") if dual is True: retry = dict(params) retry.pop("positionSide", None) - return await self.futures_create_algo_order(retry) + logger.debug(f"{symbol} 重试1: 移除 positionSide(对冲模式 -> 单向模式)") + retry_order = await self.futures_create_algo_order(retry) + if retry_order: + logger.info(f"{symbol} ✓ 重试成功(移除 positionSide)") + return retry_order else: retry = dict(params) retry["positionSide"] = "LONG" if pd == "BUY" else "SHORT" - return await self.futures_create_algo_order(retry) + logger.debug(f"{symbol} 重试1: 添加 positionSide={retry['positionSide']}(单向模式 -> 对冲模式)") + retry_order = await self.futures_create_algo_order(retry) + if retry_order: + logger.info(f"{symbol} ✓ 重试成功(添加 positionSide)") + return retry_order + + # 如果还是失败,记录详细参数用于调试 + logger.error(f"{symbol} ❌ 所有重试都失败,保护单挂单失败") + logger.error(f" 参数: {params}") + logger.error(f" 对冲模式: {dual}") + return None except BinanceAPIException as e: - # 常见:Order would immediately trigger(止损/止盈价不在正确一侧) - logger.warning(f"{symbol} 挂保护单失败({trigger_type}): {e}") + error_code = e.code if hasattr(e, 'code') else None + error_msg = str(e) + + # 详细错误日志 + logger.error(f"{symbol} ❌ 挂保护单失败({trigger_type}): {error_msg}") + logger.error(f" 错误代码: {error_code}") + logger.error(f" 触发价格: {stop_price:.8f} (格式化后: {stop_price_str})") + logger.error(f" 当前价格: {cp if cp else 'N/A'}") + logger.error(f" 持仓方向: {pd}") + logger.error(f" 平仓方向: {close_side}") + logger.error(f" 工作类型: {working_type}") + if symbol_info: + logger.error(f" 价格精度: {pp}, 价格步长: {tick}") + + # 常见错误码处理 + if error_code == -4014: + logger.error(f" 原因: 价格步长错误,需要调整到 tickSize 的倍数") + elif error_code == -4164: + logger.error(f" 原因: 订单名义价值不足(至少需要 5 USDT)") + elif error_code == -2022: + logger.error(f" 原因: ReduceOnly 订单被拒绝(可能没有持仓或持仓方向不对)") + elif "immediately trigger" in error_msg.lower() or "would immediately trigger" in error_msg.lower(): + logger.error(f" 原因: 触发价格会导致立即触发(止损/止盈价不在正确一侧)") + logger.error(f" 建议: 检查止损/止盈价格计算是否正确") + elif "position" in error_msg.lower(): + logger.error(f" 原因: 持仓相关问题(可能没有持仓或持仓方向不匹配)") + return None except Exception as e: - logger.warning(f"{symbol} 挂保护单失败({trigger_type}): {e}") + logger.error(f"{symbol} ❌ 挂保护单失败({trigger_type}): {type(e).__name__}: {e}") + logger.error(f" 触发价格: {stop_price:.8f}") + logger.error(f" 持仓方向: {pd}") + import traceback + logger.debug(f" 堆栈跟踪: {traceback.format_exc()}") return None async def set_leverage(self, symbol: str, leverage: int = 10) -> bool: diff --git a/trading_system/position_manager.py b/trading_system/position_manager.py index 80d918f..79057b5 100644 --- a/trading_system/position_manager.py +++ b/trading_system/position_manager.py @@ -1131,15 +1131,42 @@ class PositionManager: take_profit = None if not stop_loss or not take_profit: + logger.warning(f"{symbol} 止损或止盈价格为空,跳过挂保护单: stop_loss={stop_loss}, take_profit={take_profit}") return + # 验证止损价格是否合理 + entry_price = position_info.get("entryPrice") + if entry_price: + try: + entry_price_val = float(entry_price) + stop_loss_val = float(stop_loss) + # 验证止损价格方向:BUY时止损价应低于入场价,SELL时止损价应高于入场价 + if side == "BUY" and stop_loss_val >= entry_price_val: + logger.error(f"{symbol} ❌ 止损价格错误: BUY时止损价({stop_loss_val:.8f})应低于入场价({entry_price_val:.8f})") + return + if side == "SELL" and stop_loss_val <= entry_price_val: + logger.error(f"{symbol} ❌ 止损价格错误: SELL时止损价({stop_loss_val:.8f})应高于入场价({entry_price_val:.8f})") + return + except Exception as e: + logger.warning(f"{symbol} 验证止损价格时出错: {e}") + # 防重复:先取消旧的保护单(仅取消特定类型,避免误伤普通挂单) try: await self.client.cancel_open_algo_orders_by_order_types( symbol, {"STOP_MARKET", "TAKE_PROFIT_MARKET", "TRAILING_STOP_MARKET"} ) - except Exception: - pass + except Exception as e: + logger.debug(f"{symbol} 取消旧保护单时出错(可忽略): {e}") + + # 获取当前价格(如果未提供) + if current_price is None: + try: + ticker = await self.client.get_ticker_24h(symbol) + if ticker: + current_price = ticker.get('price') + logger.debug(f"{symbol} 获取当前价格: {current_price}") + except Exception as e: + logger.warning(f"{symbol} 获取当前价格失败: {e}") sl_order = await self.client.place_trigger_close_position_order( symbol=symbol, @@ -1153,6 +1180,11 @@ class PositionManager: logger.info(f"{symbol} ✓ 止损单已成功挂到交易所: {sl_order.get('algoId', 'N/A')}") else: logger.error(f"{symbol} ❌ 止损单挂单失败!将依赖WebSocket监控,但可能无法及时止损") + logger.error(f" 止损价格: {stop_loss:.8f}") + logger.error(f" 当前价格: {current_price if current_price else 'N/A'}") + logger.error(f" 持仓方向: {side}") + logger.error(f" ⚠️ 警告: 没有交易所级别的止损保护,如果系统崩溃或网络中断,可能无法及时止损!") + logger.error(f" 💡 建议: 检查止损价格计算是否正确,或手动在币安界面设置止损") tp_order = await self.client.place_trigger_close_position_order( symbol=symbol,