Harden strategy SoT: fail-closed funds gate, shared open pipeline, LIVE switch guards.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -807,33 +807,28 @@ class StrategyEngine:
|
||||
self._set_state(phase="open", last_error="有未平仓,禁止开下一组")
|
||||
return
|
||||
|
||||
# OKX:先按盘口刷新名义并检测 USDC;不够则交易账户市价兑换(不依赖已选中合格期权)
|
||||
# OKX:等待选约时仅预览名义+兑 USDC(不落库改 qty/exit)
|
||||
try:
|
||||
from .auto_usdc import prepare_okx_trading_usdc
|
||||
from .open_pipeline import prepare_usdc_while_waiting
|
||||
|
||||
prep = prepare_okx_trading_usdc(self.db)
|
||||
prep = prepare_usdc_while_waiting(self.db)
|
||||
conv = prep.get("convert") or {}
|
||||
if conv.get("acted"):
|
||||
self._set_state(
|
||||
last_error=None,
|
||||
phase="wait_signal",
|
||||
)
|
||||
logger.info("auto_usdc prepared: %s", conv.get("detail"))
|
||||
elif conv.get("ok") is False and "不足" in str(conv.get("detail") or ""):
|
||||
# 保留资金提示,但仍继续尝试选约(可能只是冷却/短暂失败)
|
||||
logger.warning("auto_usdc: %s", conv.get("detail"))
|
||||
logger.info("auto_usdc while waiting: %s", conv.get("detail"))
|
||||
elif conv.get("ok") is False:
|
||||
logger.warning("auto_usdc while waiting: %s", conv.get("detail"))
|
||||
except Exception:
|
||||
logger.exception("prepare OKX USDC failed")
|
||||
logger.exception("prepare USDC while waiting failed")
|
||||
|
||||
self._set_state(phase="wait_signal")
|
||||
pick = await get_session().pick_for_open_async()
|
||||
if pick is None:
|
||||
# 若期权仍不可开且刚才未兑成功,把资金状态写进错误,便于排查「为何没自动兑」
|
||||
try:
|
||||
from .open_capacity import assess_open_capacity
|
||||
from .open_capacity import assess_open_capacity, funds_gate_blocks
|
||||
|
||||
cap = assess_open_capacity(self.db)
|
||||
if cap.get("option_can_open") is False:
|
||||
blocked, why = funds_gate_blocks(cap)
|
||||
if blocked and cap.get("option_can_open") is False:
|
||||
self._set_state(
|
||||
phase="wait_funds",
|
||||
last_error=(
|
||||
@@ -843,6 +838,12 @@ class StrategyEngine:
|
||||
),
|
||||
)
|
||||
return
|
||||
if blocked and cap.get("option_can_open") is None:
|
||||
self._set_state(
|
||||
phase="wait_funds",
|
||||
last_error=why,
|
||||
)
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
self._set_state(
|
||||
@@ -850,56 +851,33 @@ class StrategyEngine:
|
||||
)
|
||||
return
|
||||
|
||||
# 以损定仓:每笔开仓前按指数/卖一重算 k,再写名义与出场(须在资金门前)
|
||||
try:
|
||||
from .risk_sizing import apply_risk_sizing_to_ledger
|
||||
# 选约后:定仓落库 → 兑 USDC → 资金门 fail-closed(与手动开仓同一管道)
|
||||
from .open_pipeline import size_and_gate
|
||||
|
||||
rs = apply_risk_sizing_to_ledger(
|
||||
index_px=float(pick.underlying_px),
|
||||
option_ask=float(pick.option_ask),
|
||||
db=self.db,
|
||||
)
|
||||
if not rs.ok:
|
||||
self._set_state(phase="idle", last_error=rs.detail)
|
||||
prep = size_and_gate(
|
||||
index_px=float(pick.underlying_px),
|
||||
option_ask=float(pick.option_ask),
|
||||
db=self.db,
|
||||
)
|
||||
if not prep.ok:
|
||||
phase = "wait_funds" if prep.capacity is not None else "idle"
|
||||
self._set_state(phase=phase, last_error=prep.detail)
|
||||
if prep.capacity is not None:
|
||||
maybe_notify_funds_short(prep.capacity)
|
||||
if "以损定仓" in (prep.detail or ""):
|
||||
try:
|
||||
from ..notify import wecom
|
||||
|
||||
wecom.notify_fault(
|
||||
title="以损定仓失败",
|
||||
detail=rs.detail,
|
||||
dedupe_key=f"risk_sizing:{rs.detail[:80]}",
|
||||
detail=prep.detail,
|
||||
dedupe_key=f"risk_sizing:{prep.detail[:80]}",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
except Exception:
|
||||
logger.exception("risk sizing failed")
|
||||
self._set_state(phase="idle", last_error="以损定仓计算异常,暂不开仓")
|
||||
return
|
||||
|
||||
# 选约后名义可能变化,再检一次 USDC(force 跳过冷却,避免刚选完仍差一截)
|
||||
try:
|
||||
from .auto_usdc import ensure_okx_trading_usdc
|
||||
|
||||
ensure_okx_trading_usdc(self.db, force=True)
|
||||
except Exception:
|
||||
logger.exception("auto USDC top-up failed")
|
||||
|
||||
try:
|
||||
cap = assess_open_capacity(self.db)
|
||||
if cap.get("perp_can_open") is False or cap.get("option_can_open") is False:
|
||||
detail = (
|
||||
f"{cap.get('perp_label')} · {cap.get('option_label')};"
|
||||
f"永续需≈{cap.get('perp_need_usdt')}U/有{cap.get('perp_have_usdt')}U,"
|
||||
f"期权需≈{cap.get('option_need_usdc')}U/有{cap.get('option_have_usdc')}U"
|
||||
)
|
||||
self._set_state(phase="wait_funds", last_error=f"资金不足,暂不可开新仓:{detail}")
|
||||
maybe_notify_funds_short(cap)
|
||||
return
|
||||
if st["phase"] == "wait_funds":
|
||||
self._set_state(phase="idle", last_error=None)
|
||||
except Exception:
|
||||
logger.exception("open capacity gate failed")
|
||||
if st["phase"] == "wait_funds":
|
||||
self._set_state(phase="idle", last_error=None)
|
||||
|
||||
self._set_state(phase="opening", last_error=None)
|
||||
wkey = window_key()
|
||||
|
||||
Reference in New Issue
Block a user