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:
@@ -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,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user