refactor: 将共用代码迁入 lib/ 模块化目录
统一 strategy、key_monitor、trade、hub 等共用库到 lib/ 子包,并补充 lib-structure 文档,便于四所与中控维护。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,90 +1,90 @@
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from trade_stats_calendar_lib import (
|
||||
build_initial_stats_calendar,
|
||||
build_stats_calendar_bootstrap,
|
||||
build_trade_stats_calendar,
|
||||
)
|
||||
|
||||
|
||||
def _row(**kwargs):
|
||||
base = {
|
||||
"monitor_type": "",
|
||||
"key_signal_type": "",
|
||||
"exchange_turnover_usdt": None,
|
||||
"exchange_commission_usdt": None,
|
||||
}
|
||||
base.update(kwargs)
|
||||
return SimpleNamespace(**base)
|
||||
|
||||
|
||||
def _matches_all(row, segment_key):
|
||||
return segment_key == "all"
|
||||
|
||||
|
||||
def _matches_manual(row, segment_key):
|
||||
if segment_key == "all":
|
||||
return True
|
||||
if segment_key == "manual":
|
||||
return (row.monitor_type or "").strip() == "手动" and not (row.key_signal_type or "").strip()
|
||||
return False
|
||||
|
||||
|
||||
class TradeStatsCalendarLibTests(unittest.TestCase):
|
||||
def test_groups_by_trading_day_and_segment(self):
|
||||
pnls = [
|
||||
(10.0, None, "2026-06-18", _row(monitor_type="手动")),
|
||||
(-3.0, None, "2026-06-18", _row(monitor_type="手动")),
|
||||
(5.0, None, "2026-06-19", _row(monitor_type="自动", key_signal_type="箱体突破")),
|
||||
]
|
||||
payload = build_trade_stats_calendar(
|
||||
pnls,
|
||||
2026,
|
||||
6,
|
||||
"manual",
|
||||
_matches_manual,
|
||||
reset_hour=8,
|
||||
)
|
||||
self.assertEqual(payload["month"], 6)
|
||||
self.assertEqual(payload["month_open_count"], 2)
|
||||
days = payload["days"]
|
||||
self.assertIn("2026-06-18", days)
|
||||
self.assertNotIn("2026-06-19", days)
|
||||
self.assertEqual(days["2026-06-18"]["open_count"], 2)
|
||||
self.assertAlmostEqual(days["2026-06-18"]["pnl_total"], 7.0)
|
||||
|
||||
def test_invalid_month_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
build_trade_stats_calendar([], 2026, 13, "all", _matches_all)
|
||||
|
||||
def test_initial_calendar_uses_current_month(self):
|
||||
pnls = [(2.5, None, "2026-06-20", _row())]
|
||||
payload = build_initial_stats_calendar(
|
||||
pnls,
|
||||
datetime(2026, 6, 26, 12, 0),
|
||||
_matches_all,
|
||||
reset_hour=8,
|
||||
)
|
||||
self.assertEqual(payload["year"], 2026)
|
||||
self.assertEqual(payload["month"], 6)
|
||||
self.assertEqual(payload["month_open_count"], 1)
|
||||
self.assertIn("2026-06-20", payload["days"])
|
||||
|
||||
def test_bootstrap_json_roundtrip(self):
|
||||
pnls = [(2.5, None, "2026-06-20", _row())]
|
||||
payload, raw = build_stats_calendar_bootstrap(
|
||||
pnls,
|
||||
datetime(2026, 6, 26, 12, 0),
|
||||
_matches_all,
|
||||
reset_hour=8,
|
||||
)
|
||||
self.assertIsNotNone(payload)
|
||||
self.assertIsNotNone(raw)
|
||||
self.assertIn('"month_open_count":1', raw.replace(" ", ""))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from lib.trade.trade_stats_calendar_lib import (
|
||||
build_initial_stats_calendar,
|
||||
build_stats_calendar_bootstrap,
|
||||
build_trade_stats_calendar,
|
||||
)
|
||||
|
||||
|
||||
def _row(**kwargs):
|
||||
base = {
|
||||
"monitor_type": "",
|
||||
"key_signal_type": "",
|
||||
"exchange_turnover_usdt": None,
|
||||
"exchange_commission_usdt": None,
|
||||
}
|
||||
base.update(kwargs)
|
||||
return SimpleNamespace(**base)
|
||||
|
||||
|
||||
def _matches_all(row, segment_key):
|
||||
return segment_key == "all"
|
||||
|
||||
|
||||
def _matches_manual(row, segment_key):
|
||||
if segment_key == "all":
|
||||
return True
|
||||
if segment_key == "manual":
|
||||
return (row.monitor_type or "").strip() == "手动" and not (row.key_signal_type or "").strip()
|
||||
return False
|
||||
|
||||
|
||||
class TradeStatsCalendarLibTests(unittest.TestCase):
|
||||
def test_groups_by_trading_day_and_segment(self):
|
||||
pnls = [
|
||||
(10.0, None, "2026-06-18", _row(monitor_type="手动")),
|
||||
(-3.0, None, "2026-06-18", _row(monitor_type="手动")),
|
||||
(5.0, None, "2026-06-19", _row(monitor_type="自动", key_signal_type="箱体突破")),
|
||||
]
|
||||
payload = build_trade_stats_calendar(
|
||||
pnls,
|
||||
2026,
|
||||
6,
|
||||
"manual",
|
||||
_matches_manual,
|
||||
reset_hour=8,
|
||||
)
|
||||
self.assertEqual(payload["month"], 6)
|
||||
self.assertEqual(payload["month_open_count"], 2)
|
||||
days = payload["days"]
|
||||
self.assertIn("2026-06-18", days)
|
||||
self.assertNotIn("2026-06-19", days)
|
||||
self.assertEqual(days["2026-06-18"]["open_count"], 2)
|
||||
self.assertAlmostEqual(days["2026-06-18"]["pnl_total"], 7.0)
|
||||
|
||||
def test_invalid_month_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
build_trade_stats_calendar([], 2026, 13, "all", _matches_all)
|
||||
|
||||
def test_initial_calendar_uses_current_month(self):
|
||||
pnls = [(2.5, None, "2026-06-20", _row())]
|
||||
payload = build_initial_stats_calendar(
|
||||
pnls,
|
||||
datetime(2026, 6, 26, 12, 0),
|
||||
_matches_all,
|
||||
reset_hour=8,
|
||||
)
|
||||
self.assertEqual(payload["year"], 2026)
|
||||
self.assertEqual(payload["month"], 6)
|
||||
self.assertEqual(payload["month_open_count"], 1)
|
||||
self.assertIn("2026-06-20", payload["days"])
|
||||
|
||||
def test_bootstrap_json_roundtrip(self):
|
||||
pnls = [(2.5, None, "2026-06-20", _row())]
|
||||
payload, raw = build_stats_calendar_bootstrap(
|
||||
pnls,
|
||||
datetime(2026, 6, 26, 12, 0),
|
||||
_matches_all,
|
||||
reset_hour=8,
|
||||
)
|
||||
self.assertIsNotNone(payload)
|
||||
self.assertIsNotNone(raw)
|
||||
self.assertIn('"month_open_count":1', raw.replace(" ", ""))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user