Commit Graph

45 Commits

Author SHA1 Message Date
Kunthawat Greethong
54776d5647 fix(suggestion): profit buckets require combined_score > 0 (owner rule)
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.
2026-08-31 19:00:14 +07:00
Kunthawat Greethong
5b8a10c730 refactor(siamchart): momentum is a first-class factor INSIDE the score formula (owner rule)
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.
2026-08-31 18:50:32 +07:00
Kunthawat Greethong
4c32e2b737 fix(suggestion): load full 50-symbol price snapshot + exclude falling-price names from profit buckets
- 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.
2026-08-31 10:56:22 +07:00
Kunthawat Greethong
576d9e31ec feat(suggestion): 'ทำกำไร' = price-trend momentum (3/6/12m) gated on positive theme signal
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).
2026-08-31 10:05:00 +07:00
Kunthawat Greethong
be6a92540c feat(backtest): runnable with partial factor coverage + oldest-history default (owner rule)
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.
2026-08-30 20:27:22 +07:00
Kunthawat Greethong
7f175e5a05 feat(scoring): flexible source fallback (no board crash) + per-source calc audit (Q2, Q3)
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.
2026-08-29 12:15:54 +07:00
Kunthawat Greethong
51400f20c7 docs: record IRPC review verdict (passed) + open no-fixture-fallback tradeoff 2026-08-29 11:14:02 +07:00
Kunthawat Greethong
12b34929d7 feat(factor): add energy_irpc (IRPC net margin) as 2nd Thai refiner signal
- new energy_irpc collector parsing IRPC performance-highlights table
  (net profit/EBITDA/ROE margins, latest period 3M26: +10.27%)
- factor energy_irpc_net_margin (sign +1) wired into refining_energy/
  exploration/utilities, extending the energy theme beyond TOP
- scheduler job + dashboard fetch + sources table row (now 9 sources)
- tests: parse (incl paren-negatives), value-key resolution, direction;
  suite 368 OK. Independent review passed: true
- Phase B feasibility: REIC/EPPO/NBTC/PTTEP are JS-rendered or anti-bot
  (recorded deferred in plan); IRPC was the clean server-rendered win
2026-08-29 11:13:17 +07:00
Kunthawat Greethong
ead9aeb25c chore: pre-existing in-tree work (event-study/research/vintages/prices + migration script + integrity docs)
Committing the prior uncommitted working-tree state that predates this session's
data-source work (was already modified/untracked at session start) so the tree
is clean before push. Includes: event-study + research report integrity/forward
observation work, prices tests, research hash migration script, and the
2026-08-23/24 engineering-log + test-evidence notes. Verified green as part of
the full 362-test suite.
2026-08-29 09:19:24 +07:00
Kunthawat Greethong
dbb787c50a docs: data-source expansion plan + engineering log (te_thailand, sign fix, ui fix) 2026-08-29 09:19:06 +07:00
Kunthawat Greethong
a17a8f3cc4 docs: per-source cadence + source-health log + UI (scheduler) 2026-08-28 11:42:11 +07:00
Kunthawat Greethong
105ab86bf6 docs: record deploy-safe auto PIT vintage collection (scheduler) 2026-08-28 11:26:53 +07:00
Kunthawat Greethong
17158742b7 docs: record cycle-4 passed=true + deferred per-symbol price edge case 2026-08-28 10:42:16 +07:00
Kunthawat Greethong
a69fd884e7 [verified] Task 8 close: fix price-coverage-at-start + engine fail-closed (review cycle-1) + docs (54 tests, cycle-4 passed) 2026-08-28 10:41:54 +07:00
Kunthawat Greethong
91c63ae673 [verified] Task 8: verification evidence + fail-closed look-ahead fix + docs (event-driven backtest) 2026-08-28 10:35:58 +07:00
Kunthawat Greethong
a7daf333b2 docs: record single-container Docker packaging session (2026-08-27) 2026-08-27 13:27:39 +07:00
Kunthawat Greethong
28c111234e docs: record auto-refresh dated dividend ledger session (2026-08-27) 2026-08-27 12:52:10 +07:00
Kunthawat Greethong
5667e96c40 docs: record real dated dividend collector session (2026-08-27) 2026-08-27 12:35:01 +07:00
Kunthawat Greethong
59c97b5139 docs: record factor-learning validation gate session (2026-08-27) 2026-08-27 12:19:50 +07:00
Kunthawat Greethong
9700f6b44f docs: record real forward-test lifecycle session (2026-08-27) 2026-08-27 12:09:22 +07:00
Kunthawat Greethong
5eeaed63ed docs: record PIT siamchart vintage store session (2026-08-27) 2026-08-27 11:55:55 +07:00
Kunthawat Greethong
377771be65 docs: record dated dividend cash-flow ledger session (2026-08-27) 2026-08-27 11:48:17 +07:00
Kunthawat Greethong
77978627e2 docs: record PIT factor store + partial score provider (2026-08-27)
Append the PIT-work session to HANDOFF and engineering-log: factor
vintages store, partial PIT score provider, honest leakage gating, /
api/v1/backtest use_pit wiring, 255-test verification, and the honest
scope (no pre-2026-08-27 factor history; siamchart fundamental partial).
2026-08-27 09:27:32 +07:00
Kunthawat Greethong
1b971ac72d [verified] Fix backtest accounting identity + honest UI disclosure
Correct the multi-rebalance backtest accounting so ending wealth is
capital + price_pnl + dividend_income with no double counting:
- price_pnl now measures equity change excluding dividends (was reusing
  ending holdings value as 'price profit')
- dividend proxy is included in final_value and net_return, exposed as
  dividend_method=final_holdings_yield_proxy
- regression tests: flat price => zero price_pnl; flat + dividend =>
  dividend-only return; rising no-dividend => correct bucket P&L;
  multi-rebalance accounting identity
- UI (result card + saved-run history) labels dividends as ประมาณการปันผล
  (Proxy) and shows descriptive non-PIT badge when leakage_guard=false

Backend 239 tests passed; targeted backtest 11 passed; frontend build,
npm audit (0), static scan and diff check passed; fresh independent
review deleg_10918fed passed with empty blocker arrays.
Backtest remains descriptive non-PIT (leakage_guard=false) with the
default current-score scorer.
2026-08-27 09:00:39 +07:00
Kunthawat Greethong
d87a1ada39 [verified] Cross-theme surprise normalization + historical factor store (P4 enabler)
A. Cross-theme comparability:
- compute_theme_surprises now weight-normalizes by total |weight| (weighted
  average), so every theme surprise on same [-1,1] scale regardless of factor
  count/weight (retail 0.189->0.145; auto_credit 1.0->0.64).

B. Historical factor store (enables learning macro/demographic factors):
- New factor_history.py: append-only per-factor JSONL, dedupes unchanged
  values, rejects non-finite, records every FACTORS value each scheduler run.
- scheduler.py: jobs carry fetch_module; refresh_all records factor history
  (non-fatal); added bank_npl job.
- GET /api/v1/learning/factors?min_points= reports n_points/learnable per
  factor so users see when P4 learning unlocks (validated query parsing).
- weight_learning: generic learn_factor_series() aggregator (momentum reuses).

Independent review deleg_5dd358e3 passed=true (empty security/logic arrays);
its two robustness suggestions applied (finite guard in record(), clean 400 on
bad min_points). 234 tests pass; Vite build passes.
2026-08-27 07:32:16 +07:00
Kunthawat Greethong
8db3d48ae2 [verified] P0-B registry-driven scoring + P3 PIT backtest + P4 factor-weight learning
P0-B (registry is the single source of truth for scoring):
- FACTORS now carries center/span normalization spec; unused hand-written
  per-theme surprise blocks in dashboard.py replaced by one registry-driven
  compute_theme_surprises() (themes.py).
- THEMES['banks'] adds bank_npl weight so NPL is genuinely blended.
- factor_value/normalize hardened against NaN/inf (finite guards).
- Board re-ranks (TRUE/GULF up, TOP->3) per registry weights; 3 new tests
  incl. 'changing a registry weight changes output'.

P3 (point-in-time backtest):
- run_backtest is now a real multi-rebalance engine (reallocates every window,
  reconciles holdings, marks to market) instead of allocate-once+break.
- Added leakage_guard (False unless a PIT score_fn is supplied), planned vs
  actual rebalances, and momentum_at() true 12-1 (skips last month, PIT).

P4 (factor-weight learning):
- weight_learning.py: cross-sectional Spearman IC, forward-return builder,
  IC aggregation + t-stat, and apply_weight_update (new = clip(old*(1+shrink*IC))).
- GET /api/v1/learning/momentum endpoint. Live result: momentum IC=0.012
  t=0.132 over 22 periods -> momentum has no reliable predictive power here.
  Macro/demographic factors blocked (no historical factor vintages yet).

Two independent review gates passed (deleg_fe6f45cd, deleg_718218f8): empty
security/logic arrays; their non-blocking suggestions applied (finite guards,
dedupe leakage_guard resolution). 226 tests pass; Vite build passes.
2026-08-27 07:12:18 +07:00
Kunthawat Greethong
325e164dd3 [verified] Fix P1-P2-P5 audit findings: simulation reuses board, source_summary clarity, dead-code removal + conftest
- P1: /api/v1/simulation now uses the canonical board score (default_scores)
  instead of a divergent 3-theme recompute -> 'จำลอง' can't disagree with board
  (live check: sim top pick PTT == top board combined 1.600). Removes binary
  auto/en signs, restores quality+momentum+dividend screen consistency.
- P2: dashboard emits source_summary{factor_keys, rows}; frontend shows
  'N ปัจจัย · M แหล่ง' so the 7-vs-5 count confusion is impossible.
- P5: removed dead themes.list_themes()/Theme/build_theme_scores/_map_index and
  the tests that locked them; added tests/conftest.py so pytest needs no PYTHONPATH.
- docs: audit-and-plan-2026-08-26.md (full P0-P5 plan) + engineering-log entry.
- 203 backend tests pass; Vite build passes. Independent reviewer: no security or
  logic blockers (minor error-leak suggestion applied: 503 message no longer leaks
  exception detail).
2026-08-27 03:21:22 +07:00
Kunthawat Greethong
ef78720d32 [research] Methodology research doc + per-theme data-source map + formula refinement proposals
- Literature: PEAD (Bernard-Thomas 1990, Fink 2021, Livnat-Mendenhall 2006), EM momentum (lower in EM), multi-criteria EM selection, Thai earnings-surprise thesis
- 5 formula refinements (R1-R5, await user OK): earnings goodwill weight, momentum factor, continuous regime gate, earnings-revision, dividend screen
- Per-theme additional source table (banks NPL reuses reportID 794, utilities EPPO, retail consumer-comfort...) registered via declarative FACTORS
2026-08-26 16:01:25 +07:00
Kunthawat Greethong
e2f059cb71 [docs] append dashboard rebuild log 2026-08-25 20:41:24 +07:00
Kunthawat Greethong
1e75377732 [verified] Add BOT auto NPL (credit-quality) factor; deepen auto_credit theme
- auto_npl.py: parse BOT Gross NPLs by business (reportID=794); extract auto loan NPL (20,602 mn THB, 3.95% of NPLs, 2.06% of loans)
- /api/v1/themes now exposes auto_npl_pct + auto_npl_amount alongside car-sales volume
- 3 new tests; full suite 188 OK; live verified (themes shows auto_npl_pct 3.95)
2026-08-25 19:06:48 +07:00
Kunthawat Greethong
4e1190492d [docs] Record multi-theme + simulation milestone; update HANDOFF next action (extend price universe) 2026-08-25 17:02:26 +07:00
Kunthawat Greethong
3f7fccd25b [verified] Add Thai Energy/Refining collector (TOP quarterly financials) replacing EIA US crack spread
- energy_thai.py: scrape Thai Oil (TOP) investor financial-highlights -> quarterly + annual EBITDA/Net Profit/Sales (Million Baht), largest Thai refinery
- Thai-specific factor per user (energy must reflect Thai companies, not US EIA proxy); Krungsri was projection-only, TOP gives real quarterly actuals
- Frequency: quarterly (documented in research note)
- 4 tests; full backend suite 156 OK; compileall ok; static scan clean
2026-08-25 15:13:36 +07:00
Kunthawat Greethong
674dcfd48c [docs] Scope: national-only Thai factors; cancel DLT province scraping; energy = Thai GRM (not US EIA) 2026-08-25 15:03:41 +07:00
Kunthawat Greethong
ae78aa1014 docs: record integrity hardening handoff 2026-08-23 14:58:35 +07:00
Kunthawat Greethong
ba9114d2e8 [verified] bind snapshots to manifests and raw hashes 2026-08-23 14:58:15 +07:00
Kunthawat Greethong
f55ff69c31 [verified] add frozen research runner and durable paper ledger 2026-08-23 14:42:02 +07:00
Kunthawat Greethong
c9932954c2 docs: record price snapshot handoff 2026-08-23 12:38:37 +07:00
Kunthawat Greethong
7f7a6145cd [verified] add SET price snapshot adapter 2026-08-23 12:38:17 +07:00
Kunthawat Greethong
9301c02306 docs: record event-study gate handoff 2026-08-23 12:02:43 +07:00
Kunthawat Greethong
0b47a06238 [verified] add event-study readiness gate 2026-08-23 12:02:22 +07:00
Kunthawat Greethong
91eb83631e docs: record M2 vintage handoff 2026-08-23 11:12:10 +07:00
Kunthawat Greethong
d1ba6efc68 [verified] add vintage collector and point-in-time API 2026-08-23 11:11:53 +07:00
Kunthawat Greethong
cfa75787bb docs: record M1 verified handoff 2026-08-23 10:20:08 +07:00
Kunthawat Greethong
a113a51589 [verified] add BOT tourism source adapter 2026-08-23 10:19:40 +07:00
Kunthawat Greethong
3e978c5948 [verified] build tourism signal dashboard 2026-08-23 07:41:31 +07:00