Fix LIVE SoT P0/P1: closing state machine, OO exchange fills, BN balances.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+67
-21
@@ -26,7 +26,13 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# 禁止新开仓的本地仓位状态(实盘防卡)
|
||||
BLOCKING_STATUSES = frozenset(
|
||||
{"open", "half_open", "option_closed_perp_pending", "opening"}
|
||||
{
|
||||
"open",
|
||||
"half_open",
|
||||
"option_closed_perp_pending",
|
||||
"opening",
|
||||
"closing", # 期期盈利腿已卖、账本收尾中
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -624,7 +630,14 @@ class Matcher:
|
||||
)
|
||||
|
||||
def close_winning_oo_leave_residual(
|
||||
self, *, reason: str = "target_oo_win", skip_market: bool = False
|
||||
self,
|
||||
*,
|
||||
reason: str = "target_oo_win",
|
||||
skip_market: bool = False,
|
||||
live_fill_px: float | None = None,
|
||||
live_fill_fee: float | None = None,
|
||||
live_fill_contracts: float | None = None,
|
||||
live_win_leg: str | None = None,
|
||||
) -> CloseResult:
|
||||
"""期期达标:平盈利腿,亏损腿进 residual。skip_market=True 时假定已在交易所卖掉盈利腿。"""
|
||||
pos = self.current_position()
|
||||
@@ -649,6 +662,13 @@ class Matcher:
|
||||
upl = self.unrealized()
|
||||
call_upl = float(upl.get("option_upl") or 0)
|
||||
put_upl = float(upl.get("option2_upl") or 0)
|
||||
# LIVE 收尾可指定 win_leg(崩溃恢复时 UPL 可能已不可用)
|
||||
force_leg = (live_win_leg or "").strip()
|
||||
if force_leg in ("option", "option2"):
|
||||
if force_leg == "option":
|
||||
call_upl, put_upl = 1.0, 0.0
|
||||
else:
|
||||
call_upl, put_upl = 0.0, 1.0
|
||||
# 盈利腿:UPL 更高且 > 0
|
||||
if call_upl >= put_upl and call_upl > 0:
|
||||
win_leg, lose_leg = "option", "option2"
|
||||
@@ -681,16 +701,38 @@ class Matcher:
|
||||
return CloseResult(ok=False, detail="无明确盈利腿,暂不平")
|
||||
|
||||
fee_rate = self._fee_rate()
|
||||
if skip_market:
|
||||
oq = self._quote_held_option(win_id)
|
||||
fill_px = float(oq.bid) if oq and oq.bid else float(win_entry)
|
||||
of = option_fill(
|
||||
action="close",
|
||||
bid=fill_px,
|
||||
ask=fill_px,
|
||||
qty_eth=win_qty,
|
||||
fee_rate=fee_rate,
|
||||
exec_mode = "SIM" if get_settings().is_sim else "LIVE"
|
||||
from .pricing import PriceResult
|
||||
|
||||
if skip_market and live_fill_px is not None:
|
||||
fill_px = float(live_fill_px)
|
||||
fill_fee = float(live_fill_fee or 0)
|
||||
if live_fill_contracts is not None and float(live_fill_contracts) > 0:
|
||||
win_contracts = float(live_fill_contracts)
|
||||
win_qty = eth_from_contracts(win_contracts, self._ct_mult(win_id))
|
||||
of = PriceResult(
|
||||
base_px=fill_px,
|
||||
fill_px=fill_px,
|
||||
fee=fill_fee,
|
||||
slip=0.0,
|
||||
notional=fill_px * win_qty,
|
||||
)
|
||||
elif skip_market:
|
||||
# LIVE 未传真实成交:禁止用盘口发明价,仅允许 0 价零现金镜像
|
||||
if not get_settings().is_sim:
|
||||
of = PriceResult(
|
||||
base_px=0.0, fill_px=0.0, fee=0.0, slip=0.0, notional=0.0
|
||||
)
|
||||
else:
|
||||
oq = self._quote_held_option(win_id)
|
||||
fill_px = float(oq.bid) if oq and oq.bid else float(win_entry)
|
||||
of = option_fill(
|
||||
action="close",
|
||||
bid=fill_px,
|
||||
ask=fill_px,
|
||||
qty_eth=win_qty,
|
||||
fee_rate=fee_rate,
|
||||
)
|
||||
else:
|
||||
oq = self._quote_held_option(win_id)
|
||||
if oq is None or oq.bid is None or float(oq.bid) <= 0:
|
||||
@@ -715,21 +757,24 @@ class Matcher:
|
||||
fee_rate=fee_rate,
|
||||
)
|
||||
cash = of.notional - of.fee
|
||||
if get_settings().is_sim or not skip_market:
|
||||
self.ledger.apply_cash(
|
||||
cash, kind="close_option", group_id=group_id, note=f"oo win {win_leg}"
|
||||
)
|
||||
elif skip_market:
|
||||
# LIVE:交易所已成交,仍记本地账本现金(与其它 LIVE 平仓一致)
|
||||
apply_live_cash = bool(
|
||||
get_settings().is_sim
|
||||
or not skip_market
|
||||
or (live_fill_px is not None and abs(float(live_fill_px)) + abs(float(live_fill_fee or 0)) > 1e-12)
|
||||
)
|
||||
if apply_live_cash:
|
||||
try:
|
||||
self.ledger.apply_cash(
|
||||
cash,
|
||||
kind="close_option",
|
||||
group_id=group_id,
|
||||
note=f"oo win live {win_leg}",
|
||||
note=f"oo win {win_leg}",
|
||||
allow_negative=not get_settings().is_sim,
|
||||
)
|
||||
except Exception:
|
||||
logger.exception("oo win live ledger cash failed")
|
||||
if get_settings().is_sim:
|
||||
raise
|
||||
logger.exception("oo win ledger cash failed")
|
||||
|
||||
now = int(time.time() * 1000)
|
||||
expiry_ymd = None
|
||||
@@ -764,7 +809,7 @@ class Matcher:
|
||||
of.slip,
|
||||
of.notional,
|
||||
now,
|
||||
"SIM",
|
||||
exec_mode,
|
||||
),
|
||||
)
|
||||
self.db._conn.execute(
|
||||
@@ -836,7 +881,8 @@ class Matcher:
|
||||
) -> CloseResult:
|
||||
"""期期全平两腿(到期/紧急);无永续。"""
|
||||
pos = self.current_position()
|
||||
if str(pos.get("status") or "") != "open" or not pos.get("group_id"):
|
||||
st = str(pos.get("status") or "")
|
||||
if st not in ("open", "closing") or not pos.get("group_id"):
|
||||
return CloseResult(ok=False, detail="无期期持仓可平")
|
||||
if not (
|
||||
str(pos.get("hedge_mode") or "") == "option_option"
|
||||
|
||||
Reference in New Issue
Block a user