修复期权分批平仓只记最后一笔收回导致盈亏漏算。
每次部分成交累加权利金与张数,全平后再合计 realized_pnl,并优先匹配交易所全平历史。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -306,7 +306,8 @@ def close_option_by_bid1(
|
||||
|
||||
px = float(order.get("px", locked_bid_px))
|
||||
oid = str((order.get("data") or {}).get("ordId") or "")
|
||||
prem_recv = round(total_premium(px, level_sheets * ct_mult), 4)
|
||||
# 本批预估收回(按挂单张数);成交后按实际减仓张数重算并入累计
|
||||
prem_recv_submit = round(total_premium(px, level_sheets * ct_mult), 4)
|
||||
time.sleep(0.6)
|
||||
invalidate_option_positions_cache()
|
||||
raw2 = cfg["fetch_option_positions"](ex)
|
||||
@@ -325,14 +326,47 @@ def close_option_by_bid1(
|
||||
reduced = max(0, before_avail - after_avail)
|
||||
remaining_pos = after_avail
|
||||
fully_closed = remaining_pos < 1
|
||||
filled_sheets = min(reduced, level_sheets) if reduced > 0 else 0
|
||||
# 全平但减仓读数异常时回退挂单张数,避免漏记本批
|
||||
if fully_closed and filled_sheets < 1:
|
||||
filled_sheets = level_sheets
|
||||
batch_recv = (
|
||||
round(total_premium(px, filled_sheets * ct_mult), 4) if filled_sheets > 0 else 0.0
|
||||
)
|
||||
|
||||
total_recv = batch_recv
|
||||
close_px = px
|
||||
merged_ord = oid or None
|
||||
|
||||
if fully_closed:
|
||||
clear_close_gate(inst_id)
|
||||
conn = cfg["get_db"]()
|
||||
try:
|
||||
from lib.options.options_db import init_options_tables
|
||||
from lib.options.options_db import (
|
||||
add_open_close_accum,
|
||||
get_open_close_accum,
|
||||
init_options_tables,
|
||||
)
|
||||
|
||||
init_options_tables(conn)
|
||||
# 先并入本批,再读累计(含此前分批)
|
||||
if filled_sheets > 0:
|
||||
add_open_close_accum(
|
||||
conn,
|
||||
inst_id,
|
||||
recv=batch_recv,
|
||||
sheets=filled_sheets,
|
||||
px=px,
|
||||
ord_id=oid or None,
|
||||
)
|
||||
accum = get_open_close_accum(conn, inst_id)
|
||||
total_recv = round(float(accum.get("recv") or 0), 4)
|
||||
accum_sheets = int(accum.get("sheets") or 0)
|
||||
accum_notional = float(accum.get("notional") or 0)
|
||||
if accum_sheets > 0 and accum_notional > 0:
|
||||
close_px = round(accum_notional / accum_sheets, 8)
|
||||
merged_ord = str(accum.get("close_ord_id") or "").strip() or (oid or None)
|
||||
|
||||
open_rows = conn.execute(
|
||||
"""
|
||||
SELECT id, premium_paid FROM options_trades
|
||||
@@ -346,19 +380,25 @@ def close_option_by_bid1(
|
||||
for i, row in enumerate(open_rows):
|
||||
paid = float(row["premium_paid"] or 0)
|
||||
if i == len(open_rows) - 1:
|
||||
recv = round(prem_recv - allocated, 4)
|
||||
recv = round(total_recv - allocated, 4)
|
||||
elif total_paid > 0:
|
||||
recv = round(prem_recv * (paid / total_paid), 4)
|
||||
recv = round(total_recv * (paid / total_paid), 4)
|
||||
allocated += recv
|
||||
else:
|
||||
recv = round(prem_recv / len(open_rows), 4)
|
||||
recv = round(total_recv / len(open_rows), 4)
|
||||
allocated += recv
|
||||
pnl = round(recv - paid, 4)
|
||||
note_sql = ""
|
||||
from lib.options.options_time_lib import options_app_now_str
|
||||
|
||||
# 占位顺序必须与 SQL 一致: close_quote/recv/pnl/ord/closed_at/[signal_note]/id
|
||||
params: list[Any] = [px, recv, pnl, oid or None, options_app_now_str()]
|
||||
params: list[Any] = [
|
||||
close_px,
|
||||
recv,
|
||||
pnl,
|
||||
merged_ord,
|
||||
options_app_now_str(),
|
||||
]
|
||||
if signal_note and i == len(open_rows) - 1:
|
||||
note_sql = """,
|
||||
signal_note = CASE
|
||||
@@ -371,7 +411,9 @@ def close_option_by_bid1(
|
||||
f"""
|
||||
UPDATE options_trades
|
||||
SET status = 'closed', close_quote = ?, premium_received = ?,
|
||||
realized_pnl = ?, close_ord_id = ?, closed_at = ?
|
||||
realized_pnl = ?, close_ord_id = ?, closed_at = ?,
|
||||
close_recv_accum = 0, close_sheets_accum = 0,
|
||||
close_notional_accum = 0
|
||||
{note_sql}
|
||||
WHERE id = ?
|
||||
""",
|
||||
@@ -380,6 +422,27 @@ def close_option_by_bid1(
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
elif filled_sheets > 0:
|
||||
# 部分成交:持久化本批收回,下次全平合计
|
||||
conn = cfg["get_db"]()
|
||||
try:
|
||||
from lib.options.options_db import add_open_close_accum, init_options_tables
|
||||
|
||||
init_options_tables(conn)
|
||||
add_open_close_accum(
|
||||
conn,
|
||||
inst_id,
|
||||
recv=batch_recv,
|
||||
sheets=filled_sheets,
|
||||
px=px,
|
||||
ord_id=oid or None,
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
if require_recycle_gate:
|
||||
# 自动平已挂过单:同仓续批只验流动性
|
||||
mark_close_gate_passed(inst_id)
|
||||
elif require_recycle_gate:
|
||||
# 自动平已挂过单:同仓续批只验流动性
|
||||
mark_close_gate_passed(inst_id)
|
||||
@@ -391,11 +454,12 @@ def close_option_by_bid1(
|
||||
"bid": px,
|
||||
"locked_bid_px": locked_bid_px,
|
||||
"submitted_sheets": level_sheets,
|
||||
"filled_or_reduced_sheets": min(reduced, level_sheets) if reduced else 0,
|
||||
"filled_or_reduced_sheets": filled_sheets,
|
||||
"remaining_sheets": remaining_pos,
|
||||
"premium_received": prem_recv,
|
||||
"premium_received": total_recv if fully_closed else (batch_recv or prem_recv_submit),
|
||||
"batch_premium_received": batch_recv,
|
||||
"stopped_reason": None if fully_closed else ("partial_bid1" if reduced > 0 else "order_not_filled"),
|
||||
"close_ord_id": oid or None,
|
||||
"close_ord_id": merged_ord if fully_closed else (oid or None),
|
||||
"fully_closed": fully_closed,
|
||||
"msg": (
|
||||
f"已按买一 {locked_bid_px:g} 提交 {level_sheets} 张"
|
||||
|
||||
Reference in New Issue
Block a user