"""交易账户快照盈亏结算.""" from __future__ import annotations import sqlite3 import unittest class AccountPnlSettleTests(unittest.TestCase): def test_settle_freezes_premium_usdt_move_not_account_diff(self): from lib.options.options_account_pnl_lib import ( settle_closed_trades_from_trading_account, ) from lib.options.options_db import init_options_tables conn = sqlite3.connect(":memory:") conn.row_factory = sqlite3.Row init_options_tables(conn) # coin_pnl=0.01, close=80000 → U=800; 账户差分不采用 conn.execute( """ INSERT INTO options_trades( inst_id, underlying, opt_type, sheets, eth_amount, premium_paid, premium_received, realized_pnl, premium_ccy, open_index_px, close_index_px, status, bridge_id, trading_usdt_before, pnl_settled, created_at, closed_at ) VALUES ( 'BTC-USD-261008-86500-P', 'BTC', 'P', 1, 0.01, 0.01, 0.02, 0.01, 'BTC', 90000.0, 80000.0, 'closed', 7, 1000.0, 0, '2026-10-05 05:11:30', '2026-10-07 10:03:53' ) """ ) conn.commit() n = settle_closed_trades_from_trading_account( conn, trading_usdt_after=1947.05, bridge_id=7 ) self.assertEqual(n, 1) row = conn.execute( "SELECT trading_usdt_after, realized_pnl_usdt, pnl_settled FROM options_trades WHERE id=1" ).fetchone() self.assertAlmostEqual(float(row["trading_usdt_after"]), 1947.05, places=2) self.assertAlmostEqual(float(row["realized_pnl_usdt"]), 800.0, places=2) self.assertEqual(int(row["pnl_settled"]), 1) # 已冻结不再改 n2 = settle_closed_trades_from_trading_account( conn, trading_usdt_after=9999.0, bridge_id=7 ) self.assertEqual(n2, 0) row2 = conn.execute( "SELECT realized_pnl_usdt FROM options_trades WHERE id=1" ).fetchone() self.assertAlmostEqual(float(row2["realized_pnl_usdt"]), 800.0, places=2) conn.close() 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_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, 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', 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, close_index_px, status, trading_usdt_before, trading_usdt_after, realized_pnl_usdt, pnl_settled, created_at, closed_at ) VALUES ( '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_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_count_coin_pnl_rows(self): from datetime import datetime, timezone from lib.options.options_db import init_options_tables from lib.options.options_stats_lib import header_options_stats_for_window 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, 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', 3000.0, 'closed', 500.0, 0, '2026-10-08 10:00:00', '2026-10-08 12:00:00' ) """ ) conn.execute( """ INSERT INTO options_trades( inst_id, underlying, opt_type, sheets, eth_amount, 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', 3000.0, 'closed', 1000.0, 1100.0, 100.0, 1, '2026-10-08 08:00:00', '2026-10-08 09:00:00' ) """ ) conn.commit() win = { "start_utc": datetime(2026, 10, 1, tzinfo=timezone.utc), "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"]), 2) conn.close() def test_true_pnl_premium_move_helper(self): from lib.options.options_pnl_settle_lib import ( resolve_true_pnl_usdt_from_trade, true_pnl_usdt_premium_move, ) # 0.01*80000 = 800 self.assertAlmostEqual( float( true_pnl_usdt_premium_move( coin_pnl=0.01, sell_back_px=80000, ) ), 800.0, places=4, ) pnl, src = resolve_true_pnl_usdt_from_trade( premium_paid=0.01, premium_received=0.02, realized_pnl=0.01, open_index_px=90000, close_index_px=80000, premium_ccy="BTC", ) self.assertEqual(src, "premium_move") self.assertAlmostEqual(float(pnl), 800.0, places=4) 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() if __name__ == "__main__": unittest.main()