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:
dekun
2026-08-06 09:04:30 +08:00
parent 25ed46e3f2
commit f19500bcd9
4 changed files with 189 additions and 22 deletions
+98 -3
View File
@@ -45,7 +45,7 @@ def test_format_option_history_row():
assert row["status_label"] == "已平"
assert row["open_avg_px_fmt"] == "380"
assert row["premium_paid_fmt"] == "3.80"
assert row["history_key"] == "ex:pos-btc"
assert row["history_key"] == "ex:pos-btc:1784088035000"
def test_resolve_option_close_from_history_picks_latest():
@@ -59,6 +59,40 @@ def test_resolve_option_close_from_history_picks_latest():
assert got["pos_id"] == "9"
def test_resolve_option_close_from_history_matches_open_and_sheets():
rows = [
{
"instId": "ETH-USD_UM-260806-1875-C",
"cTime": "1785932775047",
"uTime": "1785933936733",
"realizedPnl": "-3.036",
"closeAvgPx": "12.4",
"closeTotalPos": "57",
"posId": "same-pos",
},
{
"instId": "ETH-USD_UM-260806-1875-C",
"cTime": "1785938392445",
"uTime": "1785957764279",
"realizedPnl": "16.937",
"closeAvgPx": "41.0",
"closeTotalPos": "66",
"posId": "same-pos",
},
]
# 本地时间相对交易所偏 8h 时,仍应按 cTime/张数对齐到正确一笔
early = resolve_option_close_from_history(
rows, open_ms=1785902775000, close_ms=1785903937000, sheets=57
)
late = resolve_option_close_from_history(
rows, open_ms=1785908392000, close_ms=1785927764000, sheets=66
)
assert early is not None and early["realized_pnl"] == -3.036
assert early["close_quote"] == 12.4
assert late is not None and late["realized_pnl"] == 16.937
assert late["close_quote"] == 41.0
def test_sync_open_options_trades_marks_expired_closed():
conn = sqlite3.connect(":memory:")
conn.row_factory = sqlite3.Row
@@ -96,9 +130,9 @@ def test_sync_open_options_trades_skips_without_close_evidence():
INSERT INTO options_trades
(inst_id, underlying, opt_type, strike, exp_time, sheets, eth_amount,
open_quote, premium_paid, status, created_at)
VALUES (?, 'BTC', 'P', 62000, '', 1, 0.01, 380.0, 3.8, 'open', '2026-07-09 08:00:00')
VALUES (?, 'BTC', 'P', 62000, '', 1, 0.01, 380.0, 3.8, 'open', '2026-08-05 08:00:00')
""",
("BTC-USD_UM-260710-62000-P",),
("BTC-USD_UM-261231-62000-P",),
)
conn.commit()
@@ -213,3 +247,64 @@ def test_backfill_closed_options_realized_pnl_from_history():
assert float(row["close_quote"]) == 48.5
# idempotent
assert backfill_closed_options_realized_pnl_from_history(conn, hist) == 0
def test_backfill_does_not_overwrite_earlier_close_with_later_pnl():
from lib.options.options_monitor_lib import backfill_closed_options_realized_pnl_from_history
conn = sqlite3.connect(":memory:")
conn.row_factory = sqlite3.Row
init_options_tables(conn)
conn.execute(
"""
INSERT INTO options_trades
(inst_id, underlying, opt_type, strike, exp_time, sheets, eth_amount,
open_quote, close_quote, premium_paid, realized_pnl, status, created_at, closed_at)
VALUES (?, 'ETH', 'C', 1875, '', 57, 0.57, 16.6, 41.0, 9.462, 16.9368,
'closed', '2026-08-05 12:26:15', '2026-08-05 12:45:37')
""",
("ETH-USD_UM-260806-1875-C",),
)
conn.execute(
"""
INSERT INTO options_trades
(inst_id, underlying, opt_type, strike, exp_time, sheets, eth_amount,
open_quote, close_quote, premium_paid, realized_pnl, status, created_at, closed_at)
VALUES (?, 'ETH', 'C', 1875, '', 66, 0.66, 14.2, 41.0, 9.372, 16.9368,
'closed', '2026-08-05 13:59:52', '2026-08-05 19:22:45')
""",
("ETH-USD_UM-260806-1875-C",),
)
conn.commit()
hist = [
{
"instId": "ETH-USD_UM-260806-1875-C",
"uTime": "1785933936733",
"cTime": "1785932775047",
"realizedPnl": "-3.03616314",
"closeAvgPx": "12.4",
"closeTotalPos": "57",
"posId": "3806091806281486337",
},
{
"instId": "ETH-USD_UM-260806-1875-C",
"uTime": "1785957764279",
"cTime": "1785938392445",
"realizedPnl": "16.9368375",
"closeAvgPx": "41.0",
"closeTotalPos": "66",
"posId": "3806091806281486337",
},
]
n = backfill_closed_options_realized_pnl_from_history(conn, hist)
assert n >= 1
rows = {
int(r["id"]): r
for r in conn.execute(
"SELECT id, realized_pnl, close_quote FROM options_trades ORDER BY id"
).fetchall()
}
assert abs(float(rows[1]["realized_pnl"]) - (-3.0362)) < 1e-3
assert float(rows[1]["close_quote"]) == 12.4
assert abs(float(rows[2]["realized_pnl"]) - 16.9368) < 1e-3
assert float(rows[2]["close_quote"]) == 41.0