Fix LIVE open/close double-book and security audit findings.
Prevent expiry dual-close from re-booking option cash, abandon ledger rejection, margin-mode mismatch, and manual close races; harden fill wait and refuse default AUTH_SECRET on LIVE. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -47,6 +47,18 @@ class OkxLiveExecutor(Matcher):
|
||||
).strip().lower()
|
||||
return "isolated" if raw == "isolated" else "cross"
|
||||
|
||||
def _perp_margin_mode_for_group(self, group_id: str | None) -> str:
|
||||
"""平仓用开仓时写入的保证金模式;缺省回退当前设置。"""
|
||||
if group_id:
|
||||
g = self.db.fetchone(
|
||||
"SELECT perp_margin_mode FROM groups WHERE group_id=?", (group_id,)
|
||||
)
|
||||
if g is not None:
|
||||
m = str(g["perp_margin_mode"] or "").strip().lower()
|
||||
if m in ("cross", "isolated"):
|
||||
return m
|
||||
return self._perp_margin_mode()
|
||||
|
||||
def _guard_live(self) -> str | None:
|
||||
ok, reason = live_ready()
|
||||
if not ok:
|
||||
@@ -130,6 +142,13 @@ class OkxLiveExecutor(Matcher):
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception("live open option failed")
|
||||
msg = str(e)
|
||||
# 已拿到 ordId:可能已成交,禁止释放 opening 以免重复开仓
|
||||
if "ordId=" in msg:
|
||||
return OpenResult(
|
||||
ok=False,
|
||||
detail=f"实盘开期权未确认成交(保留 opening 防重复开,请核对交易所): {e}",
|
||||
)
|
||||
release_open_slot_if_opening(self.db)
|
||||
return OpenResult(ok=False, detail=f"实盘开期权失败: {e}")
|
||||
|
||||
@@ -141,6 +160,7 @@ class OkxLiveExecutor(Matcher):
|
||||
opt_qty = eth_from_contracts(opt_contracts, ct_mult)
|
||||
|
||||
# 永续市价:按产品假设,失败原因实质为保证金不足 → 必须回滚期权
|
||||
mgn = self._perp_margin_mode()
|
||||
try:
|
||||
ct_val = client.get_ct_val(perp_inst, inst_type="SWAP")
|
||||
perp_sz = perp_close_contracts_okx(
|
||||
@@ -155,7 +175,6 @@ class OkxLiveExecutor(Matcher):
|
||||
else:
|
||||
side, pos_side = "sell", "short"
|
||||
leverage = self.ledger.get_setting_float("leverage", s.leverage)
|
||||
mgn = self._perp_margin_mode()
|
||||
try:
|
||||
client.set_leverage(
|
||||
perp_inst, leverage, mgn_mode=mgn, pos_side=pos_side
|
||||
@@ -239,8 +258,8 @@ class OkxLiveExecutor(Matcher):
|
||||
"""INSERT INTO groups(
|
||||
group_id, status, bias, option_side, perp_side, option_inst_id, perp_inst_id,
|
||||
strike, expiry_ymd, entry_index_px, initial_premium, open_at_ms, fees, slip_cost,
|
||||
exec_mode
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
exec_mode, perp_margin_mode
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
(
|
||||
group_id,
|
||||
"open",
|
||||
@@ -257,6 +276,7 @@ class OkxLiveExecutor(Matcher):
|
||||
of_fee + pf_fee,
|
||||
0.0,
|
||||
"LIVE",
|
||||
mgn,
|
||||
),
|
||||
)
|
||||
self.db._conn.execute(
|
||||
@@ -653,7 +673,7 @@ class OkxLiveExecutor(Matcher):
|
||||
inst_id=perp_inst,
|
||||
side=side,
|
||||
sz=str(perp_sz),
|
||||
td_mode=self._perp_margin_mode(),
|
||||
td_mode=self._perp_margin_mode_for_group(group_id),
|
||||
pos_side=pos_side,
|
||||
reduce_only=True,
|
||||
)
|
||||
@@ -665,6 +685,8 @@ class OkxLiveExecutor(Matcher):
|
||||
detail=f"期权已平,永续待平(option_closed_perp_pending): {e}",
|
||||
)
|
||||
|
||||
# 期权已在 _mark_option_closed_perp_pending 入账/写 fill(含到期本地结算),
|
||||
# 此处 pending_perp_only 必为 True;勿再按 is_expiry 二次入账。
|
||||
return self._finalize_dual_close(
|
||||
pos=pos,
|
||||
group_id=group_id,
|
||||
@@ -678,14 +700,8 @@ class OkxLiveExecutor(Matcher):
|
||||
pf_px=pf_px,
|
||||
pf_fee=pf_fee,
|
||||
reason=reason,
|
||||
option_fill_already_written=(
|
||||
st == "option_closed_perp_pending"
|
||||
or (pending_perp_only and not is_expiry)
|
||||
),
|
||||
skip_option_cash=(
|
||||
st == "option_closed_perp_pending"
|
||||
or (pending_perp_only and not is_expiry)
|
||||
),
|
||||
option_fill_already_written=bool(pending_perp_only),
|
||||
skip_option_cash=bool(pending_perp_only),
|
||||
)
|
||||
|
||||
def _mark_option_closed_perp_pending(
|
||||
@@ -938,7 +954,7 @@ class OkxLiveExecutor(Matcher):
|
||||
inst_id=perp_inst,
|
||||
side=side,
|
||||
sz=str(perp_sz),
|
||||
td_mode=self._perp_margin_mode(),
|
||||
td_mode=self._perp_margin_mode_for_group(group_id),
|
||||
pos_side=pos_side,
|
||||
reduce_only=True,
|
||||
)
|
||||
@@ -957,6 +973,7 @@ class OkxLiveExecutor(Matcher):
|
||||
kind="close_perp",
|
||||
group_id=group_id,
|
||||
note=f"LIVE close perp abandon option {reason}",
|
||||
allow_negative=True,
|
||||
)
|
||||
|
||||
# 复用父类归档写入:临时改 fill 路径太重,直接调用父类会再平一次本地假价。
|
||||
|
||||
Reference in New Issue
Block a user