Harden LIVE opens with slot claim and exchange reconcile.

Prevent duplicate opens by atomically claiming an opening slot, verifying exchange perp is flat before live orders, setting leverage from ledger, and preferring exchange position size when closing perps.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-26 22:39:27 +08:00
parent f48ea5bbcc
commit 0cf3756b09
10 changed files with 390 additions and 21 deletions
+45 -10
View File
@@ -14,6 +14,12 @@ from ..sim.matcher import CloseResult, Matcher, OpenResult
from ..sim.pricing import option_expiry_settle, option_intrinsic
from ..strategy.session import get_session
from .okx_trade import OkxTradeClient
from .reconcile import (
assert_safe_to_open_live,
claim_open_slot,
perp_close_contracts_okx,
release_open_slot_if_opening,
)
from .symbols import live_settings, resolve_perp_inst_id
logger = logging.getLogger(__name__)
@@ -87,14 +93,16 @@ class OkxLiveExecutor(Matcher):
if err:
return OpenResult(ok=False, detail=err)
s = live_settings()
if self.has_open_position():
st = self.position_status()
return OpenResult(
ok=False,
detail=f"已有持仓/半仓状态({st}),请先修复或平仓",
)
claimed, claim_msg = claim_open_slot(self.db)
if not claimed:
return OpenResult(ok=False, detail=claim_msg)
safe, safe_msg = assert_safe_to_open_live(self)
if not safe:
release_open_slot_if_opening(self.db)
return OpenResult(ok=False, detail=safe_msg)
s = live_settings()
client = self._client()
perp_inst = resolve_perp_inst_id(self.db)
perp_qty = self.ledger.get_setting_float("perp_qty_eth", s.perp_qty_eth)
@@ -112,6 +120,7 @@ class OkxLiveExecutor(Matcher):
)
except Exception as e:
logger.exception("live open option failed")
release_open_slot_if_opening(self.db)
return OpenResult(ok=False, detail=f"实盘开期权失败: {e}")
# 以交易所实际成交张数回写名义
@@ -124,11 +133,24 @@ class OkxLiveExecutor(Matcher):
# 永续市价:按产品假设,失败原因实质为保证金不足 → 必须回滚期权
try:
ct_val = client.get_ct_val(perp_inst, inst_type="SWAP")
perp_sz = max(1, int(round(perp_qty / ct_val)))
perp_sz = perp_close_contracts_okx(
client,
perp_inst=perp_inst,
perp_side=perp_side,
perp_qty_eth=perp_qty,
ct_val=ct_val,
)
if perp_side == "long":
side, pos_side = "buy", "long"
else:
side, pos_side = "sell", "short"
leverage = self.ledger.get_setting_float("leverage", s.leverage)
try:
client.set_leverage(
perp_inst, leverage, mgn_mode="cross", pos_side=pos_side
)
except Exception as e_lev:
logger.warning("okx set_leverage failed: %s", e_lev)
perp_fill_live = client.place_market(
inst_id=perp_inst,
side=side,
@@ -168,6 +190,7 @@ class OkxLiveExecutor(Matcher):
group_id=group_id,
detail=f"永续开仓失败(保证金)且期权回滚失败,已标记 half_open: {e} / {e2}",
)
release_open_slot_if_opening(self.db)
return OpenResult(
ok=False,
detail=f"永续开仓失败(多为保证金不足),已回滚期权: {e}",
@@ -604,7 +627,13 @@ class OkxLiveExecutor(Matcher):
try:
ct_val = client.get_ct_val(perp_inst, inst_type="SWAP")
perp_sz = max(1, int(round(perp_qty / ct_val)))
perp_sz = perp_close_contracts_okx(
client,
perp_inst=perp_inst,
perp_side=perp_side,
perp_qty_eth=perp_qty,
ct_val=ct_val,
)
if perp_side == "long":
side, pos_side = "sell", "long"
else:
@@ -883,7 +912,13 @@ class OkxLiveExecutor(Matcher):
client = self._client()
try:
ct_val = client.get_ct_val(perp_inst, inst_type="SWAP")
perp_sz = max(1, int(round(perp_qty / ct_val)))
perp_sz = perp_close_contracts_okx(
client,
perp_inst=perp_inst,
perp_side=perp_side,
perp_qty_eth=perp_qty,
ct_val=ct_val,
)
if perp_side == "long":
side, pos_side = "sell", "long"
else: