fix期权复盘与数据统计双口径盈亏,并修复卖回封顶导致盈利单冻成亏损。
平仓卖光可卖币后再按账户差分结算复盘;数据统计改为历史币盈亏×卖回价;复盘展示初始权利金与盈利率,并附历史修复脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -51,55 +51,59 @@ class AccountPnlSettleTests(unittest.TestCase):
|
||||
self.assertAlmostEqual(float(row2["realized_pnl_usdt"]), 947.05, places=2)
|
||||
conn.close()
|
||||
|
||||
def test_stats_only_count_settled_account_trades(self):
|
||||
def test_stats_use_coin_times_sell_px_not_account(self):
|
||||
"""口径 B:有币盈亏即计入,U=币×卖回价;不读账户差分."""
|
||||
from lib.options.options_db import init_options_tables
|
||||
from lib.options.options_stats_lib import (
|
||||
compute_options_stats_from_history,
|
||||
history_pnl_to_usdt,
|
||||
history_pnl_to_usdt_stats,
|
||||
load_local_options_stats_history,
|
||||
)
|
||||
|
||||
conn = sqlite3.connect(":memory:")
|
||||
conn.row_factory = sqlite3.Row
|
||||
init_options_tables(conn)
|
||||
# 待结算:不进统计
|
||||
# 账户未结算但有币盈亏+卖回价 → 进统计 B
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO options_trades(
|
||||
inst_id, underlying, opt_type, sheets, eth_amount,
|
||||
premium_paid, premium_received, realized_pnl, premium_ccy,
|
||||
status, trading_usdt_before, pnl_settled, created_at, closed_at
|
||||
close_index_px, status, trading_usdt_before, pnl_settled,
|
||||
created_at, closed_at
|
||||
) VALUES (
|
||||
'ETH-USD-261009-2500-P', 'ETH', 'P', 1, 0.01,
|
||||
0.01, 0.02, 0.01, 'ETH',
|
||||
'closed', 500.0, 0, '2026-10-06 10:00:00', '2026-10-08 14:00:00'
|
||||
3000.0, 'closed', 500.0, 0,
|
||||
'2026-10-06 10:00:00', '2026-10-08 14:00:00'
|
||||
)
|
||||
"""
|
||||
)
|
||||
# 已结算
|
||||
# 账户冻结为负、币盈亏为正 → 统计仍用币×价
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO options_trades(
|
||||
inst_id, underlying, opt_type, sheets, eth_amount,
|
||||
premium_paid, premium_received, realized_pnl, premium_ccy,
|
||||
status, trading_usdt_before, trading_usdt_after,
|
||||
close_index_px, status, trading_usdt_before, trading_usdt_after,
|
||||
realized_pnl_usdt, pnl_settled, created_at, closed_at
|
||||
) VALUES (
|
||||
'ETH-USD-261009-2500-P', 'ETH', 'P', 1, 0.01,
|
||||
0.01, 0.02, 0.01, 'ETH',
|
||||
'closed', 1000.0, 1952.96,
|
||||
952.96, 1, '2026-10-01 10:00:00', '2026-10-02 14:00:00'
|
||||
'BTC-USD-261015-84000-P', 'BTC', 'P', 1, 0.01,
|
||||
0.01, 0.025, 0.015, 'BTC',
|
||||
100000.0, 'closed', 1000.0, 948.82,
|
||||
-51.18, 1, '2026-10-01 10:00:00', '2026-10-02 14:00:00'
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.commit()
|
||||
hist = load_local_options_stats_history(conn, None)
|
||||
stats = compute_options_stats_from_history(history_pnl_to_usdt(hist, None))
|
||||
self.assertEqual(int(stats["total_closed"]), 1)
|
||||
self.assertAlmostEqual(float(stats["net_realized_pnl"]), 952.96, places=2)
|
||||
stats = compute_options_stats_from_history(history_pnl_to_usdt_stats(hist, None))
|
||||
self.assertEqual(int(stats["total_closed"]), 2)
|
||||
# 0.01*3000 + 0.015*100000 = 30 + 1500 = 1530
|
||||
self.assertAlmostEqual(float(stats["net_realized_pnl"]), 1530.0, places=2)
|
||||
conn.close()
|
||||
|
||||
def test_header_stats_skip_unsettled_account_trades(self):
|
||||
def test_header_stats_count_coin_pnl_rows(self):
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from lib.options.options_db import init_options_tables
|
||||
@@ -112,11 +116,11 @@ class AccountPnlSettleTests(unittest.TestCase):
|
||||
"""
|
||||
INSERT INTO options_trades(
|
||||
inst_id, underlying, opt_type, sheets, eth_amount,
|
||||
premium_paid, realized_pnl, premium_ccy, status,
|
||||
premium_paid, realized_pnl, premium_ccy, close_index_px, status,
|
||||
trading_usdt_before, pnl_settled, created_at, closed_at
|
||||
) VALUES (
|
||||
'ETH-USD-261009-2500-P', 'ETH', 'P', 1, 0.01,
|
||||
0.01, 0.01, 'ETH', 'closed',
|
||||
0.01, 0.01, 'ETH', 3000.0, 'closed',
|
||||
500.0, 0, '2026-10-08 10:00:00', '2026-10-08 12:00:00'
|
||||
)
|
||||
"""
|
||||
@@ -125,12 +129,12 @@ class AccountPnlSettleTests(unittest.TestCase):
|
||||
"""
|
||||
INSERT INTO options_trades(
|
||||
inst_id, underlying, opt_type, sheets, eth_amount,
|
||||
premium_paid, realized_pnl, premium_ccy, status,
|
||||
premium_paid, realized_pnl, premium_ccy, close_index_px, status,
|
||||
trading_usdt_before, trading_usdt_after, realized_pnl_usdt,
|
||||
pnl_settled, created_at, closed_at
|
||||
) VALUES (
|
||||
'ETH-USD-261009-2500-P', 'ETH', 'P', 1, 0.01,
|
||||
0.01, 0.01, 'ETH', 'closed',
|
||||
0.01, 0.01, 'ETH', 3000.0, 'closed',
|
||||
1000.0, 1100.0, 100.0,
|
||||
1, '2026-10-08 08:00:00', '2026-10-08 09:00:00'
|
||||
)
|
||||
@@ -142,7 +146,50 @@ class AccountPnlSettleTests(unittest.TestCase):
|
||||
"end_utc": datetime(2026, 10, 31, 23, 59, 59, tzinfo=timezone.utc),
|
||||
}
|
||||
out = header_options_stats_for_window(conn, win, timezone.utc, None)
|
||||
self.assertEqual(int(out["total"]), 1)
|
||||
self.assertEqual(int(out["total"]), 2)
|
||||
conn.close()
|
||||
|
||||
def test_settle_blocked_when_sellable_coin(self):
|
||||
from lib.options.options_account_pnl_lib import try_settle_after_sellback
|
||||
from lib.options.options_db import init_options_tables
|
||||
|
||||
conn = sqlite3.connect(":memory:")
|
||||
conn.row_factory = sqlite3.Row
|
||||
init_options_tables(conn)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO options_trades(
|
||||
inst_id, underlying, opt_type, sheets, eth_amount,
|
||||
premium_paid, status, bridge_id, trading_usdt_before, pnl_settled,
|
||||
created_at, closed_at
|
||||
) VALUES (
|
||||
'BTC-USD-261015-84000-P', 'BTC', 'P', 1, 0.01,
|
||||
0.01, 'closed', 7, 1000.0, 0,
|
||||
'2026-10-12 01:46:50', '2026-10-13 08:07:35'
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
class _Ex:
|
||||
pass
|
||||
|
||||
# monkey: 仍有可卖 BTC
|
||||
import lib.options.options_account_pnl_lib as mod
|
||||
|
||||
old_block = mod.sellable_coin_blocks_settle
|
||||
mod.sellable_coin_blocks_settle = lambda ex, uly: "仍有可卖 BTC"
|
||||
try:
|
||||
n = try_settle_after_sellback(
|
||||
conn, _Ex(), bridge_id=7, sell_complete=True
|
||||
)
|
||||
self.assertEqual(n, 0)
|
||||
row = conn.execute(
|
||||
"SELECT pnl_settled FROM options_trades WHERE id=1"
|
||||
).fetchone()
|
||||
self.assertEqual(int(row["pnl_settled"]), 0)
|
||||
finally:
|
||||
mod.sellable_coin_blocks_settle = old_block
|
||||
conn.close()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user