Fix hub total floating PnL by excluding OKX options from swap agent.

Option legs were scored with linear swap math and then added again from the options snapshot, inflating 总浮盈亏 and 持有仓位.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-05 22:13:22 +08:00
parent 3f6e67661b
commit 7352d10254
5 changed files with 209 additions and 69 deletions
+15
View File
@@ -14,6 +14,7 @@ sys.path.insert(0, str(ROOT))
from lib.hub.hub_position_metrics import ( # noqa: E402
enrich_ccxt_position_metrics_out,
estimate_linear_swap_upnl_usdt,
is_option_like_position,
parse_position_unrealized_pnl,
resolve_position_display_upnl,
)
@@ -89,6 +90,20 @@ class TestHubAgentMarkPrice(unittest.TestCase):
)
self.assertAlmostEqual(shown, 7.86, places=2)
def test_is_option_like_position(self):
self.assertTrue(
is_option_like_position({"symbol": "ETH/USD:USD-260806-1875-C", "contracts": 1})
)
self.assertTrue(
is_option_like_position(
{"symbol": "x", "info": {"instType": "OPTION", "instId": "ETH-USD-260806-1875-P"}}
)
)
self.assertFalse(
is_option_like_position({"symbol": "BTC/USDT:USDT", "contracts": 10})
)
self.assertFalse(is_option_like_position({"symbol": "ETH/USDT:USDT"}))
if __name__ == "__main__":
unittest.main()
+105 -66
View File
@@ -1,75 +1,114 @@
"""中控监控区今日统计聚合."""
import unittest
from lib.hub.hub_monitor_totals_lib import aggregate_monitor_board_totals
from lib.hub.hub_trades_lib import summarize_trades
def test_aggregate_monitor_board_totals_sums_rows():
rows = [
{
"day_stats": {
"ok": True,
"opens_today": 2,
"trade_stats": {
"closed_count": 1,
"win_count": 1,
"loss_count": 0,
"win_pnl_u": 5.5,
"loss_pnl_u": 0,
class TestHubMonitorTotals(unittest.TestCase):
def test_aggregate_monitor_board_totals_sums_rows(self):
rows = [
{
"day_stats": {
"ok": True,
"opens_today": 2,
"trade_stats": {
"closed_count": 1,
"win_count": 1,
"loss_count": 0,
"win_pnl_u": 5.5,
"loss_pnl_u": 0,
},
},
"agent": {"positions": [{"contracts": 1}], "total_unrealized_pnl": 1.2},
},
"agent": {"positions": [{"contracts": 1}], "total_unrealized_pnl": 1.2},
},
{
"day_stats": {
"ok": True,
"opens_today": 1,
"trade_stats": {
"closed_count": 2,
"win_count": 0,
"loss_count": 2,
"win_pnl_u": 0,
"loss_pnl_u": -3.0,
{
"day_stats": {
"ok": True,
"opens_today": 1,
"trade_stats": {
"closed_count": 2,
"win_count": 0,
"loss_count": 2,
"win_pnl_u": 0,
"loss_pnl_u": -3.0,
},
},
"agent": {"positions": [], "total_unrealized_pnl": 0},
},
"agent": {"positions": [], "total_unrealized_pnl": 0},
},
]
out = aggregate_monitor_board_totals(rows, trading_day="2026-07-04", reset_hour=8)
assert out["open_count"] == 3
assert out["closed_count"] == 3
assert out["win_count"] == 1
assert out["loss_count"] == 2
assert out["win_pnl_u"] == 5.5
assert out["loss_pnl_u"] == -3.0
assert out["open_position_count"] == 1
assert out["float_pnl_u"] == 1.2
]
out = aggregate_monitor_board_totals(rows, trading_day="2026-07-04", reset_hour=8)
self.assertEqual(out["open_count"], 3)
self.assertEqual(out["closed_count"], 3)
self.assertEqual(out["win_count"], 1)
self.assertEqual(out["loss_count"], 2)
self.assertEqual(out["win_pnl_u"], 5.5)
self.assertEqual(out["loss_pnl_u"], -3.0)
self.assertEqual(out["open_position_count"], 1)
self.assertEqual(out["float_pnl_u"], 1.2)
def test_aggregate_monitor_board_totals_includes_options(self):
rows = [
{
"capabilities": ["options"],
"options": {
"ok": True,
"enabled": True,
"positions": [{"inst_id": "X"}, {"inst_id": "Y"}],
"upl_total_usdc": 1.5,
},
"agent": {"positions": [], "total_unrealized_pnl": 0},
}
]
out = aggregate_monitor_board_totals(rows, trading_day="2026-07-04", reset_hour=8)
self.assertEqual(out["options_open_position_count"], 2)
self.assertEqual(out["open_position_count"], 2)
self.assertEqual(out["options_float_pnl_u"], 1.5)
self.assertEqual(out["float_pnl_u"], 1.5)
def test_aggregate_excludes_option_like_agent_positions(self):
"""子代理误把期权当永续上报时:不算进持仓数,浮盈只用期权 snap."""
rows = [
{
"capabilities": ["options"],
"options": {
"ok": True,
"enabled": True,
"positions": [{"inst_id": "ETH-USD-260806-1875-C"}],
"upl_total_usdc": -0.4,
},
"agent": {
"positions": [
{
"symbol": "ETH/USD:USD-260806-1875-C",
"contracts": 66,
"unrealized_pnl": 27.6,
},
{
"symbol": "BTC/USDT:USDT",
"contracts": 1,
"unrealized_pnl": -4.66,
},
],
"total_unrealized_pnl": 22.94,
},
}
]
out = aggregate_monitor_board_totals(rows, trading_day="2026-08-05", reset_hour=8)
self.assertEqual(out["open_position_count"], 2)
self.assertEqual(out["options_open_position_count"], 1)
self.assertEqual(out["options_float_pnl_u"], -0.4)
self.assertEqual(out["float_pnl_u"], round(-4.66 + (-0.4), 4))
def test_summarize_trades_win_loss_amounts(self):
stats = summarize_trades(
[{"pnl_amount": 2.5}, {"pnl_amount": -1.0}, {"pnl_amount": 0}]
)
self.assertEqual(stats["win_count"], 1)
self.assertEqual(stats["loss_count"], 1)
self.assertEqual(stats["win_pnl_u"], 2.5)
self.assertEqual(stats["loss_pnl_u"], -1.0)
def test_aggregate_monitor_board_totals_includes_options():
rows = [
{
"capabilities": ["options"],
"options": {
"ok": True,
"enabled": True,
"positions": [{"inst_id": "X"}, {"inst_id": "Y"}],
"upl_total_usdc": 1.5,
},
"agent": {"positions": [], "total_unrealized_pnl": 0},
}
]
out = aggregate_monitor_board_totals(rows, trading_day="2026-07-04", reset_hour=8)
assert out["options_open_position_count"] == 2
assert out["open_position_count"] == 2
assert out["options_float_pnl_u"] == 1.5
assert out["float_pnl_u"] == 1.5
def test_summarize_trades_win_loss_amounts():
from lib.hub.hub_trades_lib import summarize_trades
stats = summarize_trades(
[{"pnl_amount": 2.5}, {"pnl_amount": -1.0}, {"pnl_amount": 0}]
)
assert stats["win_count"] == 1
assert stats["loss_count"] == 1
assert stats["win_pnl_u"] == 2.5
assert stats["loss_pnl_u"] == -1.0
if __name__ == "__main__":
unittest.main()