fix真盈亏只认历史币盈亏不再另扣费,刷新按固定量价重算;修模拟到期币盈亏。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -75,7 +75,7 @@ class SettleClosedPnlTests(unittest.TestCase):
|
||||
self.assertAlmostEqual(float(s["realized_pnl_usdt"]), expect, places=2)
|
||||
|
||||
def test_sim_loss_remaining_minus_fees(self):
|
||||
"""亏损:剩余权利金U − 手续费U − 初始U(同一式)."""
|
||||
"""亏损:费先扣进币盈亏,再按用户式折 U(不另扣手续费U)."""
|
||||
s = settle_closed_pnl(
|
||||
premium_paid=0.01,
|
||||
premium_received=0.004,
|
||||
@@ -85,8 +85,30 @@ class SettleClosedPnlTests(unittest.TestCase):
|
||||
index_px=2500,
|
||||
open_index_px=3000,
|
||||
)
|
||||
# 0.004*2500 - 0.01*3000 - (0.0001*3000+0.0001*2500) = 10 - 30 - 0.55 = -20.55
|
||||
self.assertAlmostEqual(float(s["realized_pnl_usdt"]), -20.55, places=2)
|
||||
# net_coin=-0.0062; U=-0.0062*2500 - 0.01*(3000-2500)=-20.5
|
||||
self.assertAlmostEqual(float(s["realized_pnl"]), -0.0062, places=8)
|
||||
self.assertAlmostEqual(float(s["realized_pnl_usdt"]), -20.5, places=2)
|
||||
|
||||
def test_resolve_loss_does_not_deduct_fee_again(self):
|
||||
"""历史币盈亏已含费;resolve 只跑用户式,不再用 open_fee/close_fee 扣第二遍."""
|
||||
from lib.options.options_pnl_settle_lib import resolve_true_pnl_usdt_from_trade
|
||||
|
||||
# coin 已是 recv-paid-fees=-0.0062
|
||||
pnl, src = resolve_true_pnl_usdt_from_trade(
|
||||
premium_paid=0.01,
|
||||
premium_received=0.004,
|
||||
realized_pnl=-0.0062,
|
||||
open_index_px=3000,
|
||||
close_index_px=2500,
|
||||
open_fee=0.0001,
|
||||
close_fee=0.0001,
|
||||
premium_ccy="ETH",
|
||||
)
|
||||
self.assertEqual(src, "premium_move")
|
||||
# -0.0062*2500 - 0.01*(3000-2500) = -15.5 - 5 = -20.5
|
||||
self.assertAlmostEqual(float(pnl), -20.5, places=2)
|
||||
# 若再扣一遍费会更亏于 -20.5
|
||||
self.assertGreater(float(pnl), -20.55)
|
||||
|
||||
def test_budget_ignored_for_usdt(self):
|
||||
# budget 不得抬高开仓成本
|
||||
|
||||
@@ -6,8 +6,8 @@ import unittest
|
||||
|
||||
|
||||
class ReviewLocalSyncFreezeTests(unittest.TestCase):
|
||||
def test_settled_true_pnl_not_rewritten_on_resync(self):
|
||||
"""已冻结真盈亏只读;同步不得因桥/指数再改写."""
|
||||
def test_refresh_recomputes_u_keeps_frozen_qty_px(self):
|
||||
"""刷新按公式重算U;权利金币/币盈亏/开仓卖回价不变."""
|
||||
from lib.options.options_db import init_options_tables
|
||||
from lib.options.options_review_lib import sync_options_from_local_trades
|
||||
from lib.sim.mode_lib import MODE_SIM, TRADING_MODE_KEY
|
||||
@@ -29,7 +29,7 @@ class ReviewLocalSyncFreezeTests(unittest.TestCase):
|
||||
) VALUES (
|
||||
'BTC-USD-261008-86500-P', 'BTC', 'P', 1, 0.01,
|
||||
0.01, 0.02, 0.01, 'BTC',
|
||||
0, 0, 'closed', 700.0,
|
||||
0, 0, 'closed', -51.0,
|
||||
90000.0, 80000.0, 1,
|
||||
'2026-10-05 05:11:30', '2026-10-07 10:03:53'
|
||||
)
|
||||
@@ -41,6 +41,7 @@ class ReviewLocalSyncFreezeTests(unittest.TestCase):
|
||||
trade = conn.execute(
|
||||
"SELECT realized_pnl_usdt, open_index_px, close_index_px, pnl_settled, realized_pnl, premium_paid FROM options_trades WHERE id=1"
|
||||
).fetchone()
|
||||
# 0.01*80000 - 0.01*(90000-80000)=700;旧错账 -51 被刷新纠正
|
||||
self.assertAlmostEqual(float(trade["realized_pnl_usdt"]), 700.0, places=2)
|
||||
self.assertAlmostEqual(float(trade["open_index_px"]), 90000.0, places=2)
|
||||
self.assertAlmostEqual(float(trade["close_index_px"]), 80000.0, places=2)
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
"""模拟盘到期结算:币本位盈亏须用币,不能把 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()
|
||||
Reference in New Issue
Block a user