3.2 KiB
3.2 KiB
Test Evidence — Event-Driven PIT Backtest (2026-08-28)
Summary
Replaced the calendar-based backtest loop with a strict point-in-time, event-driven engine implementing the user-confirmed lifecycle.
Confirmed user decisions implemented
- Strict PIT — backtest blocks (400 + missing list) when factor vintages, Siamchart manifest, or executable prices do not fully cover the window.
- Execution timing — signal frozen on release date D; executed at next trading-day close after D (no same-day look-ahead).
- Dividend timing — entitled on ex-date (shares held before it); cash
available exactly
ex_date + 30calendar days, exposed asex_date_plus_30d. - Average cost — realized P&L uses weighted average cost.
- Fees — all-in 0.3% of notional on every buy and every sell; no added VAT.
Tasks shipped (commits on main)
| Task | Commit | Tests |
|---|---|---|
| 1. PIT readiness + default dates | 68f2cc1 |
9 |
| 2. Unified event calendar | 6439e9c |
14 |
| 3. Portfolio accounting ledger | dc057e9 |
15 |
| 4. Lot/cash-constrained rebalancer | 71b893e |
5 |
| 5. Event-driven backtest engine | 6459015 |
4 |
| 6. Durable run store + strict route | c87767c |
4 |
| 7. Frontend readiness/report | d73a58b |
build |
| 8. Verification/review | (this doc) | — |
Standalone suite (unittest, offline, deterministic)
51 tests OK
test_backtest_readiness (9) test_backtest_events (14)
test_portfolio_ledger (15) test_portfolio_rebalancer (5)
test_backtest_engine (4) test_backtest_store (4)
Compileall (all new modules): pass. git diff --check: clean.
Static security scan (new backend files)
- secrets regex: 4 hits in
backtest_readiness.py— all are the prose string "missing token" (the umbrella missing-coverage token), not credentials. - eval/exec: 0. Debug/print: 0.
- No hardcoded credentials, no path traversal, no unsafe eval.
Live probes
Fail-closed readiness (real local data — no factor vintages yet)
POST /api/v1/backtest/run on the on-disk data (0 factor vintages, no Siamchart
manifest) returns 400 with missing listing factor keys + siamchart — it
does not manufacture a fake PIT window.
GET /api/v1/backtest/readiness on real data: ready=false,
missing includes factor + siamchart, recommended_end=2026-08-27 (yesterday,
Bangkok).
Happy-path engine (synthetic PIT-complete inputs)
backend/scripts/probe_event_backtest.py (deterministic):
accounting_reconciled: True- accounting identity satisfied:
final_equity - capital == realized + unrealized + dividend_cash - fees(lhs=84760.0 == rhs=84760.0) leakage_guard: True(pit_meta-attested scorer)- dividend cash credited (ex_date+30), receivable 0 by end
- holdings all 100-lot multiples (A=5000, B=3000)
- fee 0.3% applied (240.0)
Accounting invariant
ending_equity - initial_capital
= realized_trading_pnl (gross, avg cost)
+ unrealized_trading_pnl
+ dividend_cash_received
- transaction_costs
price_pnl = realized + unrealized (compat).
Independent review
See the reviewer subagent verdict recorded in the commit / engineering-log for
this session (deleg_5225e9de).