修复期权分批平仓只记最后一笔收回导致盈亏漏算。
每次部分成交累加权利金与张数,全平后再合计 realized_pnl,并优先匹配交易所全平历史。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -105,6 +105,10 @@ def init_options_tables(conn: sqlite3.Connection) -> None:
|
||||
"ALTER TABLE options_trades ADD COLUMN premium_ccy TEXT DEFAULT 'USDC'",
|
||||
"ALTER TABLE options_trades ADD COLUMN bridge_id INTEGER",
|
||||
"ALTER TABLE options_trades ADD COLUMN budget_usdt REAL",
|
||||
# 分批平仓累计:全平前写在最早一笔 open 上,全平后并入 premium_received
|
||||
"ALTER TABLE options_trades ADD COLUMN close_recv_accum REAL DEFAULT 0",
|
||||
"ALTER TABLE options_trades ADD COLUMN close_sheets_accum INTEGER DEFAULT 0",
|
||||
"ALTER TABLE options_trades ADD COLUMN close_notional_accum REAL DEFAULT 0",
|
||||
):
|
||||
try:
|
||||
conn.execute(ddl)
|
||||
@@ -154,3 +158,86 @@ def sum_open_sheets(conn: sqlite3.Connection, inst_id: str) -> int | None:
|
||||
if not row or int(row["n"] or 0) < 1:
|
||||
return None
|
||||
return int(row["total"] or 0)
|
||||
|
||||
|
||||
def _earliest_open_trade_id(conn: sqlite3.Connection, inst_id: str) -> int | None:
|
||||
inst = (inst_id or "").strip()
|
||||
if not inst:
|
||||
return None
|
||||
row = conn.execute(
|
||||
"""
|
||||
SELECT id FROM options_trades
|
||||
WHERE inst_id = ? AND status = 'open'
|
||||
ORDER BY id ASC
|
||||
LIMIT 1
|
||||
""",
|
||||
(inst,),
|
||||
).fetchone()
|
||||
return int(row["id"]) if row else None
|
||||
|
||||
|
||||
def get_open_close_accum(conn: sqlite3.Connection, inst_id: str) -> dict[str, float | int | str | None]:
|
||||
"""读取分批平仓累计(挂在最早一笔 open 上)."""
|
||||
tid = _earliest_open_trade_id(conn, inst_id)
|
||||
if tid is None:
|
||||
return {"recv": 0.0, "sheets": 0, "notional": 0.0, "close_ord_id": None}
|
||||
row = conn.execute(
|
||||
"""
|
||||
SELECT close_recv_accum, close_sheets_accum, close_notional_accum, close_ord_id
|
||||
FROM options_trades WHERE id = ?
|
||||
""",
|
||||
(tid,),
|
||||
).fetchone()
|
||||
if not row:
|
||||
return {"recv": 0.0, "sheets": 0, "notional": 0.0, "close_ord_id": None}
|
||||
return {
|
||||
"recv": float(row["close_recv_accum"] or 0),
|
||||
"sheets": int(row["close_sheets_accum"] or 0),
|
||||
"notional": float(row["close_notional_accum"] or 0),
|
||||
"close_ord_id": str(row["close_ord_id"] or "").strip() or None,
|
||||
}
|
||||
|
||||
|
||||
def add_open_close_accum(
|
||||
conn: sqlite3.Connection,
|
||||
inst_id: str,
|
||||
*,
|
||||
recv: float,
|
||||
sheets: int,
|
||||
px: float,
|
||||
ord_id: str | None = None,
|
||||
) -> bool:
|
||||
"""分批平仓成交后累加收回权利金/张数/名义(供全平 VWAP 与总盈亏)."""
|
||||
tid = _earliest_open_trade_id(conn, inst_id)
|
||||
if tid is None:
|
||||
return False
|
||||
sheets_i = max(0, int(sheets or 0))
|
||||
recv_f = float(recv or 0)
|
||||
px_f = float(px or 0)
|
||||
if sheets_i < 1 and abs(recv_f) < 1e-15:
|
||||
return False
|
||||
notional_add = px_f * sheets_i if sheets_i > 0 and px_f > 0 else 0.0
|
||||
oid = str(ord_id or "").strip()
|
||||
prev = conn.execute(
|
||||
"SELECT close_ord_id FROM options_trades WHERE id = ?", (tid,)
|
||||
).fetchone()
|
||||
prev_oids = str((prev["close_ord_id"] if prev else None) or "").strip()
|
||||
if oid:
|
||||
parts = [p for p in prev_oids.split(",") if p] if prev_oids else []
|
||||
if oid not in parts:
|
||||
parts.append(oid)
|
||||
merged_oid = ",".join(parts)
|
||||
else:
|
||||
merged_oid = prev_oids or None
|
||||
conn.execute(
|
||||
"""
|
||||
UPDATE options_trades
|
||||
SET close_recv_accum = COALESCE(close_recv_accum, 0) + ?,
|
||||
close_sheets_accum = COALESCE(close_sheets_accum, 0) + ?,
|
||||
close_notional_accum = COALESCE(close_notional_accum, 0) + ?,
|
||||
close_ord_id = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(recv_f, sheets_i, notional_add, merged_oid, tid),
|
||||
)
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user