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
+36 -9
View File
@@ -12,6 +12,12 @@ from ..sim.matcher import CloseResult, Matcher, OpenResult
from ..sim.pricing import option_expiry_settle, option_intrinsic
from ..strategy.session import get_session
from .binance_trade import BinanceTradeClient
from .reconcile import (
assert_safe_to_open_live,
claim_open_slot,
perp_close_qty_eth_binance,
release_open_slot_if_opening,
)
from .symbols import live_settings, resolve_perp_inst_id
logger = logging.getLogger(__name__)
@@ -83,14 +89,16 @@ class BinanceLiveExecutor(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)
@@ -106,6 +114,7 @@ class BinanceLiveExecutor(Matcher):
)
except Exception as e:
logger.exception("binance live open option failed")
release_open_slot_if_opening(self.db)
return OpenResult(ok=False, detail=f"币安开期权失败: {e}")
filled_opt_contracts = float(opt_fill.sz) if opt_fill.sz and opt_fill.sz > 0 else float(
@@ -120,6 +129,11 @@ class BinanceLiveExecutor(Matcher):
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)
except Exception as e_lev:
logger.warning("binance set_leverage failed: %s", e_lev)
perp_fill_live = client.place_perp_market(
symbol=perp_inst,
side=side,
@@ -157,6 +171,7 @@ class BinanceLiveExecutor(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}",
@@ -598,10 +613,16 @@ class BinanceLiveExecutor(Matcher):
side, pos_side = "SELL", "LONG"
else:
side, pos_side = "BUY", "SHORT"
perp_qty_close = perp_close_qty_eth_binance(
client,
perp_inst=perp_inst,
perp_side=perp_side,
perp_qty_eth=perp_qty,
)
perp_live = client.place_perp_market(
symbol=perp_inst,
side=side,
qty_eth=perp_qty,
qty_eth=perp_qty_close,
position_side=pos_side,
reduce_only=True,
)
@@ -901,10 +922,16 @@ class BinanceLiveExecutor(Matcher):
side, pos_side = "SELL", "LONG"
else:
side, pos_side = "BUY", "SHORT"
perp_qty_close = perp_close_qty_eth_binance(
client,
perp_inst=perp_inst,
perp_side=perp_side,
perp_qty_eth=perp_qty,
)
perp_live = client.place_perp_market(
symbol=perp_inst,
side=side,
qty_eth=perp_qty,
qty_eth=perp_qty_close,
position_side=pos_side,
reduce_only=True,
)