From 1b39c53f189ef7ed8dd41defcf7906891b3c2d4e Mon Sep 17 00:00:00 2001 From: ao gong <41768719+ageorge156@users.noreply.github.com> Date: Fri, 21 Aug 2026 22:02:58 +0800 Subject: [PATCH] test: define daily ledger projection contract --- tests/test_execution.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/test_execution.py b/tests/test_execution.py index dc9cf15..aebda84 100644 --- a/tests/test_execution.py +++ b/tests/test_execution.py @@ -583,6 +583,47 @@ def test_daily_ledger_nav_is_rebuildable_and_trades_are_projectable() -> None: assert (trades["qty"] > 0).all() +def test_daily_ledger_frame_matches_platform_projection_contract() -> None: + """核心层输出稳定日频投影,但不携带 run_id 或执行数据库写入。""" + config = ExecutionConfig( + commission_bps=0, + stamp_tax_bps=0, + slippage_bps=0, + min_trade_amount=0, + ) + result = simulate_daily_ledger_with_audit( + target_weights_history=[("d1", {"A": 1.0})], + execution_price_history=[("d1", {"A": 10.0})], + valuation_price_history=[ + ("d0", {"A": 9.0}), + ("d1", {"A": 11.0}), + ("d2", {"A": 12.0}), + ], + initial_cash=1_000.0, + config=config, + ) + + ledger = result.ledger_frame + + assert ledger.columns.tolist() == [ + "trade_date", + "portfolio_value", + "nav", + "pnl", + "pnl_pct", + "position_value", + "cash", + "turnover", + ] + assert ledger["trade_date"].tolist() == ["d0", "d1", "d2"] + assert ledger["nav"].tolist() == pytest.approx([1.0, 1.1, 1.2]) + assert ledger["pnl"].tolist() == pytest.approx([0.0, 100.0, 100.0]) + assert ledger["pnl_pct"].tolist() == pytest.approx([0.0, 0.1, 1.2 / 1.1 - 1.0]) + assert ledger["position_value"].tolist() == pytest.approx([0.0, 1_100.0, 1_200.0]) + assert ledger["cash"].tolist() == pytest.approx([1_000.0, 0.0, 0.0]) + assert ledger["turnover"].tolist() == pytest.approx([0.0, 1.0, 0.0]) + + def test_daily_ledger_rejects_missing_close_for_held_asset() -> None: """已有持仓缺少收盘估值价时必须 fail closed。""" with pytest.raises(ValueError, match="missing valuation price for held asset A"):