From 5eeaed63ed88352f69cd386cb612323ba6f8706e Mon Sep 17 00:00:00 2001 From: Kunthawat Greethong Date: Thu, 27 Aug 2026 11:55:55 +0700 Subject: [PATCH] docs: record PIT siamchart vintage store session (2026-08-27) --- docs/HANDOFF.md | 23 +++++++++++++++++++++++ docs/engineering-log.md | 1 + 2 files changed, 24 insertions(+) diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 7c8ca4c..b7c2b6c 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -193,6 +193,29 @@ Implement dated dividend events and a provenance-validated PIT score provider be --- +## Session 2026-08-27 — PIT Siamchart vintage store (verified + pushed) + +**Branch:** main · **HEAD:** 887e9c9 (pushed) + +### Completed this session (verified + pushed) +- **Siamchart vintage store** `backend/app/siamchart_vintages.py`: append-only, hash-chained store of every collected Siamchart fundamental snapshot. `snapshot_at(as_of)` returns the newest snapshot whose `retrieved_at <= as_of` (anti-look-ahead, fail-closed when none). Deduplicates identical persists. +- **Fundamental un-partialed** `backend/app/pit_scorer.py`: `PitScoreProvider` accepts `siamchart_store`; when wired, `siamchart_factor_view(as_of)` reads the snapshot knowable at as_of (`pit_grade='pit'`) instead of the current snapshot (`pit_grade='current'`). `score_board` sets `partial_pit` only when no store is present; theme dimension still fails closed (`pit=false`) unless every theme factor has a released PIT value by as_of. +- **API** `backend/app/__init__.py`: `/api/v1/backtest use_pit` seeds the first vintage from the on-disk snapshot (idempotent) and wires the store. +- **Tests**: store (6) + provider-with-store anti-look-ahead (1) — full backend **273 passed** (was 266). + +### Verified +- Backend full suite **273 passed**; compileall, `git diff --check`, static scan clean. +- Live isolated probe: with siamchart store + one released factor, `score_board` returns `partial_pit=false` (fundamental PIT via store) and `pit=false` with a full `blocked_theme` list (themes still lack PIT releases — correct fail-closed). + +### Honest scope / known limitations +- Snapshot reconstruction is forward-only; no history before the first collected snapshot. +- EPS year-keys inside a snapshot are not tied to calendar years, so EPS growth is latest-vs-prior, not fiscal-year-pinned. + +### Exact next action +Implement a real per-stock **dividend-history collector** (siamchart `/stock-info//`) that appends dated payments into `dividend_ledger` so symbols upgrade from `dps_annual_proxy` to `dated_ledger`. Do not reset or stage unrelated pre-existing working-tree changes. + +--- + ## Session 2026-08-27 — Dated dividend cash-flow ledger (verified + pushed) **Branch:** main · **HEAD:** 5b8b6d1 (pushed `068dff2` + `5b8b6d1`; backend `068dff2`, frontend `5b8b6d1`) diff --git a/docs/engineering-log.md b/docs/engineering-log.md index 387e26d..3692087 100644 --- a/docs/engineering-log.md +++ b/docs/engineering-log.md @@ -100,3 +100,4 @@ - Backtest accounting remediation (2026-08-27): deterministic flat-price tests exposed that ending holdings value was reported as `price_pnl` and the dividend proxy was excluded from final value/net return. The corrected identity is `final_value = capital + price_pnl + dividend_income`; API now emits `dividend_method=final_holdings_yield_proxy`, and both result/history UI paths label the proxy and warn when `leakage_guard=false`. RED failures reproduced `price_pnl=20,000/49,000`; GREEN verification: 239 backend tests, compileall, Vite build, npm audit 0 high-severity vulnerabilities, served-bundle disclosure check, static scan and diff check passed. Fresh final independent review `deleg_10918fed` passed with empty blocker arrays. Evidence: `docs/engineering-log/2026-08-27-backtest-accounting-remediation.md` and `docs/test-evidence/2026-08-27-backtest-accounting-remediation.md`. The dividend model remains a proxy and the default public backtest remains descriptive non-PIT. - 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).