a
This commit is contained in:
parent
14b5acae09
commit
fb3d1a0dda
|
|
@ -1552,22 +1552,42 @@ class BinanceClient:
|
||||||
if sp <= 0:
|
if sp <= 0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 触发方向约束:
|
# 触发方向约束(避免立即触发):
|
||||||
# - long 止损:价格 <= stopPrice(stopPrice 应 < current)
|
# - long 止损:价格 <= stopPrice(stopPrice 应 < current,至少差一个 min_step)
|
||||||
# - short 止损:价格 >= stopPrice(stopPrice 应 > current)
|
# - short 止损:价格 >= stopPrice(stopPrice 应 > current,至少差一个 min_step)
|
||||||
# - long 止盈:价格 >= stopPrice(stopPrice 应 > current)
|
# - long 止盈:价格 >= stopPrice(stopPrice 应 > current,至少差一个 min_step)
|
||||||
# - short 止盈:价格 <= stopPrice(stopPrice 应 < current)
|
# - short 止盈:价格 <= stopPrice(stopPrice 应 < current,至少差一个 min_step)
|
||||||
if cp and cp > 0:
|
if cp and cp > 0:
|
||||||
if ttype == "STOP_MARKET":
|
if ttype == "STOP_MARKET":
|
||||||
if pd == "BUY" and sp >= cp:
|
if pd == "BUY":
|
||||||
sp = max(0.0, cp - min_step)
|
# 做多止损:止损价必须 < 当前价,至少差一个 min_step
|
||||||
if pd == "SELL" and sp <= cp:
|
if sp >= cp:
|
||||||
sp = cp + min_step
|
# 如果止损价 >= 当前价,调整为当前价 - 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 ttype == "TAKE_PROFIT_MARKET":
|
||||||
if pd == "BUY" and sp <= cp:
|
if pd == "BUY":
|
||||||
sp = cp + min_step
|
# 做多止盈:止盈价必须 > 当前价,至少差一个 min_step
|
||||||
if pd == "SELL" and sp >= cp:
|
if sp <= cp:
|
||||||
sp = max(0.0, cp - min_step)
|
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 规则(提高命中概率,避免“显示等于入场价”的误差带来立即触发/永不触发):
|
# rounding 规则(提高命中概率,避免“显示等于入场价”的误差带来立即触发/永不触发):
|
||||||
# 止损:long 用 UP(更靠近当前价),short 用 DOWN
|
# 止损:long 用 UP(更靠近当前价),short 用 DOWN
|
||||||
|
|
|
||||||
|
|
@ -1158,24 +1158,107 @@ class PositionManager:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"{symbol} 取消旧保护单时出错(可忽略): {e}")
|
logger.debug(f"{symbol} 取消旧保护单时出错(可忽略): {e}")
|
||||||
|
|
||||||
# 获取当前价格(如果未提供)
|
# 获取当前价格(如果未提供,优先使用标记价格 MARK_PRICE,因为止损单使用 MARK_PRICE)
|
||||||
if current_price is None:
|
if current_price is None:
|
||||||
try:
|
try:
|
||||||
ticker = await self.client.get_ticker_24h(symbol)
|
# 优先获取标记价格(MARK_PRICE),因为止损单使用 MARK_PRICE 作为触发基准
|
||||||
if ticker:
|
positions = await self.client.get_open_positions()
|
||||||
current_price = ticker.get('price')
|
position = next((p for p in positions if p['symbol'] == symbol), None)
|
||||||
logger.debug(f"{symbol} 获取当前价格: {current_price}")
|
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:
|
except Exception as e:
|
||||||
logger.warning(f"{symbol} 获取当前价格失败: {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(
|
# 在挂止损单前,检查当前价格是否已经触发止损(避免 -2021 错误)
|
||||||
symbol=symbol,
|
if current_price and stop_loss:
|
||||||
position_direction=side,
|
try:
|
||||||
trigger_type="STOP_MARKET",
|
current_price_val = float(current_price)
|
||||||
stop_price=stop_loss,
|
stop_loss_val = float(stop_loss)
|
||||||
current_price=current_price,
|
entry_price_val = float(entry_price) if entry_price else None
|
||||||
working_type="MARK_PRICE",
|
|
||||||
)
|
# 检查是否已经触发止损
|
||||||
|
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:
|
if sl_order:
|
||||||
logger.info(f"{symbol} ✓ 止损单已成功挂到交易所: {sl_order.get('algoId', 'N/A')}")
|
logger.info(f"{symbol} ✓ 止损单已成功挂到交易所: {sl_order.get('algoId', 'N/A')}")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user