"""P0 对账 spike 的纯逻辑测试(compare / _auto_splits)。 跑真实数据的对账在 VPS 上执行(见 scripts/shadow_desk/spike_p0_checkpoint_replay.py)。 """ from __future__ import annotations import importlib.util import sys from pathlib import Path import pytest _SPEC = importlib.util.spec_from_file_location( "spike_p0", Path(__file__).resolve().parents[2] / "scripts" / "shadow_desk" / "spike_p0_checkpoint_replay.py", ) spike = importlib.util.module_from_spec(_SPEC) sys.modules.setdefault("spike_p0", spike) _SPEC.loader.exec_module(spike) def _full(consistency: str = "same") -> dict: if consistency == "same": positions = [{"security": "600519.SH", "amount": 100, "avg_cost": 1700.5, "price": 1750.0}] cash, total = 820_000.0, 995_000.0 equity = [("2024-01-05", 1_000_000.0), ("2024-02-01", 1_010_000.0)] elif consistency == "amount_diff": positions = [{"security": "600519.SH", "amount": 200, "avg_cost": 1700.5, "price": 1750.0}] cash, total = 820_000.0, 995_000.0 equity = [("2024-01-05", 1_000_000.0), ("2024-02-01", 1_010_000.0)] else: # equity_drift positions = [{"security": "600519.SH", "amount": 100, "avg_cost": 1700.5, "price": 1750.0}] cash, total = 820_000.0, 995_000.0 equity = [("2024-01-05", 1_000_000.0), ("2024-02-01", 1_010_500.0)] return { "final_portfolio": {"cash": cash, "total_value": total, "positions": positions}, "equity_curve": [{"date": d, "equity": v} for d, v in equity], } def _seg() -> dict: return { "final_portfolio": { "cash": 820_000.000_000_4, # 浮点级噪声 "total_value": 995_000.0, "positions": [{"security": "600519.SH", "amount": 100, "avg_cost": 1700.500_000_1, "price": 1750.0}], }, "equity_curve": [{"date": "2024-01-05", "equity": 1_000_000.0}, {"date": "2024-02-01", "equity": 1_010_000.0}], } def test_compare_pass_on_float_noise(): assert spike.compare(_full(), _seg()) == [] def test_compare_catches_amount_diff(): problems = spike.compare(_full("amount_diff"), _seg()) assert any("数量" in p for p in problems) def test_compare_catches_equity_drift(): problems = spike.compare(_full("equity_drift"), _seg()) assert any("净值曲线最大偏差" in p for p in problems) def test_compare_catches_missing_position(): seg = _seg() seg["final_portfolio"]["positions"] = [] problems = spike.compare(_full(), seg) assert any("单边缺失" in p for p in problems) def test_compare_catches_missing_final_portfolio(): problems = spike.compare({"equity_curve": []}, _seg()) assert problems and "final_portfolio" in problems[0] def test_auto_splits_month_first_and_ascending(): splits = spike._auto_splits("2024-01-01", "2024-12-31") assert len(splits) == 2 assert splits[0] < splits[1] assert all(s.endswith("-01") for s in splits) # 月初