Add OKX trading-account USDC auto-swap and lock exit target while in position.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-30 00:16:47 +08:00
parent f0b0b91f1b
commit 0837982714
16 changed files with 626 additions and 36 deletions
+52 -2
View File
@@ -129,8 +129,26 @@ class StrategyEngine:
risk_exit_unit = self.ledger.get_setting_float("risk_exit_unit", 15.0)
risk_last_k = self.ledger.get_setting_float("risk_last_k", 0.0)
risk_preview: dict[str, Any] | None = None
# 以损定仓:监控页展示「下一次开仓」实时估算,避免仍显示上次手填/过期名义
if sizing_mode == "risk_based":
pos_status = str(upl.get("status") or "flat")
trade_locked = pos_status in (
"open",
"half_open",
"option_closed_perp_pending",
"opening",
)
locked_exit = None
try:
from .exits import read_locked_exit_target
locked_exit = read_locked_exit_target(upl)
except Exception:
locked_exit = None
if trade_locked and locked_exit is not None:
# 持仓中:出场目标锁定,不再用盘口重算覆盖
net_target = float(locked_exit)
exit_amt = float(locked_exit)
# 以损定仓:仅空仓时用实时估算覆盖展示;持仓中保持开仓锁定名义/k/目标
if sizing_mode == "risk_based" and not trade_locked:
try:
from .risk_sizing import preview_risk_sizing
@@ -154,6 +172,16 @@ class StrategyEngine:
except Exception:
logger.exception("risk sizing preview for state() failed")
risk_preview = {"ok": False, "detail": "以损定仓预览失败"}
elif sizing_mode == "risk_based" and trade_locked:
risk_preview = {
"ok": True,
"locked": True,
"detail": "持仓中已锁定本组成交目标与名义,平仓后再自动计算",
"net_profit_target": net_target,
"k": risk_last_k if risk_last_k > 0 else None,
"perp_qty_eth": perp_qty,
"option_qty_eth": opt_qty,
}
rest_until = row["rest_until_ms"]
rest_left = 0
if rest_until:
@@ -199,6 +227,7 @@ class StrategyEngine:
"risk_exit_unit": risk_exit_unit,
"risk_last_k": risk_last_k if risk_last_k > 0 else None,
"risk_sizing_preview": risk_preview,
"risk_sizing_locked": bool(trade_locked and sizing_mode == "risk_based"),
"min_option_hours": min_hours,
"min_option_leverage": min_opt_lev,
"atm_open_offset_enabled": atm_off_on,
@@ -687,6 +716,18 @@ class StrategyEngine:
if st_pos == "open":
upl = self.matcher.unrealized()
from .exits import lock_trade_exit_target, read_locked_exit_target
locked_exit = read_locked_exit_target(upl)
if locked_exit is None and upl.get("group_id"):
try:
locked_exit = lock_trade_exit_target(
self.db,
group_id=str(upl["group_id"]),
initial_premium=float(upl.get("initial_premium") or 0),
)
except Exception:
logger.exception("backfill exit lock failed")
expired = check_expiry_close(expiry_ms=self._position_expiry_ms(upl))
decision = check_exits(
net_pnl=float(upl.get("net_pnl") or 0),
@@ -694,6 +735,7 @@ class StrategyEngine:
net_profit_target=net_target,
premium_exit_multiple=prem_mult,
initial_premium=float(upl.get("initial_premium") or 0),
locked_exit_target=locked_exit,
)
pending_close = st["phase"] in ("liquidity_wait", "closing")
if expired.should_close or decision.should_close or pending_close:
@@ -800,6 +842,14 @@ class StrategyEngine:
self._set_state(phase="idle", last_error="以损定仓计算异常,暂不开仓")
return
# OKX:交易账户 USDC 不够开期权时,市价 USDT→USDC(目标=所需×2);够则跳过
try:
from .auto_usdc import ensure_okx_trading_usdc
ensure_okx_trading_usdc(self.db)
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: