This commit is contained in:
薇薇安 2026-01-23 21:43:36 +08:00
parent 14b5acae09
commit fb3d1a0dda
2 changed files with 129 additions and 26 deletions

View File

@ -1552,22 +1552,42 @@ class BinanceClient:
if sp <= 0:
return None
# 触发方向约束
# - long 止损:价格 <= stopPricestopPrice 应 < current
# - short 止损:价格 >= stopPricestopPrice 应 > current
# - long 止盈:价格 >= stopPricestopPrice 应 > current
# - short 止盈:价格 <= stopPricestopPrice 应 < current
# 触发方向约束(避免立即触发)
# - long 止损:价格 <= stopPricestopPrice 应 < current,至少差一个 min_step
# - short 止损:价格 >= stopPricestopPrice 应 > current,至少差一个 min_step
# - long 止盈:价格 >= stopPricestopPrice 应 > current,至少差一个 min_step
# - short 止盈:价格 <= stopPricestopPrice 应 < current,至少差一个 min_step
if cp and cp > 0:
if ttype == "STOP_MARKET":
if pd == "BUY" and sp >= cp:
sp = max(0.0, cp - min_step)
if pd == "SELL" and sp <= cp:
sp = cp + min_step
if pd == "BUY":
# 做多止损:止损价必须 < 当前价,至少差一个 min_step
if sp >= cp:
# 如果止损价 >= 当前价,调整为当前价 - min_step但这样止损太紧可能不合理
# 更好的做法是:如果止损价太接近当前价,增加一个安全距离(例如 0.5%
safety_margin = max(min_step, cp * 0.005) # 至少 0.5% 的安全距离
sp = max(0.0, cp - safety_margin)
logger.warning(f"{symbol} [止损修正] BUY止损价({sp:.8f})太接近当前价({cp:.8f}),调整为 {sp:.8f}")
elif pd == "SELL":
# 做空止损:止损价必须 > 当前价,至少差一个 min_step
if sp <= cp:
# 如果止损价 <= 当前价,调整为当前价 + min_step但这样止损太紧可能不合理
# 更好的做法是:如果止损价太接近当前价,增加一个安全距离(例如 0.5%
safety_margin = max(min_step, cp * 0.005) # 至少 0.5% 的安全距离
sp = cp + safety_margin
logger.warning(f"{symbol} [止损修正] SELL止损价({sp:.8f})太接近当前价({cp:.8f}),调整为 {sp:.8f}")
if ttype == "TAKE_PROFIT_MARKET":
if pd == "BUY" and sp <= cp:
sp = cp + min_step
if pd == "SELL" and sp >= cp:
sp = max(0.0, cp - min_step)
if pd == "BUY":
# 做多止盈:止盈价必须 > 当前价,至少差一个 min_step
if sp <= cp:
safety_margin = max(min_step, cp * 0.005)
sp = cp + safety_margin
logger.warning(f"{symbol} [止盈修正] BUY止盈价({sp:.8f})太接近当前价({cp:.8f}),调整为 {sp:.8f}")
elif pd == "SELL":
# 做空止盈:止盈价必须 < 当前价,至少差一个 min_step
if sp >= cp:
safety_margin = max(min_step, cp * 0.005)
sp = max(0.0, cp - safety_margin)
logger.warning(f"{symbol} [止盈修正] SELL止盈价({sp:.8f})太接近当前价({cp:.8f}),调整为 {sp:.8f}")
# rounding 规则(提高命中概率,避免“显示等于入场价”的误差带来立即触发/永不触发):
# 止损long 用 UP更靠近当前价short 用 DOWN

View File

@ -1158,24 +1158,107 @@ class PositionManager:
except Exception as e:
logger.debug(f"{symbol} 取消旧保护单时出错(可忽略): {e}")
# 获取当前价格(如果未提供
# 获取当前价格(如果未提供,优先使用标记价格 MARK_PRICE因为止损单使用 MARK_PRICE
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}")
# 优先获取标记价格MARK_PRICE因为止损单使用 MARK_PRICE 作为触发基准
positions = await self.client.get_open_positions()
position = next((p for p in positions if p['symbol'] == symbol), None)
if position:
mark_price = position.get('markPrice')
if mark_price and float(mark_price) > 0:
current_price = float(mark_price)
logger.debug(f"{symbol} 从持仓获取标记价格: {current_price}")
else:
# 如果没有标记价格,使用 ticker 价格
ticker = await self.client.get_ticker_24h(symbol)
if ticker:
current_price = ticker.get('price')
logger.debug(f"{symbol} 从ticker获取当前价格: {current_price}")
else:
# 如果没有持仓,使用 ticker 价格
ticker = await self.client.get_ticker_24h(symbol)
if ticker:
current_price = ticker.get('price')
logger.debug(f"{symbol} 从ticker获取当前价格: {current_price}")
except Exception as e:
logger.warning(f"{symbol} 获取当前价格失败: {e}")
# 如果仍然没有当前价格,记录警告
if current_price is None:
logger.warning(f"{symbol} ⚠️ 无法获取当前价格,止损单可能无法正确验证触发条件")
sl_order = await self.client.place_trigger_close_position_order(
symbol=symbol,
position_direction=side,
trigger_type="STOP_MARKET",
stop_price=stop_loss,
current_price=current_price,
working_type="MARK_PRICE",
)
# 在挂止损单前,检查当前价格是否已经触发止损(避免 -2021 错误)
if current_price and stop_loss:
try:
current_price_val = float(current_price)
stop_loss_val = float(stop_loss)
entry_price_val = float(entry_price) if entry_price else None
# 检查是否已经触发止损
if side == "BUY":
# 做多:当前价 <= 止损价,说明已触发止损
if current_price_val <= stop_loss_val:
logger.error(f"{symbol} ⚠️ 当前价格({current_price_val:.8f})已触发止损价({stop_loss_val:.8f}),无法挂止损单,应该立即平仓!")
logger.error(f" 入场价: {entry_price_val:.8f if entry_price_val else 'N/A'}")
logger.error(f" 建议: 立即手动平仓或等待WebSocket监控触发平仓")
# 不挂止损单依赖WebSocket监控立即平仓
sl_order = None
else:
sl_order = await self.client.place_trigger_close_position_order(
symbol=symbol,
position_direction=side,
trigger_type="STOP_MARKET",
stop_price=stop_loss,
current_price=current_price,
working_type="MARK_PRICE",
)
elif side == "SELL":
# 做空:当前价 >= 止损价,说明已触发止损
if current_price_val >= stop_loss_val:
logger.error(f"{symbol} ⚠️ 当前价格({current_price_val:.8f})已触发止损价({stop_loss_val:.8f}),无法挂止损单,应该立即平仓!")
logger.error(f" 入场价: {entry_price_val:.8f if entry_price_val else 'N/A'}")
logger.error(f" 建议: 立即手动平仓或等待WebSocket监控触发平仓")
# 不挂止损单依赖WebSocket监控立即平仓
sl_order = None
else:
sl_order = await self.client.place_trigger_close_position_order(
symbol=symbol,
position_direction=side,
trigger_type="STOP_MARKET",
stop_price=stop_loss,
current_price=current_price,
working_type="MARK_PRICE",
)
else:
sl_order = await self.client.place_trigger_close_position_order(
symbol=symbol,
position_direction=side,
trigger_type="STOP_MARKET",
stop_price=stop_loss,
current_price=current_price,
working_type="MARK_PRICE",
)
except Exception as e:
logger.warning(f"{symbol} 检查止损触发条件时出错: {e},继续尝试挂单")
sl_order = await self.client.place_trigger_close_position_order(
symbol=symbol,
position_direction=side,
trigger_type="STOP_MARKET",
stop_price=stop_loss,
current_price=current_price,
working_type="MARK_PRICE",
)
else:
sl_order = await self.client.place_trigger_close_position_order(
symbol=symbol,
position_direction=side,
trigger_type="STOP_MARKET",
stop_price=stop_loss,
current_price=current_price,
working_type="MARK_PRICE",
)
if sl_order:
logger.info(f"{symbol} ✓ 止损单已成功挂到交易所: {sl_order.get('algoId', 'N/A')}")
else: