Fix ghost finalize on unknown perp size and open rollback hazards.
Distinguish exchange query None from flat zero; keep opening when size unknown; idempotent half_open cash; scan options while opening; continue manage after recover. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -19,6 +19,7 @@ from .reconcile import (
|
||||
claim_open_slot,
|
||||
exchange_option_abs_size,
|
||||
perp_close_contracts_okx,
|
||||
perp_open_contracts_okx,
|
||||
recover_stuck_opening,
|
||||
release_open_slot_if_opening,
|
||||
stamp_opening_intent,
|
||||
@@ -189,13 +190,7 @@ class OkxLiveExecutor(Matcher):
|
||||
mgn = self._perp_margin_mode()
|
||||
try:
|
||||
ct_val = client.get_ct_val(perp_inst, inst_type="SWAP")
|
||||
perp_sz = perp_close_contracts_okx(
|
||||
client,
|
||||
perp_inst=perp_inst,
|
||||
perp_side=perp_side,
|
||||
perp_qty_eth=perp_qty,
|
||||
ct_val=ct_val,
|
||||
)
|
||||
perp_sz = perp_open_contracts_okx(perp_qty_eth=perp_qty, ct_val=ct_val)
|
||||
if perp_side == "long":
|
||||
side, pos_side = "buy", "long"
|
||||
else:
|
||||
@@ -216,18 +211,18 @@ class OkxLiveExecutor(Matcher):
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception("live open perp failed (likely margin); rollback option")
|
||||
# 永续可能已成交:先查仓,有仓则不得回滚期权
|
||||
# 永续可能已成交:先查仓;查失败或有仓均不得回滚期权
|
||||
try:
|
||||
live_perp = client.get_perp_pos_sz(
|
||||
perp_inst, pos_side=("long" if perp_side == "long" else "short")
|
||||
)
|
||||
except Exception:
|
||||
live_perp = None
|
||||
if live_perp is not None and live_perp > 1e-8:
|
||||
if live_perp is None or live_perp > 1e-8:
|
||||
return OpenResult(
|
||||
ok=False,
|
||||
detail=(
|
||||
f"永续可能已成交但未确认成交明细(保留 opening): {e}; "
|
||||
f"永续开仓未确认(保留 opening,禁止回滚期权): {e}; "
|
||||
f"ex_perp={live_perp}"
|
||||
),
|
||||
)
|
||||
@@ -419,13 +414,19 @@ class OkxLiveExecutor(Matcher):
|
||||
"""期权已成交、永续未开且回滚失败 → 落 half_open,禁止新开,待 repair。"""
|
||||
perp_inst = resolve_perp_inst_id(self.db, group_id=group_id)
|
||||
initial_premium = of_px * opt_qty
|
||||
self.ledger.apply_cash(
|
||||
-(of_px * opt_qty + of_fee),
|
||||
kind="open_option",
|
||||
group_id=group_id,
|
||||
note=f"LIVE half_open option {group_id}",
|
||||
allow_negative=True,
|
||||
# 幂等:崩溃重入时勿二次扣权利金
|
||||
prior_cash = self.db.fetchone(
|
||||
"SELECT id FROM ledger_entries WHERE group_id=? AND kind='open_option' LIMIT 1",
|
||||
(group_id,),
|
||||
)
|
||||
if prior_cash is None:
|
||||
self.ledger.apply_cash(
|
||||
-(of_px * opt_qty + of_fee),
|
||||
kind="open_option",
|
||||
group_id=group_id,
|
||||
note=f"LIVE half_open option {group_id}",
|
||||
allow_negative=True,
|
||||
)
|
||||
now = int(time.time() * 1000)
|
||||
with self.db._lock:
|
||||
existing = self.db._conn.execute(
|
||||
@@ -629,6 +630,19 @@ class OkxLiveExecutor(Matcher):
|
||||
pf_px = entry_index if entry_index > 0 else of_px
|
||||
initial_premium = of_px * opt_qty
|
||||
mgn = self._perp_margin_mode()
|
||||
# 幂等补记权利金(崩溃在成交后、账本前时)
|
||||
prior_cash = self.db.fetchone(
|
||||
"SELECT id FROM ledger_entries WHERE group_id=? AND kind='open_option' LIMIT 1",
|
||||
(group_id,),
|
||||
)
|
||||
if prior_cash is None and of_px > 0 and opt_qty > 0:
|
||||
self.ledger.apply_cash(
|
||||
-(of_px * opt_qty),
|
||||
kind="open_option",
|
||||
group_id=group_id,
|
||||
note=f"LIVE recover promote open_option {group_id}",
|
||||
allow_negative=True,
|
||||
)
|
||||
now = int(time.time() * 1000)
|
||||
with self.db._lock:
|
||||
existing = self.db._conn.execute(
|
||||
@@ -857,6 +871,11 @@ class OkxLiveExecutor(Matcher):
|
||||
ct_val=ct_val,
|
||||
allow_db_fallback=not pending_perp_only,
|
||||
)
|
||||
if perp_sz is None:
|
||||
return CloseResult(
|
||||
ok=False,
|
||||
detail="期权已平,永续待平(无法核对交易所仓位,禁止空仓 finalize)",
|
||||
)
|
||||
if perp_sz <= 0:
|
||||
# 永续已在交易所平掉:用入场价近似 finalize(净盈亏由对账校正)
|
||||
pf_px = float(pos.get("perp_entry_px") or 0) or 0.0
|
||||
@@ -1152,6 +1171,11 @@ class OkxLiveExecutor(Matcher):
|
||||
perp_qty_eth=perp_qty,
|
||||
ct_val=ct_val,
|
||||
)
|
||||
if perp_sz is None or perp_sz <= 0:
|
||||
return CloseResult(
|
||||
ok=False,
|
||||
detail="弃期权平永续失败: 无法取得有效永续仓位数量",
|
||||
)
|
||||
if perp_side == "long":
|
||||
side, pos_side = "sell", "long"
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user