# 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 1. **Strict PIT** — backtest blocks (400 + missing list) when factor vintages, Siamchart manifest, or executable prices do not fully cover the window. 2. **Execution timing** — signal frozen on release date D; executed at next trading-day close after D (no same-day look-ahead). 3. **Dividend timing** — entitled on ex-date (shares held before it); cash available exactly `ex_date + 30` calendar days, exposed as `ex_date_plus_30d`. 4. **Average cost** — realized P&L uses weighted average cost. 5. **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`).