Files
crypto_okx/tests/test_options_pnl_settle.py
T

157 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""期权已实现盈亏:扣手续费 + 平仓冻结 U 不跟实时指数变."""
from __future__ import annotations
import unittest
from lib.options.options_pnl_settle_lib import (
fee_to_premium_ccy,
settle_closed_pnl,
settle_exchange_realized_pnl,
)
from lib.options.options_stats_lib import (
compute_options_stats_from_history,
history_pnl_to_usdt,
)
class FeeConvertTests(unittest.TestCase):
def test_fee_same_ccy(self):
self.assertAlmostEqual(
fee_to_premium_ccy(0.001, "ETH", premium_ccy="ETH", index_px=3000),
0.001,
places=8,
)
def test_fee_usdt_to_eth(self):
self.assertAlmostEqual(
fee_to_premium_ccy(3.0, "USDT", premium_ccy="ETH", index_px=3000),
0.001,
places=8,
)
class SettleClosedPnlTests(unittest.TestCase):
def test_net_of_fees_and_freeze_usdt(self):
# 收回 0.02 ETH,已付 0.01,开+平手续费各 0.0005 → 净 0.009
# × 3000 = 27 U
s = settle_closed_pnl(
premium_paid=0.01,
premium_received=0.02,
open_fee=0.0005,
close_fee=0.0005,
premium_ccy="ETH",
index_px=3000,
)
self.assertAlmostEqual(float(s["realized_pnl"]), 0.009, places=8)
self.assertAlmostEqual(float(s["realized_pnl_usdt"]), 27.0, places=4)
self.assertAlmostEqual(float(s["close_index_px"]), 3000.0, places=4)
def test_usdc_freeze_is_net(self):
s = settle_closed_pnl(
premium_paid=10,
premium_received=12,
open_fee=0.1,
close_fee=0.1,
premium_ccy="USDC",
index_px=None,
)
self.assertAlmostEqual(float(s["realized_pnl"]), 1.8, places=4)
self.assertAlmostEqual(float(s["realized_pnl_usdt"]), 1.8, places=4)
def test_exchange_realized_not_fee_again(self):
# OKX realizedPnl 已含费:只折 U,不得再减 fee
s = settle_exchange_realized_pnl(
realized_pnl=0.009,
premium_ccy="ETH",
index_px=3000,
fee_hint=0.001,
)
self.assertAlmostEqual(float(s["realized_pnl"]), 0.009, places=8)
self.assertAlmostEqual(float(s["realized_pnl_usdt"]), 27.0, places=4)
class RepairDoubleFeeTests(unittest.TestCase):
def test_repair_detects_double_fee(self):
import sqlite3
from lib.options.options_db import init_options_tables
from lib.options.options_stats_lib import repair_double_counted_fee_pnl
conn = sqlite3.connect(":memory:")
conn.row_factory = sqlite3.Row
init_options_tables(conn)
# recv-paid=0.01, fees=0.001 → 单次净 0.009;错误写成双重 0.008
conn.execute(
"""
INSERT INTO options_trades(
inst_id, underlying, opt_type, sheets, eth_amount,
premium_paid, premium_received, realized_pnl, open_fee, close_fee,
premium_ccy, status, close_index_px, realized_pnl_usdt
) VALUES (
'ETH-USD-260701-2500-C', 'ETH', 'C', 1, 0.01,
0.01, 0.02, 0.008, 0.0005, 0.0005,
'ETH', 'closed', 3000, 24.0
)
"""
)
conn.commit()
n = repair_double_counted_fee_pnl(conn, None)
self.assertEqual(n, 1)
row = conn.execute(
"SELECT realized_pnl, realized_pnl_usdt FROM options_trades WHERE id=1"
).fetchone()
self.assertAlmostEqual(float(row["realized_pnl"]), 0.009, places=8)
self.assertAlmostEqual(float(row["realized_pnl_usdt"]), 27.0, places=4)
conn.close()
class FrozenStatsTests(unittest.TestCase):
def test_frozen_usdt_ignores_live_index(self):
history = [
{
"status": "closed",
"inst_id": "ETH-USD-260701-2500-C",
"underlying": "ETH",
"premium_ccy": "ETH",
"realized_pnl": 0.01, # 币数量
"realized_pnl_usdt": 25.0, # 平仓时冻结
"close_index_px": 2500.0,
"created_at": "2026-10-01 10:00:00",
"closed_at": "2026-10-01 12:00:00",
}
]
class _Ex:
def public_get_market_ticker(self, params):
return {"data": [{"last": "99999"}]} # 实时价再变也不影响
converted = history_pnl_to_usdt(history, _Ex())
self.assertAlmostEqual(float(converted[0]["realized_pnl"]), 25.0, places=4)
stats = compute_options_stats_from_history(converted)
self.assertAlmostEqual(float(stats["net_realized_pnl"]), 25.0, places=4)
def test_close_index_used_when_no_frozen_usdt(self):
history = [
{
"status": "closed",
"inst_id": "ETH-USD-260701-2500-C",
"underlying": "ETH",
"premium_ccy": "ETH",
"realized_pnl": 0.01,
"close_index_px": 2500.0,
"created_at": "2026-10-01 10:00:00",
"closed_at": "2026-10-01 12:00:00",
}
]
class _Ex:
def public_get_market_ticker(self, params):
return {"data": [{"last": "99999"}]}
converted = history_pnl_to_usdt(history, _Ex())
self.assertAlmostEqual(float(converted[0]["realized_pnl"]), 25.0, places=4)
if __name__ == "__main__":
unittest.main()