Fix LIVE SoT P0/P1: closing state machine, OO exchange fills, BN balances.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -477,19 +477,53 @@ class StrategyEngine:
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
elif st == "closing":
|
||||
close_oo = getattr(
|
||||
self.matcher, "close_winning_oo_leave_residual", None
|
||||
)
|
||||
if callable(close_oo):
|
||||
r = close_oo(reason="emergency_closing")
|
||||
else:
|
||||
r = self.matcher.close_group(
|
||||
reason="emergency", bypass_liquidity=True
|
||||
)
|
||||
ok = r.ok
|
||||
detail = r.detail
|
||||
close_data = r.data
|
||||
if r.ok:
|
||||
self.enter_rest_after_close()
|
||||
elif st in ("open", "option_closed_perp_pending"):
|
||||
# A:双腿(或续平永续)
|
||||
r = self.matcher.close_group(reason="emergency", bypass_liquidity=True)
|
||||
if not r.ok and st == "open":
|
||||
# B:砸不出期权时强制只平永续(不要求远虚)
|
||||
abandon = getattr(self.matcher, "close_perp_abandon_option", None)
|
||||
if callable(abandon):
|
||||
try:
|
||||
r2 = abandon(reason="emergency_perp", require_deep_otm=False)
|
||||
except TypeError:
|
||||
r2 = abandon(reason="emergency_perp")
|
||||
if r2.ok:
|
||||
r = r2
|
||||
is_oo = (
|
||||
str(pos.get("hedge_mode") or "") == "option_option"
|
||||
or bool(pos.get("option2_inst_id"))
|
||||
)
|
||||
if is_oo and st == "open":
|
||||
close_full = getattr(self.matcher, "close_oo_full", None)
|
||||
if callable(close_full):
|
||||
r = close_full(reason="emergency", bypass_liquidity=True)
|
||||
else:
|
||||
r = self.matcher.close_group(
|
||||
reason="emergency", bypass_liquidity=True
|
||||
)
|
||||
else:
|
||||
# A:双腿(或续平永续)
|
||||
r = self.matcher.close_group(
|
||||
reason="emergency", bypass_liquidity=True
|
||||
)
|
||||
if not r.ok and st == "open" and not is_oo:
|
||||
# B:砸不出期权时强制只平永续(不要求远虚)
|
||||
abandon = getattr(
|
||||
self.matcher, "close_perp_abandon_option", None
|
||||
)
|
||||
if callable(abandon):
|
||||
try:
|
||||
r2 = abandon(
|
||||
reason="emergency_perp", require_deep_otm=False
|
||||
)
|
||||
except TypeError:
|
||||
r2 = abandon(reason="emergency_perp")
|
||||
if r2.ok:
|
||||
r = r2
|
||||
ok = r.ok
|
||||
detail = r.detail
|
||||
close_data = r.data
|
||||
@@ -948,6 +982,24 @@ class StrategyEngine:
|
||||
)
|
||||
return
|
||||
|
||||
if st_pos == "closing":
|
||||
allowed, left = self._retry_allowed("closing")
|
||||
if not allowed:
|
||||
self._set_state(
|
||||
phase="closing",
|
||||
last_error=f"closing 收尾退避中,{left:.0f}s 后再试",
|
||||
)
|
||||
return
|
||||
close_oo = getattr(self.matcher, "close_winning_oo_leave_residual", None)
|
||||
if callable(close_oo):
|
||||
r = await asyncio.to_thread(close_oo, reason="closing_retry")
|
||||
self._note_retry_result("closing", ok=r.ok, detail=r.detail)
|
||||
if r.ok:
|
||||
self.enter_rest_after_close()
|
||||
else:
|
||||
self._set_state(phase="closing", last_error=r.detail)
|
||||
return
|
||||
|
||||
if st_pos == "open":
|
||||
upl = self.matcher.unrealized()
|
||||
from .exits import lock_trade_exit_target, read_locked_exit_target
|
||||
@@ -1001,8 +1053,24 @@ class StrategyEngine:
|
||||
# 复用 ExitDecision 形态
|
||||
from .exits import ExitDecision
|
||||
|
||||
should_semi = bool(semi_d.should_close)
|
||||
if should_semi:
|
||||
# 出场前要求持仓期权有买一,避免无对手盘硬平
|
||||
opt_id = str(upl.get("option_inst_id") or "")
|
||||
oq = None
|
||||
try:
|
||||
oq = self.matcher._quote_held_option(opt_id)
|
||||
except Exception:
|
||||
oq = None
|
||||
if oq is None or oq.bid is None or float(oq.bid) <= 0:
|
||||
should_semi = False
|
||||
self._set_state(
|
||||
phase="liquidity_wait",
|
||||
last_error="半自动已达标但期权无买一,等待流动性",
|
||||
)
|
||||
return
|
||||
decision = ExitDecision(
|
||||
bool(semi_d.should_close),
|
||||
should_semi,
|
||||
str(semi_d.reason or ""),
|
||||
float(semi_d.net_target or 0),
|
||||
)
|
||||
|
||||
@@ -70,8 +70,15 @@ def _live_balances() -> dict[str, float | None]:
|
||||
|
||||
ex = str(load_runtime_settings().exchange or "").strip().lower()
|
||||
if ex in ("binance", "bn"):
|
||||
# 币安资金接口尚未接入;返回 None 使可开判定为「未知」而非误用 OKX
|
||||
logger.debug("open_capacity: binance live balance not wired; treating as unknown")
|
||||
from ..live.binance_trade import BinanceTradeClient
|
||||
|
||||
client = BinanceTradeClient()
|
||||
try:
|
||||
bal = client.fetch_balances()
|
||||
out["trading_usdt"] = _f(bal.get("trading_usdt"))
|
||||
out["trading_usdc"] = _f(bal.get("trading_usdc"))
|
||||
finally:
|
||||
client.close()
|
||||
else:
|
||||
from ..live.okx_funds import OkxFundsClient
|
||||
|
||||
|
||||
Reference in New Issue
Block a user