Fix options PnL backfill matching when the same contract is traded twice.
Match exchange history by sheets and open time so an earlier close is not overwritten with the later trade's PnL. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -142,11 +142,13 @@ def _created_at_ms(created_at: Any) -> int | None:
|
||||
|
||||
def _group_key_for_closed_trade(row: Any) -> str:
|
||||
inst = str(row["inst_id"] or "").strip()
|
||||
ord_id = str(row["close_ord_id"] or "").strip() if "close_ord_id" in row.keys() else ""
|
||||
if ord_id:
|
||||
return f"{inst}|ord:{ord_id}"
|
||||
closed = str(row["closed_at"] or "").strip()
|
||||
return f"{inst}|close:{(closed[:16] if closed else '')}"
|
||||
close_prefix = closed[:16] if closed else ""
|
||||
ord_id = str(row["close_ord_id"] or "").strip() if "close_ord_id" in row.keys() else ""
|
||||
# 即使 close_ord_id/posId 相同,也要按平仓时间拆开(OKX 可能复用 posId)
|
||||
if ord_id:
|
||||
return f"{inst}|ord:{ord_id}|close:{close_prefix}"
|
||||
return f"{inst}|close:{close_prefix}"
|
||||
|
||||
|
||||
def backfill_closed_options_realized_pnl_from_history(
|
||||
@@ -170,7 +172,8 @@ def backfill_closed_options_realized_pnl_from_history(
|
||||
|
||||
rows = conn.execute(
|
||||
"""
|
||||
SELECT id, inst_id, sheets, premium_paid, realized_pnl, created_at, closed_at, close_ord_id
|
||||
SELECT id, inst_id, sheets, premium_paid, realized_pnl, close_quote,
|
||||
created_at, closed_at, close_ord_id
|
||||
FROM options_trades
|
||||
WHERE status = 'closed'
|
||||
ORDER BY id DESC
|
||||
@@ -193,13 +196,26 @@ def backfill_closed_options_realized_pnl_from_history(
|
||||
inst = str(group[0]["inst_id"] or "").strip()
|
||||
open_candidates = [_created_at_ms(r["created_at"]) for r in group]
|
||||
open_ms = min((x for x in open_candidates if x is not None), default=None)
|
||||
close_info = resolve_option_close_from_history(by_inst.get(inst) or [], open_ms=open_ms)
|
||||
close_candidates = [_created_at_ms(r["closed_at"]) for r in group]
|
||||
close_ms = max((x for x in close_candidates if x is not None), default=None)
|
||||
sheets_hint = None
|
||||
try:
|
||||
sheets_hint = sum(float(_safe_float(r["sheets"]) or 0.0) for r in group) or None
|
||||
except (TypeError, ValueError):
|
||||
sheets_hint = None
|
||||
close_info = resolve_option_close_from_history(
|
||||
by_inst.get(inst) or [],
|
||||
open_ms=open_ms,
|
||||
close_ms=close_ms,
|
||||
sheets=sheets_hint,
|
||||
)
|
||||
if not close_info:
|
||||
continue
|
||||
ex_pnl = _safe_float(close_info.get("realized_pnl"))
|
||||
if ex_pnl is None:
|
||||
continue
|
||||
close_quote = _safe_float(close_info.get("close_quote"))
|
||||
matched_pos = str(close_info.get("pos_id") or "").strip() or None
|
||||
total_paid = 0.0
|
||||
for r in group:
|
||||
total_paid += float(_safe_float(r["premium_paid"]) or 0.0)
|
||||
@@ -215,7 +231,14 @@ def backfill_closed_options_realized_pnl_from_history(
|
||||
share = round(float(ex_pnl) / len(group), 4)
|
||||
allocated += share
|
||||
local = _safe_float(r["realized_pnl"])
|
||||
if local is not None and abs(local - share) < 1e-6:
|
||||
local_close = _safe_float(r["close_quote"])
|
||||
local_ord = str(r["close_ord_id"] or "").strip()
|
||||
pnl_ok = local is not None and abs(local - share) < 1e-6
|
||||
quote_ok = close_quote is None or (
|
||||
local_close is not None and abs(local_close - float(close_quote)) < 1e-6
|
||||
)
|
||||
ord_ok = (not matched_pos) or (local_ord == matched_pos)
|
||||
if pnl_ok and quote_ok and ord_ok:
|
||||
continue
|
||||
prem_recv = round(paid + share, 4)
|
||||
conn.execute(
|
||||
@@ -223,10 +246,11 @@ def backfill_closed_options_realized_pnl_from_history(
|
||||
UPDATE options_trades
|
||||
SET realized_pnl = ?,
|
||||
premium_received = ?,
|
||||
close_quote = COALESCE(?, close_quote)
|
||||
close_quote = COALESCE(?, close_quote),
|
||||
close_ord_id = COALESCE(?, close_ord_id)
|
||||
WHERE id = ?
|
||||
""",
|
||||
(share, prem_recv, close_quote, int(r["id"])),
|
||||
(share, prem_recv, close_quote, matched_pos, int(r["id"])),
|
||||
)
|
||||
updated += 1
|
||||
return updated
|
||||
|
||||
Reference in New Issue
Block a user