Backfill OO expiry settle display via public ETHUSDT and intrinsic overlay.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+86
-14
@@ -25,7 +25,7 @@ def _is_oo_group(g: dict) -> bool:
|
||||
|
||||
|
||||
def _infer_settle_index(g: dict, fills: list) -> float | None:
|
||||
"""优先库内 settle_index_px;否则用「实值腿」成交反推。虚值 fill≈0 时禁止推成行权价。"""
|
||||
"""优先库内 settle_index_px;否则用「实值腿」成交反推;再否则公开指数近似。"""
|
||||
settle_index = g.get("settle_index_px")
|
||||
if settle_index is not None:
|
||||
try:
|
||||
@@ -68,10 +68,62 @@ def _infer_settle_index(g: dict, fills: list) -> float | None:
|
||||
candidates.append(k + px)
|
||||
elif side in ("put", "p"):
|
||||
candidates.append(k - px)
|
||||
if not candidates:
|
||||
return None
|
||||
# 多腿一致时取平均;实值腿通常只有一条
|
||||
return round(sum(candidates) / len(candidates), 4)
|
||||
if candidates:
|
||||
return round(sum(candidates) / len(candidates), 4)
|
||||
|
||||
try:
|
||||
from .public_index import maybe_public_settle_index
|
||||
|
||||
pub = maybe_public_settle_index(g)
|
||||
if pub is not None and pub > 0:
|
||||
return round(float(pub), 4)
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def _overlay_expiry_zero_fills(
|
||||
g: dict, fills: list, settle_index: float | None
|
||||
) -> list:
|
||||
"""到期 close 价为 0 且已有结算指数时,用内在价值覆盖展示(响应层,不写库)。"""
|
||||
if settle_index is None or settle_index <= 0:
|
||||
return fills
|
||||
if str(g.get("close_reason") or "") != "expiry":
|
||||
return fills
|
||||
out: list = []
|
||||
changed = False
|
||||
for raw in fills:
|
||||
f = dict(raw) if not isinstance(raw, dict) else dict(raw)
|
||||
if str(f.get("action") or "") == "close" and str(f.get("leg") or "") in (
|
||||
"option",
|
||||
"option2",
|
||||
):
|
||||
try:
|
||||
px = float(f.get("fill_px") or 0)
|
||||
except (TypeError, ValueError):
|
||||
px = 0.0
|
||||
if px <= 1e-9:
|
||||
leg = str(f.get("leg") or "")
|
||||
if leg == "option":
|
||||
strike = g.get("strike")
|
||||
side = str(g.get("option_side") or "").lower()
|
||||
else:
|
||||
strike = g.get("strike2")
|
||||
side = str(g.get("option2_side") or "put").lower()
|
||||
if strike is not None:
|
||||
try:
|
||||
intrinsic = _intrinsic(side, float(settle_index), float(strike))
|
||||
qty = float(f.get("qty_eth") or 0)
|
||||
f["fill_px"] = intrinsic
|
||||
f["base_px"] = intrinsic
|
||||
f["notional"] = intrinsic * qty
|
||||
f["slip"] = 0.0
|
||||
f["_overlay_intrinsic"] = True
|
||||
changed = True
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
out.append(f)
|
||||
return out if changed else fills
|
||||
|
||||
def _intrinsic(side: str, settle_index: float, strike: float) -> float:
|
||||
s = str(side or "").lower()
|
||||
@@ -211,15 +263,26 @@ def _option_leverage_for_leg(
|
||||
def _enrich_group(g: dict, fills: list) -> dict:
|
||||
is_oo = _is_oo_group(g)
|
||||
g["is_oo"] = is_oo
|
||||
summary = summarize_fills_pnl(fills)
|
||||
# LIVE:优先 groups.realized_pnl(已按交易所回写,含资金费)
|
||||
if str(g.get("exec_mode") or "").upper() == "LIVE" and g.get("realized_pnl") is not None:
|
||||
settle = _infer_settle_index(g, fills)
|
||||
view_fills = _overlay_expiry_zero_fills(g, fills, settle)
|
||||
overlaid = any(
|
||||
isinstance(f, dict) and f.get("_overlay_intrinsic") for f in view_fills
|
||||
)
|
||||
summary = summarize_fills_pnl(view_fills)
|
||||
# LIVE 且未做内在价值覆盖:优先 groups.realized_pnl(含资金费)
|
||||
if (
|
||||
not overlaid
|
||||
and str(g.get("exec_mode") or "").upper() == "LIVE"
|
||||
and g.get("realized_pnl") is not None
|
||||
):
|
||||
summary = dict(summary)
|
||||
summary["net_pnl"] = float(g["realized_pnl"])
|
||||
if g.get("funding_usdt") is not None:
|
||||
summary["funding_usdt"] = float(g["funding_usdt"])
|
||||
summary["pnl_source"] = "live_exchange"
|
||||
# 期期 SIM:若成交汇总缺腿但组上已有 realized_pnl,用组值兜底
|
||||
elif overlaid:
|
||||
summary = dict(summary)
|
||||
summary["pnl_source"] = "expiry_intrinsic_overlay"
|
||||
elif (
|
||||
is_oo
|
||||
and g.get("realized_pnl") is not None
|
||||
@@ -240,17 +303,20 @@ def _enrich_group(g: dict, fills: list) -> dict:
|
||||
prem2 = float(g.get("initial_premium2") or 0) if is_oo else 0.0
|
||||
g["total_initial_premium"] = prem1 + prem2 if is_oo else prem1
|
||||
g.update(hold_timing(g, fills))
|
||||
info = _expiry_settle_info(g, fills)
|
||||
if settle is not None and g.get("settle_index_px") is None:
|
||||
g["settle_index_px"] = float(settle)
|
||||
info = _expiry_settle_info(g, view_fills)
|
||||
if info:
|
||||
g["expiry_settle"] = info
|
||||
if g.get("settle_index_px") is None and info.get("settle_index_px") is not None:
|
||||
g["settle_index_px"] = info["settle_index_px"]
|
||||
mp = _move_points(g, fills)
|
||||
mp = _move_points(g, view_fills)
|
||||
g["move_points"] = mp
|
||||
g["close_index_px"] = _close_index_px(g, fills)
|
||||
g["close_index_px"] = _close_index_px(g, view_fills)
|
||||
g["option_leverage"] = _option_leverage_for_leg(g, fills, leg="option")
|
||||
if is_oo:
|
||||
g["option2_leverage"] = _option_leverage_for_leg(g, fills, leg="option2")
|
||||
g["_view_fills"] = view_fills
|
||||
return g
|
||||
|
||||
|
||||
@@ -265,7 +331,9 @@ async def list_groups(_user: Annotated[str, Depends(require_user)]) -> dict:
|
||||
"SELECT * FROM fills WHERE group_id=? ORDER BY id ASC",
|
||||
(g["group_id"],),
|
||||
)
|
||||
groups.append(_enrich_group(g, fills))
|
||||
gr = _enrich_group(g, fills)
|
||||
gr.pop("_view_fills", None)
|
||||
groups.append(gr)
|
||||
return {"groups": groups}
|
||||
|
||||
|
||||
@@ -281,9 +349,13 @@ async def group_detail(
|
||||
"SELECT * FROM fills WHERE group_id=? ORDER BY id ASC", (group_id,)
|
||||
)
|
||||
gr = _enrich_group(_row(g), fills)
|
||||
view_fills = gr.pop("_view_fills", None) or fills
|
||||
return {
|
||||
"group": gr,
|
||||
"fills": [_row(x) for x in fills],
|
||||
"fills": [
|
||||
{k: v for k, v in (dict(x) if not isinstance(x, dict) else x).items() if k != "_overlay_intrinsic"}
|
||||
for x in view_fills
|
||||
],
|
||||
"pnl_summary": gr.get("pnl_summary"),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user