Fix OO expiry Put PnL: overlay wrong near-zero settlement fills with intrinsic.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+29
-16
@@ -82,10 +82,14 @@ def _infer_settle_index(g: dict, fills: list) -> float | None:
|
||||
return None
|
||||
|
||||
|
||||
def _overlay_expiry_zero_fills(
|
||||
def _overlay_expiry_intrinsic_fills(
|
||||
g: dict, fills: list, settle_index: float | None
|
||||
) -> list:
|
||||
"""到期 close 价为 0 且已有结算指数时,用内在价值覆盖展示(响应层,不写库)。"""
|
||||
"""到期且已有结算指数:close 成交按内在价值覆盖展示/盈亏(响应层)。
|
||||
|
||||
交易所账单偶发落成近 0 价(如 0.2),与内在价值(如 42.2)严重不符时
|
||||
若只覆盖 fill≈0,Put 仍会按错误价算成巨亏。
|
||||
"""
|
||||
if settle_index is None or settle_index <= 0:
|
||||
return fills
|
||||
if str(g.get("close_reason") or "") != "expiry":
|
||||
@@ -102,17 +106,21 @@ def _overlay_expiry_zero_fills(
|
||||
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))
|
||||
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 = float(
|
||||
_intrinsic(side, float(settle_index), float(strike))
|
||||
)
|
||||
# 与内在价值偏差超过 0.5 USDT(或相对 5%)则覆盖
|
||||
tol = max(0.5, abs(intrinsic) * 0.05)
|
||||
if abs(px - intrinsic) > tol:
|
||||
qty = float(f.get("qty_eth") or 0)
|
||||
f["fill_px"] = intrinsic
|
||||
f["base_px"] = intrinsic
|
||||
@@ -120,11 +128,16 @@ def _overlay_expiry_zero_fills(
|
||||
f["slip"] = 0.0
|
||||
f["_overlay_intrinsic"] = True
|
||||
changed = True
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
out.append(f)
|
||||
return out if changed else fills
|
||||
|
||||
|
||||
# 兼容旧测试名
|
||||
_overlay_expiry_zero_fills = _overlay_expiry_intrinsic_fills
|
||||
|
||||
|
||||
def _intrinsic(side: str, settle_index: float, strike: float) -> float:
|
||||
s = str(side or "").lower()
|
||||
if s in ("call", "c"):
|
||||
@@ -264,7 +277,7 @@ def _enrich_group(g: dict, fills: list) -> dict:
|
||||
is_oo = _is_oo_group(g)
|
||||
g["is_oo"] = is_oo
|
||||
settle = _infer_settle_index(g, fills)
|
||||
view_fills = _overlay_expiry_zero_fills(g, fills, settle)
|
||||
view_fills = _overlay_expiry_intrinsic_fills(g, fills, settle)
|
||||
overlaid = any(
|
||||
isinstance(f, dict) and f.get("_overlay_intrinsic") for f in view_fills
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user