Close the last deferred PIT milestone by collecting REAL per-stock dated
dividend cash-flow history from Siamchart, upgrading the dividend ledger
from DPS estimates to dated_ledger.
- backend/app/siamchart.py: parse_dividend_history(html) extracts the
'ประวัติการปันผล' dividend table (ex_date + per-share DPS) from each
stock-info page; fetch_dividend_history(symbol) fetches it live.
- backend/app/dividend_ledger.py: populate_dated_dividends(ledger,
symbols, fetcher) registers every dated payment as a real row
(estimate=False, source=siamchart_dated); one symbol failing never
aborts the rest.
- backend/app/__init__.py: DividendLedger persisted at
data/dividends/ledger.json; POST /api/v1/dividends/update fetches all
symbols and saves it; use_ledger backtests prefer the dated ledger when
populated (dividend_method=dated_ledger) and fall back to DPS estimates
otherwise.
- tests: parser (4) + populate (2) — full backend 292 passed.
Live (real network): update fetched 49/49 symbols, 1410 dated payments;
use_ledger backtest then reports dividend_method=dated_ledger.
The 'eval(' static-scan hit is ast.literal_eval (safe literal parse, no
code execution), not eval().
Replace the single final-holdings yield proxy with a per-symbol dated
dividend ledger for the backtest engine:
- backend/app/dividend_ledger.py: DividendLedger store (ex_date,
record_date, pay_date, per_share, source, estimate flag) with validation
and persistence; credit_dividends credits per_share * qty once a payment is
due (on/after ex-date and pay date); build_dps_ledger builds estimate rows
from siamchart ratios.DPS (per-share, price-independent) as a step up from
the yield-percentage proxy.
- backend/app/backtest.py: run_backtest accepts dividend_ledger; when set,
dividend_income comes from the ledger and dividend_method reports
'dated_ledger' (real rows) or 'dps_annual_proxy' (estimate). No ledger ->
legacy final_holdings_yield_proxy preserved and labelled.
- backend/app/__init__.py: /api/v1/backtest accepts use_ledger, wiring the
DPS-built ledger.
- tests: ledger store/credit (9) + backtest ledger integration (2 new) —
full backend suite 266 passed. Live probe: use_ledger flips dividend_method
to dps_annual_proxy with per-share income (4151.0) vs proxy (5041.96).
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 when present.