fix复盘真盈亏按权利金USDT波动公式计算,忽略现货多买。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import unittest
|
||||
|
||||
|
||||
class AccountPnlSettleTests(unittest.TestCase):
|
||||
def test_settle_after_minus_before(self):
|
||||
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,
|
||||
)
|
||||
@@ -15,16 +15,19 @@ class AccountPnlSettleTests(unittest.TestCase):
|
||||
conn = sqlite3.connect(":memory:")
|
||||
conn.row_factory = sqlite3.Row
|
||||
init_options_tables(conn)
|
||||
# coin_pnl=0.01, open=90000, close=80000 → A=700; 账户差分会是 947 但不采用
|
||||
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
|
||||
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, 'closed', 7, 1000.0, 0,
|
||||
'2026-10-05 05:11:30', '2026-10-07 10:03:53'
|
||||
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'
|
||||
)
|
||||
"""
|
||||
)
|
||||
@@ -37,7 +40,7 @@ class AccountPnlSettleTests(unittest.TestCase):
|
||||
"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"]), 947.05, places=2)
|
||||
self.assertAlmostEqual(float(row["realized_pnl_usdt"]), 700.0, places=2)
|
||||
self.assertEqual(int(row["pnl_settled"]), 1)
|
||||
|
||||
# 已冻结不再改
|
||||
@@ -48,7 +51,7 @@ class AccountPnlSettleTests(unittest.TestCase):
|
||||
row2 = conn.execute(
|
||||
"SELECT realized_pnl_usdt FROM options_trades WHERE id=1"
|
||||
).fetchone()
|
||||
self.assertAlmostEqual(float(row2["realized_pnl_usdt"]), 947.05, places=2)
|
||||
self.assertAlmostEqual(float(row2["realized_pnl_usdt"]), 700.0, places=2)
|
||||
conn.close()
|
||||
|
||||
def test_stats_use_coin_times_sell_px_not_account(self):
|
||||
@@ -149,48 +152,35 @@ class AccountPnlSettleTests(unittest.TestCase):
|
||||
self.assertEqual(int(out["total"]), 2)
|
||||
conn.close()
|
||||
|
||||
def test_resolve_true_pnl_prefers_account_then_bridge(self):
|
||||
from lib.options.options_account_pnl_lib import resolve_true_pnl_usdt
|
||||
from lib.options.options_db import init_options_tables
|
||||
from lib.options.options_spot_bridge_lib import ensure_bridge_table
|
||||
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,
|
||||
)
|
||||
|
||||
conn = sqlite3.connect(":memory:")
|
||||
conn.row_factory = sqlite3.Row
|
||||
init_options_tables(conn)
|
||||
ensure_bridge_table(conn)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO options_spot_bridge(
|
||||
id, underlying, status, budget_usdt, usdt_recovered
|
||||
) VALUES (3, 'ETH', 'closed', 100.0, 160.0)
|
||||
"""
|
||||
# A = 0.01*80000 - (0.01*90000 - 0.01*80000) = 700
|
||||
self.assertAlmostEqual(
|
||||
float(
|
||||
true_pnl_usdt_premium_move(
|
||||
coin_pnl=0.01,
|
||||
premium_paid=0.01,
|
||||
open_index_px=90000,
|
||||
sell_back_px=80000,
|
||||
)
|
||||
),
|
||||
700.0,
|
||||
places=4,
|
||||
)
|
||||
conn.commit()
|
||||
pnl, src = resolve_true_pnl_usdt(
|
||||
conn,
|
||||
trading_usdt_before=500.0,
|
||||
trading_usdt_after=580.0,
|
||||
bridge_id=3,
|
||||
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, "account")
|
||||
self.assertAlmostEqual(float(pnl), 80.0, places=4)
|
||||
pnl2, src2 = resolve_true_pnl_usdt(
|
||||
conn,
|
||||
trading_usdt_before=500.0,
|
||||
trading_usdt_after=None,
|
||||
bridge_id=3,
|
||||
)
|
||||
self.assertEqual(src2, "bridge")
|
||||
self.assertAlmostEqual(float(pnl2), 60.0, places=4)
|
||||
pnl3, src3 = resolve_true_pnl_usdt(
|
||||
conn,
|
||||
trading_usdt_before=None,
|
||||
realized_pnl_usdt=999.0,
|
||||
pnl_settled=1,
|
||||
)
|
||||
self.assertEqual(src3, "none")
|
||||
self.assertIsNone(pnl3)
|
||||
conn.close()
|
||||
self.assertEqual(src, "premium_move")
|
||||
self.assertAlmostEqual(float(pnl), 700.0, places=4)
|
||||
|
||||
def test_settle_blocked_when_sellable_coin(self):
|
||||
from lib.options.options_account_pnl_lib import try_settle_after_sellback
|
||||
|
||||
@@ -7,6 +7,7 @@ from lib.options.options_pnl_settle_lib import (
|
||||
fee_to_premium_ccy,
|
||||
settle_closed_pnl,
|
||||
settle_exchange_realized_pnl,
|
||||
true_pnl_usdt_premium_move,
|
||||
)
|
||||
from lib.options.options_stats_lib import (
|
||||
apply_exchange_pnl_freeze,
|
||||
@@ -24,6 +25,26 @@ class FeeConvertTests(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class PremiumMoveFormulaTests(unittest.TestCase):
|
||||
def test_user_formula_matches_proceeds_minus_open_cost(self):
|
||||
coin = 0.01
|
||||
paid = 0.01
|
||||
open_px = 90000.0
|
||||
close_px = 80000.0
|
||||
a = true_pnl_usdt_premium_move(
|
||||
coin_pnl=coin,
|
||||
premium_paid=paid,
|
||||
open_index_px=open_px,
|
||||
sell_back_px=close_px,
|
||||
)
|
||||
# 历史盈亏×卖回 − (权利金×开 − 权利金×平)
|
||||
expect = coin * close_px - (paid * open_px - paid * close_px)
|
||||
self.assertAlmostEqual(float(a), expect, places=4)
|
||||
# 无费时 ≡ 收回×平 − 已付×开 (recv=paid+coin)
|
||||
recv = paid + coin
|
||||
self.assertAlmostEqual(float(a), recv * close_px - paid * open_px, places=4)
|
||||
|
||||
|
||||
class SettleClosedPnlTests(unittest.TestCase):
|
||||
def test_sim_net_times_close_index(self):
|
||||
# 无开仓指数时回退净币×平仓价
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""复盘真盈亏=卖光收回−买币;禁止权利金×指数冒充."""
|
||||
"""复盘真盈亏=币盈亏×卖回价 − 权利金×(开仓价−卖回价)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
@@ -6,7 +6,7 @@ import unittest
|
||||
|
||||
|
||||
class ReviewLocalSyncFreezeTests(unittest.TestCase):
|
||||
def test_account_after_minus_before_is_true_pnl(self):
|
||||
def test_premium_usdt_move_formula(self):
|
||||
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
|
||||
@@ -17,77 +17,65 @@ class ReviewLocalSyncFreezeTests(unittest.TestCase):
|
||||
init_options_tables(conn)
|
||||
ensure_runtime_settings_table(conn)
|
||||
runtime_set(conn, TRADING_MODE_KEY, MODE_SIM)
|
||||
# paid=0.01, recv=0.02, coin_pnl=0.01; open=90000, close=80000
|
||||
# A = 0.01*80000 - (0.01*90000 - 0.01*80000) = 800 - 100 = 700
|
||||
# 亦= recv*close - paid*open = 1600 - 900 = 700
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO options_trades(
|
||||
inst_id, underlying, opt_type, sheets, eth_amount,
|
||||
premium_paid, premium_received, realized_pnl, premium_ccy,
|
||||
status, realized_pnl_usdt, close_index_px,
|
||||
open_fee, close_fee, status, realized_pnl_usdt,
|
||||
open_index_px, close_index_px,
|
||||
trading_usdt_before, trading_usdt_after, pnl_settled,
|
||||
created_at, closed_at
|
||||
) VALUES (
|
||||
'BTC-USD-261008-86500-P', 'BTC', 'P', 1, 0.01,
|
||||
0.01, 0.02, 0.01, 'BTC',
|
||||
'closed', -51.0, 85000.0,
|
||||
0, 0, 'closed', -51.0,
|
||||
90000.0, 80000.0,
|
||||
1000.0, 2506.0, 1,
|
||||
'2026-10-05 05:11:30', '2026-10-07 10:03:53'
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
class _Ex:
|
||||
def public_get_market_ticker(self, params):
|
||||
return {"data": [{"last": "999999"}]}
|
||||
|
||||
out = sync_options_from_local_trades(conn, _Ex())
|
||||
out = sync_options_from_local_trades(conn, None)
|
||||
self.assertTrue(out.get("ok"))
|
||||
row = conn.execute(
|
||||
"SELECT realized_pnl_total FROM options_review_trades WHERE history_key=?",
|
||||
("local_opt:1",),
|
||||
).fetchone()
|
||||
self.assertIsNotNone(row)
|
||||
# 真盈亏=after−before=1506,不是错误冻结 -51,也不是指数折算
|
||||
self.assertAlmostEqual(float(row["realized_pnl_total"]), 1506.0, places=2)
|
||||
self.assertAlmostEqual(float(row["realized_pnl_total"]), 700.0, places=2)
|
||||
# 纠偏写回,不用账户差分 1506
|
||||
healed = conn.execute(
|
||||
"SELECT realized_pnl_usdt FROM options_trades WHERE id=1"
|
||||
).fetchone()
|
||||
self.assertAlmostEqual(float(healed["realized_pnl_usdt"]), 1506.0, places=2)
|
||||
self.assertAlmostEqual(float(healed["realized_pnl_usdt"]), 700.0, places=2)
|
||||
conn.close()
|
||||
|
||||
def test_bridge_cash_is_true_pnl_when_no_after(self):
|
||||
def test_same_open_close_px_equals_coin_times_close(self):
|
||||
from lib.options.options_db import init_options_tables
|
||||
from lib.options.options_review_lib import sync_options_from_local_trades
|
||||
from lib.options.options_spot_bridge_lib import ensure_bridge_table
|
||||
from lib.sim.mode_lib import MODE_SIM, TRADING_MODE_KEY
|
||||
from lib.instance.runtime_settings_lib import ensure_runtime_settings_table, runtime_set
|
||||
|
||||
conn = sqlite3.connect(":memory:")
|
||||
conn.row_factory = sqlite3.Row
|
||||
init_options_tables(conn)
|
||||
ensure_bridge_table(conn)
|
||||
ensure_runtime_settings_table(conn)
|
||||
runtime_set(conn, TRADING_MODE_KEY, MODE_SIM)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO options_spot_bridge(
|
||||
id, underlying, status, budget_usdt, usdt_recovered, message
|
||||
) VALUES (7, 'BTC', 'closed', 2200.0, 3706.0, 'done')
|
||||
"""
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO options_trades(
|
||||
inst_id, underlying, opt_type, sheets, eth_amount,
|
||||
premium_paid, premium_received, realized_pnl, premium_ccy,
|
||||
status, realized_pnl_usdt, close_index_px,
|
||||
trading_usdt_before, bridge_id, pnl_settled,
|
||||
open_fee, close_fee, status, open_index_px, close_index_px,
|
||||
created_at, closed_at
|
||||
) VALUES (
|
||||
'BTC-USD-261008-86500-P', 'BTC', 'P', 1, 0.01,
|
||||
0.01, 0.02, 0.01, 'BTC',
|
||||
'closed', 850.0, 85000.0,
|
||||
1000.0, 7, 0,
|
||||
0, 0, 'closed', 85000.0, 85000.0,
|
||||
'2026-10-05 05:11:30', '2026-10-07 10:03:53'
|
||||
)
|
||||
"""
|
||||
@@ -98,44 +86,8 @@ class ReviewLocalSyncFreezeTests(unittest.TestCase):
|
||||
"SELECT realized_pnl_total FROM options_review_trades WHERE history_key=?",
|
||||
("local_opt:1",),
|
||||
).fetchone()
|
||||
# 3706 − 2200 = 1506;不是 0.01*85000
|
||||
self.assertAlmostEqual(float(row["realized_pnl_total"]), 1506.0, places=2)
|
||||
conn.close()
|
||||
|
||||
def test_index_formula_not_shown_as_true_pnl(self):
|
||||
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
|
||||
from lib.instance.runtime_settings_lib import ensure_runtime_settings_table, runtime_set
|
||||
|
||||
conn = sqlite3.connect(":memory:")
|
||||
conn.row_factory = sqlite3.Row
|
||||
init_options_tables(conn)
|
||||
ensure_runtime_settings_table(conn)
|
||||
runtime_set(conn, TRADING_MODE_KEY, MODE_SIM)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO options_trades(
|
||||
inst_id, underlying, opt_type, sheets, eth_amount,
|
||||
premium_paid, premium_received, realized_pnl, premium_ccy,
|
||||
status, realized_pnl_usdt, close_index_px,
|
||||
created_at, closed_at
|
||||
) VALUES (
|
||||
'BTC-USD-261008-86500-P', 'BTC', 'P', 1, 0.01,
|
||||
0.01, 0.02, 0.01, 'BTC',
|
||||
'closed', 850.0, 85000.0,
|
||||
'2026-10-05 05:11:30', '2026-10-07 10:03:53'
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.commit()
|
||||
sync_options_from_local_trades(conn, None)
|
||||
row = conn.execute(
|
||||
"SELECT realized_pnl_total, status_raw FROM options_review_trades WHERE history_key=?",
|
||||
("local_opt:1",),
|
||||
).fetchone()
|
||||
# 无账户/桥快照:不得用指数折 U 冒充真盈亏
|
||||
self.assertIsNone(row["realized_pnl_total"])
|
||||
# 开平价相同:波动项为0 → 等于币×卖回价=850
|
||||
self.assertAlmostEqual(float(row["realized_pnl_total"]), 850.0, places=2)
|
||||
conn.close()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user