docs: record real forward-test lifecycle session (2026-08-27)

This commit is contained in:
Kunthawat Greethong
2026-08-27 12:09:22 +07:00
parent 80c6d792f4
commit 9700f6b44f
2 changed files with 24 additions and 0 deletions

View File

@@ -193,6 +193,29 @@ Implement dated dividend events and a provenance-validated PIT score provider be
---
## Session 2026-08-27 — Real forward-test lifecycle (verified + pushed)
**Branch:** main · **HEAD:** 80c6d79 (pushed `6d9c283` + `80c6d79`)
### Completed this session (verified + pushed)
- **ForwardTestStore** `backend/app/forward_test.py`: durable, thread-safe JSON store of forward paper runs with an explicit lifecycle — `frozen` (signals snapshotted, immutable) → `executed` (fills 50/20/30 buckets at post-freeze prices) → `marked` (mark-to-market equity series) → `matured` (net_return finalised). Frozen signals are never re-readable/rewritten after creation, so later data cannot retroactively change the run's decision.
- **API** `backend/app/__init__.py`: `GET /api/v1/forward(+<id>)`, `POST /api/v1/forward` (create+execute, `use_pit` freeze option), `POST /<id>/mark`, `POST /<id>/mature`. Store extension backed by `data/forward/runs.json` (survives restarts).
- **Frontend** `frontend/src/App.vue`: simulation tab's Forward mode now calls the real `/api/v1/forward` lifecycle (not the cosmetic `/api/v1/simulation` with a mode string), loads runs on mount, and renders a forward panel with status badge, non-PIT tag, holdings, and Mark / Mature per run.
### Verified
- Backend full suite **280 passed** (was 273); new lifecycle tests (7).
- compileall, `git diff --check`, static scan clean.
- Live route probe (isolated store): create→execute (201, real holdings AWC/CRC/MINT/PTT), list, mark, mature all work; run persists across the probe.
### Honest scope / known limitations
- The score source at CREATE time may be the current board (`non_pit=true`, tagged). A PIT scorer only marks `non_pit=false` when its scores assert `pit_meta.pit=true`.
- Paper-only; never dispatches a real MT5 order.
### Exact next action
Implement the **factor-learning validation gate** (PIT exposures + forward returns + Spearman IC + holdout/walk-forward + baseline, no auto-apply). Do not reset or stage unrelated pre-existing working-tree changes.
---
## Session 2026-08-27 — PIT Siamchart vintage store (verified + pushed)
**Branch:** main · **HEAD:** 887e9c9 (pushed)

View File

@@ -101,3 +101,4 @@
- PIT factor store + partial score provider (2026-08-27, commit `1f630be`): added `factor_vintages.py` (append-only PIT factor store: observed/released/retrieved_at + SHA-256 hash chain; `value_at(as_of)` anti-look-ahead, fail-closed when nothing released by as_of) and `pit_scorer.py` (`PitScoreProvider.score_board(as_of)` + `make_pit_score_fn`). `backtest._resolve_scores` now sets `leakage_guard=True` only when the supplied score_fn asserts `pit_meta.pit=True` (closes "any callable ⇒ PIT"); `/api/v1/backtest` accepts `use_pit`. Empty factor store fail-closes (live probe `leakage_guard=false`). Full backend **255 passed** (was 239). Honest scope: theme dimension PIT from this store forward; no factor history before 2026-08-27 so pre-today backtests stay non-PIT; siamchart fundamental partial (EPS 5-yr series PIT-grade, ratios current → `partial_pit`). Next: dated dividend cash-flow ledger.
- Dated dividend cash-flow ledger (2026-08-27, commits `068dff2` + `5b8b6d1`): added `dividend_ledger.py` (per-symbol dated store: ex_date/record_date/pay_date/per_share/source/estimate flag; `credit_dividends` credits per_share×qty when payment due, fail-closed with no entry; `build_dps_ledger` builds per-share estimate rows from siamchart ratios.DPS). `run_backtest` accepts `dividend_ledger` and reports `dividend_method` = `dated_ledger` (real rows) | `dps_annual_proxy` (estimate) | `final_holdings_yield_proxy` (legacy). `/api/v1/backtest` accepts `use_ledger`. Frontend maps method to an honest Thai label + Proxy badge on result card and history rows. Full backend **266 passed** (was 255). Honest scope: DPS rows are estimates (no ex-date history in snapshot yet); real dated cash flows require collecting per-stock dividend history, which upgrades a symbol to `dated_ledger`.
- PIT siamchart vintage store (2026-08-27, commit `887e9c9`): added `siamchart_vintages.py` (append-only hash-chained store of every collected siamchart snapshot; `snapshot_at(as_of)` returns the newest snapshot with `retrieved_at <= as_of` — anti-look-ahead, fail-closed). `PitScoreProvider` accepts `siamchart_store`; when wired the fundamental dimension reads the snapshot knowable at as_of (`pit_grade='pit'`) and `partial_pit` no longer forces true. `/api/v1/backtest use_pit` seeds the first vintage from the current snapshot. Full backend **273 passed** (was 266). Honest scope: snapshot reconstruction forward only; EPS year-keys not tied to calendar years (growth = latest-vs-prior).
- Real forward-test lifecycle (2026-08-27, commits `6d9c283` + `80c6d79`): the cosmetic "forward" (same single-pass backtest, different mode string) is replaced with a durable, frozen-signal paper-portfolio lifecycle. `forward_test.py` `ForwardTestStore` (thread-safe JSON store) with status flow: frozen (signals snapshotted immutable) → executed (fills 50/20/30 at post-freeze prices) → marked (mark-to-market equity series) → matured (net_return). New routes: `GET /api/v1/forward(+<id>)`, `POST /api/v1/forward` (create+execute, `use_pit` freeze), `POST /<id>/mark`, `POST /<id>/mature`; store at `data/forward/runs.json` (survives restarts). UI simulation tab: forward calls `/api/v1/forward`, loads runs, shows status/non-PIT/holdings + Mark/Mature per run. Full backend **280 passed** (was 273). Honest scope: score source at CREATE may be current board (`non_pit=true` tagged); paper-only.