Mirror option expiry settlements from exchange bills; debit OO LIVE open premiums.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -829,6 +829,22 @@ class BinanceLiveExecutor(Matcher):
|
||||
put_qty = eth_from_contracts(put_contracts, put_ct)
|
||||
call_prem = of_px * call_qty
|
||||
put_prem = pf_px * put_qty
|
||||
call_fee = float(getattr(call_fill, "fee", 0) or 0)
|
||||
put_fee = float(getattr(put_fill, "fee", 0) or 0)
|
||||
self.ledger.apply_cash(
|
||||
-(call_prem + call_fee),
|
||||
kind="open_option",
|
||||
group_id=group_id,
|
||||
note=f"LIVE-BN oo open call {group_id}",
|
||||
allow_negative=True,
|
||||
)
|
||||
self.ledger.apply_cash(
|
||||
-(put_prem + put_fee),
|
||||
kind="open_option",
|
||||
group_id=group_id,
|
||||
note=f"LIVE-BN oo open put {group_id}",
|
||||
allow_negative=True,
|
||||
)
|
||||
now = int(time.time() * 1000)
|
||||
with self.db._lock:
|
||||
self.db._conn.execute(
|
||||
@@ -850,8 +866,7 @@ class BinanceLiveExecutor(Matcher):
|
||||
entry_index_px,
|
||||
call_prem,
|
||||
now,
|
||||
float(getattr(call_fill, "fee", 0) or 0)
|
||||
+ float(getattr(put_fill, "fee", 0) or 0),
|
||||
call_fee + put_fee,
|
||||
0.0,
|
||||
"LIVE",
|
||||
"option_option",
|
||||
@@ -1023,6 +1038,20 @@ class BinanceLiveExecutor(Matcher):
|
||||
for i, (leg, inst, qty, contracts) in enumerate(legs):
|
||||
if not inst:
|
||||
continue
|
||||
if reason == "expiry":
|
||||
px, fee, notional, cash = self._live_option_settlement_fill(
|
||||
option_inst_id=inst, qty_eth=qty, group_id=group_id
|
||||
)
|
||||
else:
|
||||
px, fee, notional, cash = 0.0, 0.0, 0.0, 0.0
|
||||
if abs(cash) > 1e-12:
|
||||
self.ledger.apply_cash(
|
||||
cash,
|
||||
kind="close_option",
|
||||
group_id=group_id,
|
||||
note=f"LIVE-BN oo settle {leg} {reason}",
|
||||
allow_negative=True,
|
||||
)
|
||||
with self.db._lock:
|
||||
self.db._conn.execute(
|
||||
"""INSERT INTO fills(group_id, leg, action, side, inst_id, qty_eth, qty_contracts,
|
||||
@@ -1036,11 +1065,11 @@ class BinanceLiveExecutor(Matcher):
|
||||
inst,
|
||||
qty,
|
||||
contracts,
|
||||
px,
|
||||
px,
|
||||
fee,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
notional,
|
||||
now + i,
|
||||
"LIVE",
|
||||
),
|
||||
@@ -1062,7 +1091,7 @@ class BinanceLiveExecutor(Matcher):
|
||||
int(time.time() * 1000),
|
||||
reason,
|
||||
net,
|
||||
f"oo full close {reason} exchange_flat_mirror",
|
||||
f"oo full close {reason} exchange_flat_or_settle",
|
||||
group_id,
|
||||
),
|
||||
)
|
||||
@@ -1296,13 +1325,17 @@ class BinanceLiveExecutor(Matcher):
|
||||
ex_opt,
|
||||
group_id,
|
||||
)
|
||||
of_px = 0.0
|
||||
of_fee = 0.0
|
||||
of_notional = 0.0
|
||||
of_px, of_fee, of_notional, settle_cash = self._live_option_settlement_fill(
|
||||
option_inst_id=option_inst_id,
|
||||
qty_eth=opt_qty,
|
||||
group_id=group_id,
|
||||
)
|
||||
of_slip = 0.0
|
||||
option_apply_cash = False
|
||||
option_apply_cash = abs(settle_cash) > 1e-12
|
||||
logger.info(
|
||||
"bn expiry: skip option order, close perp only group=%s", group_id
|
||||
"bn expiry: skip option order, close perp only group=%s settle_cash=%.4f",
|
||||
group_id,
|
||||
settle_cash,
|
||||
)
|
||||
self._ensure_option_closed_perp_pending(
|
||||
group_id=group_id,
|
||||
@@ -1314,7 +1347,7 @@ class BinanceLiveExecutor(Matcher):
|
||||
of_notional=of_notional,
|
||||
of_slip=of_slip,
|
||||
reason=reason,
|
||||
apply_cash=False,
|
||||
apply_cash=option_apply_cash,
|
||||
)
|
||||
pending_perp_only = True
|
||||
else:
|
||||
@@ -1474,6 +1507,44 @@ class BinanceLiveExecutor(Matcher):
|
||||
settle_index_px=spot,
|
||||
)
|
||||
|
||||
def _live_option_settlement_fill(
|
||||
self,
|
||||
*,
|
||||
option_inst_id: str,
|
||||
qty_eth: float,
|
||||
group_id: str | None = None,
|
||||
begin_ms: int | None = None,
|
||||
) -> tuple[float, float, float, float]:
|
||||
from .option_settle import fetch_option_settlement, settlement_to_fill
|
||||
|
||||
open_ms = begin_ms
|
||||
if open_ms is None and group_id:
|
||||
g = self.db.fetchone(
|
||||
"SELECT open_at_ms FROM groups WHERE group_id=?", (group_id,)
|
||||
)
|
||||
if g and g["open_at_ms"]:
|
||||
open_ms = int(g["open_at_ms"])
|
||||
st = fetch_option_settlement(
|
||||
self._client(),
|
||||
exchange="binance",
|
||||
option_inst_id=option_inst_id,
|
||||
qty_eth=float(qty_eth),
|
||||
begin_ms=open_ms,
|
||||
)
|
||||
px, fee, notional = settlement_to_fill(st, qty_eth=float(qty_eth))
|
||||
cash = float(st.cash) if st.found else 0.0
|
||||
if st.found:
|
||||
logger.info(
|
||||
"bn option settlement %s source=%s px=%.6f fee=%.6f cash=%.6f (%s)",
|
||||
option_inst_id,
|
||||
st.source,
|
||||
px,
|
||||
fee,
|
||||
cash,
|
||||
st.detail,
|
||||
)
|
||||
return px, fee, notional, cash
|
||||
|
||||
def _ensure_option_closed_perp_pending(
|
||||
self,
|
||||
*,
|
||||
@@ -1772,16 +1843,36 @@ class BinanceLiveExecutor(Matcher):
|
||||
)
|
||||
if ex_sz <= 1e-8:
|
||||
now_ms = int(time.time() * 1000)
|
||||
qty_eth = float(row.get("option_qty_eth") or 0)
|
||||
begin = None
|
||||
try:
|
||||
begin = int(row.get("created_at_ms") or 0) or None
|
||||
except Exception:
|
||||
begin = None
|
||||
px, fee, notional, _cash = self._live_option_settlement_fill(
|
||||
option_inst_id=option_inst_id,
|
||||
qty_eth=qty_eth,
|
||||
group_id=group_id,
|
||||
begin_ms=begin,
|
||||
)
|
||||
booked = self._book_residual_market_close(
|
||||
row,
|
||||
fill_px=0.0,
|
||||
fee=0.0,
|
||||
notional=0.0,
|
||||
fill_px=px,
|
||||
fee=fee,
|
||||
notional=notional,
|
||||
slip=0.0,
|
||||
now_ms=now_ms,
|
||||
note="LIVE-BN residual already flat on exchange",
|
||||
note=(
|
||||
f"LIVE-BN residual already flat; settlement px={px}"
|
||||
if notional > 0 or fee > 0
|
||||
else "LIVE-BN residual already flat on exchange"
|
||||
),
|
||||
exec_mode="LIVE",
|
||||
filled_contracts=0.0,
|
||||
filled_contracts=(
|
||||
float(row.get("option_qty_contracts") or 0)
|
||||
if (notional > 0 or fee > 0)
|
||||
else 0.0
|
||||
),
|
||||
remaining_contracts=0.0,
|
||||
close_reason="residual_premium_close",
|
||||
)
|
||||
@@ -1927,14 +2018,31 @@ class BinanceLiveExecutor(Matcher):
|
||||
)
|
||||
return None
|
||||
if ex_sz <= 1e-8:
|
||||
qty_eth = float(row.get("option_qty_eth") or 0)
|
||||
begin = None
|
||||
try:
|
||||
begin = int(row.get("created_at_ms") or 0) or None
|
||||
except Exception:
|
||||
begin = None
|
||||
gid = str(row.get("group_id") or "")
|
||||
px, fee, notional, _cash = self._live_option_settlement_fill(
|
||||
option_inst_id=option_inst_id,
|
||||
qty_eth=qty_eth,
|
||||
group_id=gid or None,
|
||||
begin_ms=begin,
|
||||
)
|
||||
return {
|
||||
"fill_px": 0.0,
|
||||
"fee": 0.0,
|
||||
"notional": 0.0,
|
||||
"fill_px": px,
|
||||
"fee": fee,
|
||||
"notional": notional,
|
||||
"slip": 0.0,
|
||||
"filled_contracts": 0.0,
|
||||
"filled_contracts": float(row.get("option_qty_contracts") or 0),
|
||||
"remaining_contracts": 0.0,
|
||||
"note": "LIVE-BN residual flat on exchange before settle",
|
||||
"note": (
|
||||
f"LIVE-BN residual flat; settlement px={px}"
|
||||
if notional > 0 or fee > 0
|
||||
else "LIVE-BN residual flat on exchange before settle"
|
||||
),
|
||||
"exec_mode": "LIVE",
|
||||
"close_reason": "emergency" if force else "expiry",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user