From 22830355c6f2cf0b5f6398dcead5daa68b087516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Fri, 20 Feb 2026 11:23:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(binance=5Fclient):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=B6=85=E6=97=B6=E8=AE=BE=E7=BD=AE=E4=B8=8E=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 `binance_client.py` 中调整了 API 请求的超时设置,首次请求超时为 25 秒,后续请求为 35 秒,以适应币安的响应时间。同时,增加了重试机制的等待时间,首次重试为 4 秒,后续重试为 5 秒,确保在网络波动时能更好地恢复。此外,新增了止盈价合理性校验,避免因错误数据导致的挂单被拒,提升了交易逻辑的稳定性与风险控制能力。 --- trading_system/binance_client.py | 42 ++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/trading_system/binance_client.py b/trading_system/binance_client.py index 1ef4abb..806e23b 100644 --- a/trading_system/binance_client.py +++ b/trading_system/binance_client.py @@ -417,8 +417,8 @@ class BinanceClient: import aiohttp url = f"{self._futures_base_url()}/fapi/v1/listenKey" headers = {"X-MBX-APIKEY": self.api_key} - # ⚠️ 优化:使用较短的超时时间(15秒),失败后快速重试 - timeout_sec = 15 if attempt == 0 else 20 + # 超时:首次 25s,后续 35s(币安有时较慢,避免 20s 超时) + timeout_sec = 25 if attempt == 0 else 35 async with aiohttp.ClientSession() as session: async with session.post(url, headers=headers, timeout=aiohttp.ClientTimeout(total=timeout_sec)) as resp: text = await resp.text() @@ -428,7 +428,7 @@ class BinanceClient: resp.status, (text[:500] if text else ""), attempt + 1, max_retries + 1, ) if attempt < max_retries: - await asyncio.sleep(2) # 等待 2 秒后重试 + await asyncio.sleep(4) # 等待 4 秒后重试,给币安/网络恢复时间 continue return None try: @@ -446,7 +446,7 @@ class BinanceClient: last_error, attempt + 1, max_retries + 1, ) if attempt < max_retries: - await asyncio.sleep(2) # 等待 2 秒后重试 + await asyncio.sleep(5) # 等待 5 秒后重试 continue except Exception as e: last_error = getattr(e, "message", str(e)) or repr(e) @@ -455,7 +455,7 @@ class BinanceClient: type(e).__name__, last_error, attempt + 1, max_retries + 1, ) if attempt < max_retries: - await asyncio.sleep(2) # 等待 2 秒后重试 + await asyncio.sleep(5) # 等待 5 秒后重试 continue logger.error(f"create_futures_listen_key (REST) 重试 {max_retries + 1} 次后仍失败: {last_error}") @@ -2420,6 +2420,27 @@ class BinanceClient: if sp <= 0: return None + # 触发价合理性:止盈价不能偏离当前价过远(避免错误数据导致挂单被拒或 -4509) + if cp and cp > 0 and ttype == "TAKE_PROFIT_MARKET": + if pd == "SELL": + # 做空止盈:触发价应 < 当前价,且不应低于当前价的 1%(避免错用成 0.001 等错误数值) + if sp >= cp: + pass # 下方会做“止盈修正” + elif sp < cp * 0.01: + logger.warning( + f"{symbol} [止盈校验] SELL 止盈价 {sp:.8f} 远低于当前价 {cp:.8f},疑似数据错误,跳过挂单" + ) + return None + else: + # 做多止盈:触发价应 > 当前价,且不应超过当前价的 100 倍 + if sp <= cp: + pass + elif sp > cp * 100: + logger.warning( + f"{symbol} [止盈校验] BUY 止盈价 {sp:.8f} 远高于当前价 {cp:.8f},疑似数据错误,跳过挂单" + ) + return None + # 触发方向约束(避免立即触发): # - long 止损:价格 <= stopPrice(stopPrice 应 < current,至少差一个 min_step) # - short 止损:价格 >= stopPrice(stopPrice 应 > current,至少差一个 min_step) @@ -2509,8 +2530,13 @@ class BinanceClient: except BinanceAPIException as e: error_code = e.code if hasattr(e, 'code') else None error_msg = str(e) - - # 详细错误日志 + # -4509/-4061:持仓已平或方向不匹配,仅打一条 warning,不刷详细日志 + if error_code in (-4509, -4061): + logger.warning( + f"{symbol} 保护单挂单被拒({error_code}): 持仓可能已平或方向不匹配,已跳过" + ) + return None + # 其他错误:详细错误日志 logger.error(f"{symbol} ❌ 挂保护单失败({trigger_type}): {error_msg}") logger.error(f" 错误代码: {error_code}") logger.error(f" 触发价格: {stop_price:.8f} (格式化后: {stop_price_str})") @@ -2521,7 +2547,7 @@ class BinanceClient: if symbol_info: logger.error(f" 价格精度: {pp}, 价格步长: {tick}") - # 常见错误码处理 + # 常见错误码说明 if error_code == -4014: logger.error(f" 原因: 价格步长错误,需要调整到 tickSize 的倍数") elif error_code == -4164: