a
This commit is contained in:
parent
9e70f90260
commit
efbd149f1f
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user