Owner: a stock with a NEGATIVE overall (combined) score must not be recommended
in the profit buckets, even with positive momentum + theme. BGRIM/TTB had
combined -0.029/-0.066 yet still landed in bucket 1.
- allocate_capital profit_pool now gates on combined_score > 0 (in addition to
momentum>0 + theme>0), still ranking by momentum within that pool.
- Verified: bucket 1 now picks PTTGC (+0.209, mom +1.85) instead of BGRIM/TTB;
BANPU in bucket 2; ADVANC/SCB/LH in bucket 3 by yield. Full suite 377 green.
Owner: momentum must enter the formula (a factor), not be bolted on outside it.
- Single declarative source _SIAMCHART_WEIGHTS = {eps_growth:1.5, dividend_yield:2.0, momentum:0.5}.
- New siamchart_raw_score(g,d,m) = single source of the formula; momentum is an
explicit term inside it. All 3 call sites (build_siamchart_score + both
symbol_breakdown spots) now share it — no duplicated arithmetic.
- Pure refactor: outputs unchanged (weights identical). Tests added for the
momentum-inside-formula rule + momentum raising the score. Full suite 376 green.
- load_price_snapshot picked the last snapshot by filename (lexicographic),
selecting a stale 9-symbol collection over the full 50-symbol universe. Now
picks the snapshot with the latest source.retrieved_at.
- allocate_capital profit buckets now also require momentum > 0 (a falling-price
name is not 'ทำกำไร'), while momentum/theme_signal stay Optional so the PIT
backtest path (which doesn't provide them) still allocates.
- Suggestion now allocates across all 50 SET50 names (B1: BGRIM,TTB; B2: BANPU;
B3: ADVANC,SCB,LH).
- Regression tests for both. Full suite 374 green.
Owner rule: a stock that should be bought for profit is one whose PRICE is
likely to rise in the next 3-6 months — not one with high EPS growth (BTS had
EPS +137% yet flat/falling price). The old selection ranked buckets 1/2 by
(60/40 theme+siamchart where siamchart was EPS-growth dominated).
- themes.price_trend_score(): blend of ~3/6/12-month price momentum, z-scored
across the universe (heavier 3/6m weight per the 3-6 month tenure).
- allocate_capital: buckets 1/2 rank by momentum, gated on theme_signal > 0
(mean surprise across the symbol's themes). theme_signal=None (backtest path)
is not gated so PIT backtest still allocates. Bucket 3 unchanged (yield top).
- suggestion endpoint passes real momentum + theme_signal from the live board.
- Verified: bucket 1 now picks CRC/BEM (dividend + rising price); falling-price
PTT/MINT go to bucket 3 by yield, not bucket 1. Full suite 372 green (3 new
momentum/theme-gate tests).
Owner's rule: backtest must run as soon as there's enough data to estimate an
investment — it must NOT be blocked just because some sources lack deep PIT
history. Scoring is deliberately flexible (a theme uses whatever subset of
factors was knowable that day).
- backtest_readiness: readiness = usable window (price + Siamchart + >=1 factor),
not all-factors-present. Missing factors still reported (transparency) but no
longer block the run. recommended_start = oldest executable price (oldest
history the system holds); recommended_end = last complete trading day.
- pit_scorer.theme_surprise_report: flexible — skips factors not released by
as_of; blocked only when NO factor has a value. pit_meta.partial_pit reflects
themes scored from a partial factor subset.
- Verified end-to-end: readiness ready=true (recommended 2024-01-03 -> 2026-08-29);
POST /api/v1/backtest/run default window returns 201 full result (1M -> final
equity 1,117,243.95), no 400 from missing factors.
- test_backtest_readiness updated to earliest-runnable semantics; full suite 369 green.
Q2 flexible scoring: _fetch_with_cache now degrades instead of raising
DashboardError — a source that fails with no cached value returns {} so the
theme scorer drops that source's factors; a previously-good value is kept as
stale by the daily cache. Verified: all-sources-down still builds 13 themes.
Q3 per-source audit: new themes.factor_source_breakdown(fetched, theme) shows
per factor source/raw/normalized/weight/contribution; dashboard exposes
fetch_data + factor_sources; per-symbol modal renders symbolDetail.factor_sources
(e.g. retail: te_thailand ยอดขายปลีก -14.5 -> -1.0 x 0.7 = -0.7).
Suite 369 green; independent review passed: true.
Q1 (HAR for deferred sources) spike recorded: method works, REIC needs deeper
interaction; NBTC 403 likely unbpassable without a session.