dd979d9dd0
Co-authored-by: Cursor <cursoragent@cursor.com>
95 lines
3.3 KiB
Python
95 lines
3.3 KiB
Python
"""模拟盘到期结算:币本位盈亏须用币,不能把 U 实值减权利金币."""
|
|
from __future__ import annotations
|
|
|
|
import sqlite3
|
|
import unittest
|
|
|
|
from lib.options.options_db import init_options_tables
|
|
from lib.sim.broker_lib import SimBroker
|
|
from lib.sim.db_lib import init_sim_tables
|
|
|
|
|
|
class SimExpiryCoinPnlTests(unittest.TestCase):
|
|
def test_coin_expiry_pnl_not_usd_minus_coin(self):
|
|
conn = sqlite3.connect(":memory:")
|
|
conn.row_factory = sqlite3.Row
|
|
init_options_tables(conn)
|
|
init_sim_tables(conn)
|
|
# 251001 = 2025-10-01 08:00 UTC 已过期
|
|
inst = "BTC-USD-251001-100000-P"
|
|
prem = 0.001
|
|
sheets = 1.0
|
|
ct_mult = 0.01
|
|
spot = 90000.0
|
|
conn.execute(
|
|
"""
|
|
INSERT INTO sim_option_positions(
|
|
inst_id, side, sheets, entry_px, ct_mult, premium_paid_usdc, updated_at
|
|
) VALUES (?,?,?,?,?,?,?)
|
|
""",
|
|
(inst, "buy", sheets, 0.1, ct_mult, prem, "2025-09-01 10:00:00"),
|
|
)
|
|
conn.execute(
|
|
"""
|
|
INSERT INTO options_trades(
|
|
inst_id, underlying, opt_type, strike, sheets, eth_amount,
|
|
premium_paid, status, premium_ccy, open_index_px, open_fee,
|
|
created_at
|
|
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
|
|
""",
|
|
(
|
|
inst,
|
|
"BTC",
|
|
"P",
|
|
100000,
|
|
int(sheets),
|
|
sheets * ct_mult,
|
|
prem,
|
|
"open",
|
|
"BTC",
|
|
95000.0,
|
|
0.0,
|
|
"2025-09-01 10:00:00",
|
|
),
|
|
)
|
|
conn.commit()
|
|
|
|
class _Proxy:
|
|
def __getattr__(self, name):
|
|
return getattr(conn, name)
|
|
|
|
def close(self):
|
|
return None
|
|
|
|
broker = SimBroker(lambda: _Proxy()) # type: ignore[arg-type]
|
|
broker._index_px_for_option = lambda *_a, **_k: spot # type: ignore[method-assign]
|
|
# 跳过卖回(本测只核币盈亏)
|
|
import lib.options.options_spot_bridge_lib as bridge
|
|
|
|
bridge.sell_residual_after_option_flat = lambda *_a, **_k: {"ok": True, "skipped": True}
|
|
|
|
# 2025-10-02 UTC
|
|
now_ms = 1759363200000
|
|
out = broker.settle_expired_option_positions(None, now_ms=now_ms)
|
|
self.assertEqual(len(out), 1)
|
|
|
|
intrinsic_u = 100000.0 - spot # 10000
|
|
settle_recv = (intrinsic_u / spot) * sheets * ct_mult
|
|
expected_pnl = settle_recv - prem
|
|
# 旧 bug: option_expiry_pnl = intrinsic_u*sheets*ct_mult - prem ≈ 99.999
|
|
wrong_old = intrinsic_u * sheets * ct_mult - prem
|
|
self.assertGreater(abs(wrong_old - expected_pnl), 1.0)
|
|
|
|
row = conn.execute(
|
|
"SELECT premium_received, realized_pnl, close_index_px, status FROM options_trades WHERE id=1"
|
|
).fetchone()
|
|
self.assertEqual(str(row["status"]), "closed")
|
|
self.assertAlmostEqual(float(row["premium_received"]), settle_recv, places=8)
|
|
self.assertAlmostEqual(float(row["realized_pnl"]), expected_pnl, places=8)
|
|
self.assertAlmostEqual(float(row["close_index_px"]), spot, places=2)
|
|
self.assertLess(abs(float(row["realized_pnl"])), 1.0) # 币量级,绝不是 ~100 U
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|