factors.py: full OLS of all 2,384 funds on a 35-driver basis
(overinclusive, no portfolio-corr screening - corr is a replacement
signal, not a rejection). v1's 21 + lqd/hyg/prefs/emb/tip/shy/vtv/
8 sectors/CTA/commodities. Basis fixes: drop vea/vug (dupes of
efa/qqq), drop finux (TERMINATED 2017 - silently zeroed the
complete-case mask; v1 forward selection never hit this), drop bil
(shv/bil near-null -> offsetting shv+64/bil-54 noise fits),
residualize vblix on ivv+tlt (pure vol axis), ridge 0.02.
cluster.py: hierarchical tree saved but fixed-k cuts degenerate
(most funds are blends -> one 2250-fund blob); k-means++ (deterministic)
is the useful grouping, re-run live in-app for any k.
app: Return-driver clusters expander (k slider 10-60, summary table,
member table sorted by alpha-t). Findings at k=30 in RESEARCH.md.
fundlab/RESEARCH.md - running research log: sources that work/die
(full-index = discovery workhorse; browse-edgar JS-dead;
investment-company-tickers.json nonexistent; company_tickers.json
useless for OTC; Yahoo crumb throttled but chart API fine), 13
hard-won learnings (OTC funds report exchange 'Nasdaq' -> use
instrumentType; 497 SGML cover uses UNCLOSED line-based tags ->
parse before tag-stripping; full-index columns drift -> regex the
line; one quarter != universe -> 4-qtr union; accession paths
relative to /Archives/ not /Archives/edgar/data/; family CIKs
repeat -> dedupe by series name; portfolio is 50% MN so MN alpha
funds are 'correlated', not diversifying).
fundlab/overnight.py - resumable all-stage pipeline (kill/restart safe):
verify (Yahoo chart per non-local ticker, 4-thread, 429 backoff,
local tickers measured from CSV row counts) -> select (pure
select_rows: MUTUALFUND, >=5y, one longest-history class per series
name, alpha_name as TAG not filter) -> download (goget in 200-sym
batches) -> screen (streamed, skip-already-done) -> finalize
(verdict counts + candidates the v1 name-filter would have missed).
Universe: 10,372 class tickers -> 10,260 verified -> 2,384 funds
(407 local, 1,977 external; only 54 match the alpha name pattern -
the v2 point is to screen the other 2,330).
app: alpha table now dedupes by sym with search_all.json winning
(comprehensive superset).
tests: select_rows unit tests (ETF drop, short-history drop, class
collapse, name tagging). 70/70 fundlab.