fix期权复盘与数据统计双口径盈亏,并修复卖回封顶导致盈利单冻成亏损。

平仓卖光可卖币后再按账户差分结算复盘;数据统计改为历史币盈亏×卖回价;复盘展示初始权利金与盈利率,并附历史修复脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-13 09:10:13 +08:00
parent d4de6a23ae
commit 9d77a430e5
16 changed files with 2858 additions and 2242 deletions
+96 -7
View File
@@ -92,24 +92,107 @@ def transfer_blocked_by_position_msg(conn, ex: Any = None) -> str | None:
return None
def sellable_coin_blocks_settle(
ex: Any,
underlying: str | None,
) -> str | None:
"""仍有可卖标的币则禁止账户差分结算(规格:卖光再冻结)."""
from lib.options.options_spot_bridge_lib import (
fetch_trading_coin_available,
is_sellable_spot_coin,
)
uly = (underlying or "").strip().upper()
if uly not in ("ETH", "BTC"):
return None
try:
avail = fetch_trading_coin_available(ex, uly)
except Exception:
return None
if avail is None:
return None
if is_sellable_spot_coin(ex, uly, avail):
return f"仍有可卖 {uly}({float(avail):g}),暂不结算账户盈亏"
return None
def _underlying_for_settle(
conn,
*,
bridge_id: int | None = None,
trade_id: int | None = None,
) -> str | None:
try:
if trade_id is not None:
row = conn.execute(
"SELECT underlying FROM options_trades WHERE id=?",
(int(trade_id),),
).fetchone()
if row:
return str(row["underlying"] if hasattr(row, "keys") else row[0] or "").strip().upper() or None
if bridge_id is not None:
row = conn.execute(
"""
SELECT underlying FROM options_trades
WHERE bridge_id=? AND status='closed'
ORDER BY id DESC LIMIT 1
""",
(int(bridge_id),),
).fetchone()
if row:
return str(row["underlying"] if hasattr(row, "keys") else row[0] or "").strip().upper() or None
brow = conn.execute(
"SELECT underlying FROM options_spot_bridge WHERE id=?",
(int(bridge_id),),
).fetchone()
if brow:
return str(brow["underlying"] if hasattr(brow, "keys") else brow[0] or "").strip().upper() or None
except Exception:
return None
return None
def try_settle_after_sellback(
get_db: Any,
ex: Any,
*,
bridge_id: int | None = None,
cfg: dict[str, Any] | None = None,
underlying: str | None = None,
sell_complete: bool | None = None,
) -> int:
"""卖回路径通用结算:读交易账户 after 并冻结未结算单."""
pack = cfg if isinstance(cfg, dict) else {"fetch_options_balances": None}
after = fetch_trading_usdt(pack, ex)
if after is None:
"""卖回路径通用结算:读交易账户 after 并冻结未结算单.
门禁:sell_complete 显式 False 时不结算;仍有可卖标的币时不结算.
"""
if sell_complete is False:
return 0
conn = get_db() if callable(get_db) else get_db
close_conn = callable(get_db)
pack = cfg if isinstance(cfg, dict) else {"fetch_options_balances": None}
import sqlite3
# Windows 上 sqlite3.Connection 的 callable() 可能为 True,不能据此判断 get_db
if isinstance(get_db, sqlite3.Connection):
conn = get_db
close_conn = False
elif callable(get_db):
conn = get_db()
close_conn = True
else:
conn = get_db
close_conn = False
try:
from lib.options.options_db import init_options_tables
init_options_tables(conn)
uly = (underlying or "").strip().upper() or None
if not uly:
uly = _underlying_for_settle(conn, bridge_id=bridge_id)
block = sellable_coin_blocks_settle(ex, uly)
if block:
return 0
after = fetch_trading_usdt(pack, ex)
if after is None:
return 0
n = 0
if bridge_id is not None:
n = settle_closed_trades_from_trading_account(
@@ -125,10 +208,16 @@ def try_settle_after_sellback(
"""
).fetchone()
if row:
tid = int(row["id"] if hasattr(row, "keys") else row[0])
if not uly:
uly = _underlying_for_settle(conn, trade_id=tid)
block2 = sellable_coin_blocks_settle(ex, uly)
if block2:
return 0
n = settle_closed_trades_from_trading_account(
conn,
trading_usdt_after=after,
trade_id=int(row["id"] if hasattr(row, "keys") else row[0]),
trade_id=tid,
)
try:
conn.commit()