Commit Graph

6 Commits

Author SHA1 Message Date
1f5720d7db verify_official: 497/497K forms, per-share layout, candidate re-ranking
- EFTS queries now include 497/497K (many fund families publish their
  per-fund highlights there, not in the consolidated N-CSR) and re-rank
  hits by registrant name match (ticker/brand words), newest first,
  capped at 2 filings per CIK
- new parse_per_share_blocks for the JPMorgan-style 'Per share operating
  performance' table (per-class value blocks; dashes = zero)
- parse_highlights now tolerates row labels split across table cells
  (modernized N-CSRS format, e.g. Calamos 2026)
- region finders: word-flexible name patterns (US vs U.S., class letters),
  self-validating per-share regions (a candidate block must match the
  local series, so a name mention in notes doesn't attribute another
  fund's tables in a combined 58 MB report)
- main() keeps the best result across candidate filings (N-CSRS vs 497
  can round differently) and stops early on 'ok'
- local_series applies the corrections overlay so corrected funds verify
  against their filing

Results: JLPSX and CVSIX now 'ok' (all bounded fiscal years agree with
the official filings); CVSIX also gets a 2023-12-21 0.510 capital-gain
correction. bnd/pmaix still ok (no regression).
2026-08-31 18:10:39 -04:00
6536c9903e Tier-3 verification: cross-check distributions against SEC filings
scripts/verify_official.py locates each fund's latest N-CSR/N-CSRS/10-K/10-Q
via EDGAR full-text search (full fund-name phrase first, then ticker +
name words, then bare ticker), extracts the fund's Financial Highlights
tables (both Vanguard-style and Victory-style layouts, calendar and
non-calendar fiscal years, M/D/YY and month-name headers), matches the
share class by per-share distribution series + NAV magnitude, and
compares per period window against the local Yahoo CSVs (frozen copies
for stale tickers). Per-symbol JSONs + SUMMARY.md land in
reports/xcheck_official/; results are cached per fund.

Run on the 29 curated funds: 8 ok (exact to 3dp, e.g. VTSAX 2021-2026H1),
1 mismatch (CVSIX FY2009: local 1.104 vs official 0.81 - the Yahoo
2008-12-18 row of 0.292 looks spurious), 3 weak-match (uncovered doc
formats, e.g. Leuthold), 17 not-found (mostly ETF families whose
10-K layouts aren't covered yet).
2026-08-31 14:41:03 -04:00
02aa750a06 Freeze stale (Yahoo-empty) tickers: snapshot final series in overrides/frozen/
230 tickers whose Yahoo chart responses now come back without a timestamp
array (terminated/merged funds): goget overwrites the .json on every pass
while ohlc.Conv skips the write, leaving the old CSVs as the last known
series. Snapshot them into overrides/frozen/ (git-tracked, audited in
reports/stale-funds.md) and make data.py prefer the frozen copies and
ignore any future data-root rewrite/delete for those symbols, so the
final series survives future goget runs. The cache manifest now covers
the overrides dir too; incremental refresh skips data-root files of
frozen symbols.
2026-08-31 13:23:24 -04:00
2bbe4e58ec Style-tilt battery + commentary in the fund report pipeline
fundlab/styletilt.py: 22 style/asset sleeves regressed on excess-of-T-bill
returns (full history + 5y); BIC forward selection identifies the tilt
stack; residual-alpha verdict ('factor exposure, not skill' when t<1.75);
data-driven English commentary with sign-specific phrasing. Rendered as a
'Style tilts' block (factor table + prose) in both the app and the HTML
report. All 24 pre-built + 8 ad-hoc fund reports rebuilt.
2026-08-30 20:36:04 -04:00
3fbf332b31 Per-fund report: app Summary page + narrative engine + mix_series beta fix
- fundlab/narrative.py: data-driven English prose per fund (performance,
  drivers tiered by fit, explicit 'what we do NOT know', bottom line)
- fundlab/reportdata.py: static build -> reports/report_data.json
- app.py Fund Lab Summary: at-a-glance table + per-fund expanders
  (narrative, equity curve, period table with fund-ref gap, drivers,
  reference mix, tax, cluster peers)
- fundlab/report.py: narrative in the HTML report; forward-selected
  reference (weak-fit funds anchor to cash); SLEEVE_DESC exposure
  explanations
- BUG: mix_series() never applied the betas (reference curves were raw
  sleeve sums; JLPSX 'reference' +407% vs fund +123%) - fixed and all
  reference curves/tables regenerated
- reports/fund_report.html + report_data.json regenerated
2026-08-30 17:36:58 -04:00
328855a926 Fund report: 11 candidates + 13 shortlist funds, self-contained HTML
fundlab/report.py -> reports/fund_report.html (20 MB, plotly inlined,
opens offline). Per fund: max-history equity curve (fund vs fitted
reference vs IVV); performance table (full/5y/1y, the 5 market
episodes, calendar years) with the fund-minus-reference period-alpha
column; drivers (reference-model R²/alpha/t + 34-sleeve signature +
curated decomposition verdict and N-PORT cross-check notes); the
reference mix explained sleeve-by-sleeve (what each exposure actually
is, plus net-cash/net-levered read); tax character + taxable/IRA
placement; and a peer table of the 4 best funds in the same k=30
return-driver cluster with computed advantages/disadvantages.

Weak-fit (R²<0.5) funds anchor their tables to CASH rather than the
statistically-thin forward-selected mix (which can be an offsetting
VIX/duration spec combination whose path is meaningless); the loadings
are still shown with a 'weak fit' caveat.
2026-08-30 17:00:41 -04:00