# Fund discovery research log Running log of attempts, dead ends, and learnings for finding candidate funds (alpha-driven, portfolio-complementing) with the `fundlab` pipeline. Newest entries at the bottom of each section. Update as you go — this file IS the knowledge base. ## Sources & what works | Source | Status | Notes | |---|---|---| | Local stocks DB (`~/prog/fin/stocks/`, 8,250 syms) | ✅ primary | ~100 open-end alt families already present; `dbmine.py` mines by name pattern | | SEC full-index `Archives/edgar/full-index/YYYY/QTRn/company.gz` | ✅ **discovery workhorse** | lists EVERY filing; 497/497K filers = all active open-end funds. 4-quarter union = 1,668 CIKs, 33,188 series, 10,372 class tickers | | SEC full-submission `.txt` (per accession) | ✅ | ~10–50KB; line-based SGML cover with `` (UNCLOSED tag) + `` per class; often several series per filing | | Yahoo chart API (`query1.../v8/finance/chart/`) | ✅ | no crumb needed; meta has instrumentType/exchange/longName; `range=20y` gives history length | | `goget` (`~/go/bin/goget`) | ✅ | batch downloader, idempotent, ~2–5s/sym | | EDGAR FTS (`efts.sec.gov/LATEST/search-index`) | ⚠️ fragile | phrase queries w/ hyphens fail; AND-semantics; 100-hit cap per query → incomplete for common phrases. OK for rare phrases only | | SEC `browse-edgar` company listing | ❌ dead | now JS-rendered, no data in HTML | | SEC `/files/investment-company-tickers.json` | ❌ doesn't exist | 404 (misremembered) | | SEC `company_tickers.json` | ❌ for our purpose | only exchange-listed (ETFs/CEFs/stocks); open-end OTC funds absent | | Yahoo search/crumb API | ❌ throttled | IP-level "Too Many Requests" on `fc.yahoo.com`/`getcrumb`; chart API unaffected | | stockanalysis.com/funds/ | ❌ 404 | path guessed wrong, not pursued | ## Learnings (hard-won) 1. **Famous multi-strategy/macro funds are private/offshore** — Millennium, Balyasny, Schonfeld, ExodusPoint, Two Sigma, Winton, Marshall Wace, Brevan Howard, AQR Event-Driven: no US open-end class, no EDGAR 497, no Yahoo OTC ticker. Structural, not a search failure. 2. **Precision over recall for name→ticker resolution**: a guessed ticker that "looks right" is worse than no answer. Chart-API name gate + 2/3 token overlap rejected 23/24 memory-based guesses. 3. **Yahoo exchange name is a USELESS fund/ETF discriminator**: OTC mutual funds report `fullExchangeName="Nasdaq"`. Use `instrumentType` (MUTUALFUND vs ETF). 4. **497 SGML cover uses UNCLOSED tags, one per line** — `Foo Fund\nTNMAX\n`. Closed-tag regexes find nothing; `to_text()` (tag stripping) destroys the data. Parse line-based, BEFORE any tag stripping. 5. **Full-index columns drift** — don't trust fixed widths; the header line and data rows don't align. Regex the whole line. 6. **One quarter ≠ the universe**: each fund's annual base-497 re-filing lands in a random quarter; union of 4 consecutive quarters is the full active universe (1,209 in Q2 alone → 1,668 union). 7. **Accession paths in the index are relative to `/Archives/`**, not `/Archives/edgar/data/` — doubling the prefix 404s. 8. **Amendments (497A/497VPU) may lack the series cover** — base 497 / 497K carry it. (497A inclusion pending — see below.) 9. **Family CIKs repeat across the index** (e.g. AB under 2 CIKs, same series listed twice) — dedupe by series name, not CIK. 10. **Large-n BIC is knife-edge** (ΔBIC=2 ≈ ΔR²=0.0008 at n=2500) — the |t|>2 gate on added regressors is essential (decompose.py). 11. **The portfolio is 50% market-neutral (qspnx)** — MN/L-S-equity funds show the strongest alpha on screen (+12–17%/yr) but corr 0.35–0.76 with the portfolio. "Alpha" ≠ "diversifying for YOU". 12. **Near-duplicate sleeves make OLS knife-edge** — distinct-axis candidate sets (one rep per sleeve family) or the betas split arbitrarily between ivv/vti/vt. 13. **Wrong-fund objectives are worse than none** — all EDGAR resolution stages gate on name match before accepting. ## Pipeline stages (current) ``` full-index (4 qtrs) → per-CIK latest 497/497K .txt (cached, 4-thread) → parse_cover (line-based SGML) → [name filter — REMOVED in v2, kept as a tag] → Yahoo chart verify (instrumentType, 20y length) → share-class dedupe (longest history) → goget missing (batched) → screen_fund (sleeve OLS, BIC fwd-select, 5y alpha t, persistence, corr vs portfolio & benchmark) [0.3s/fund] → search_*.json → app Fund Lab "Alpha search" table ``` ## Overnight comprehensive screen (v2, started 2026-08-26) Goal: screen EVERY OTC open-end fund with ≥5y history from the 497 universe — no name pre-filter. Name match becomes a tag/cross-check, not a gate. ### Notes while running (live - the log() below appends automatically) - [x] survey: 10,372 class tickers in covers cache; 1,223 already local, 9,149 to verify on Yahoo; screen costs 0.3s/fund. - [x] 497A adds ZERO new CIKs over 497/497K (every 497A filer also filed a base in window) - no need to include. - [x] verify: 10,260/10,372 tickers with data (4-thread, ~10 min; 112 dead/never-listed tickers dropped). Cached in universe_cache/yahoo_meta.json. - [x] select: **2,384 funds** (407 local, 1,977 external). Only 54 match the alpha name pattern - the other 2,330 are exactly what the v1 name-filter would have missed. Worklist cached in universe_cache/selected.json. - [x] download: 1,960 symbols (resumed at 1,823 after the watchdog kill-test). Logs to fundlab/overnight.log + this file. - [x] screen: 2,384 funds -> search_all.json (1.2MB). - [x] finalize: verdict counts + "candidates v1 would have missed". ### RESULTS (run finished 2026-08-27 03:23, 5.2h, watchdog exited clean) - 2,384 screened: 250 CANDIDATE, 1336 sleeve mix, 707 weak, 42 alpha-but-correlated, 32 not-persistent, 17 no-5y-window. - Of the 250: ~37 are short-duration/floating-rate/money-market CARRY (model artifact - the 21-sleeve set has no short-duration axis, so carry shows up as a positive intercept). The rest mix: a) GENUINE idiosyncratic alpha: hmezx NexPoint Merger Arb (t5 7.1, tF 4.6, corr 0.14), mervx The Merger Fund, egrix Eaton Vance Global Macro (R2 0.07, +7.9%), wmnux Westwood Alt Income, dmszx Destinations Multi-Strat, anglx Angel Oak Multi-Strat, aguax Am Beacon Dev World Income (+6.5%, t5 3.7), femdx Franklin EM Debt Opps (+6.0%), rctix River Canyon TR Bond (t5 5.6), kmdnx Kinetics Multi-Disciplinary. b) MISSING-FACTOR exposures (sector not in the 21 sleeves, so sector exposure reads as "alpha"): munis (hicox, fhmix), preferreds (lpxax, dpiax), EM debt (femdx), securitized credit (scfzx), TIPS/real-return. - ~200 candidates have "boring" names the v1 alpha-name filter would have rejected - direct quantification of the v1 blind spot. - eniax SIIT Opportunistic Income: t5 10.1 but tF 1.44 -> alpha is RECENT (last 5y), not full-history. Good thing the screen keeps both stats. ### Next iterations 1. [x] **Add missing factors + cluster by return driver** (fundlab/factors.py + fundlab/cluster.py). 2. [x] **N-PORT holdings cross-check on the top candidates** (fundlab/xcheck.py) - results below. 3. [x] **Drawdown-resilience screen** (fundlab/drawdown.py) - which candidates were positive when equities crashed. 4. CEF universe (485/N-2 filers) - separate pass; CEFs have premium/discount dynamics the NAV screen can't see. ### Price-appreciation vs. payout split (fundlab/taxsplit.py, 2026-08-27) Follow-up to the tax-location plan: the user pointed out that fund NAV appreciation is ALSO a capital gain (LTCG on a >1y sale) - the taxplan score only measured DISTRIBUTION character. The fund price files carry both series (Close = raw NAV with distributions paid out; Adj Close = total return reinvested), so the split is computable directly: price appreciation = f(Close) -> investor's own gain on sale payout component = f(Adj)-f(Close) -> taxed every year FINDINGS (5y windows): - ACCUMULATORS (>=50% of return is appreciation -> upgraded to "TAXABLE (accrues)" in taxplan): MBXIX 76% (0.0% payout 12m), ATESX 66%, LAMHX 62%, CVSIX 61%, and candidate PBAIX 60% (0.0% payout 12m). For these the taxable account's LTCG-on-sale benefit is real and large - the annual drag is tiny. - PAY-OUT funds (return comes almost entirely as distributions): HMEZX (99% paid out - the STCG merger-arb case), MERVX, COSIX, PMORX, SVARX, SCFZX, DMSZX, all munis, EM/HY credit. IRA stands. - DATA ARTIFACTS caught: JLPSX & QSPNX show one-time NAV gap events ~2022 (special distribution or share-class reorg) - the 5y payout average (13%/yr) is misleading; the 12m payout column (0-3%) reflects current behavior. QCMMRX (MMF) price data is a total-return index, not NAV - its "100% appreciation" is meaningless. Hence both the 5y and the 12m payout are shown. CAPITAL-LOSS DISTRIBUTIONS: registered US RICs CANNOT distribute net realized capital losses to shareholders (code) - losses stay in the fund as a carryforward. The taxable-account benefit the user was looking for exists as the fund's HARVEST RESERVE: a fund that realized losses in down years (2022/2025) offsets FUTURE gain distributions, so later appreciation converts to distributions without triggering a gain distribution. Detectable only from the annual report (N-CSR) / 1099 history: low-or-zero capital-gain distributions after strong up-years. K-1 vehicles (MLPs, some private credit) DO pass through losses, but they're ordinary-income vehicles -> IRA anyway. NEXT: pull N-CSR per-share distribution tables for the MIXED funds (PMAIX/PMFKX, QSPNX, MBXIX, EGRIX, DMSZX, PBAIX, FCVSX/FISCX, MMUFX/PRUAX, PABAX, WISEX). ### Tax-location plan (fundlab/taxplan.py, 2026-08-27) User's premise: current LTCG rate < future ordinary rate, so a fund's account placement follows the CHARACTER of its distributions: - qualified dividends + LTCG -> TAXABLE (low LTCG rate is the benefit) - tax-exempt (munis) -> TAXABLE (wasted in an IRA) - ordinary interest / STCG / REIT / K-1 -> IRA (deferral is the benefit) - cash -> FLEXIBLE No 1099-DIV characterizations on file for 2,400 funds, so this is a STRUCTURAL estimate. `score` = estimated share of distributions that are tax-favorable (QD + LTCG + tax-exempt), from three tiers of ground truth: 1. 16-fund shortlist -> nport_cache buckets (keyword) 2. 22 cross-checked -> xcheck_report buckets (SEC assetCat/issuerCat) 3. 250 candidates -> factor sleeve loadings (return proxy) Fallback: if the keyword parser left >50% of a book unclassified ("Other"), use the return sleeves. Manual override for the Leuthold wrappers (no return history). Location bands: score >= 0.60 TAXABLE, <= 0.35 IRA, else MIXED (check the 1099). Name-based overrides: muni name -> TAXABLE (munis), money market -> FLEXIBLE, and strategy caps (merger-arb/event-driven capped at 0.35 because gains are mostly SHORT-TERM; market-neutral 0.35; hedge 0.50; style-premia 0.50). KEY FINDINGS: - 16 shortlist: TAXABLE = ATESX, JLPSX, LAMHX, LCORX, LCRIX (all equity). IRA = ATRFX, COSIX, CVSIX, PMORX, SVARX, EAGMX/EGRSX (macro/market-neutral/income). MIXED (check 1099) = MBXIX (hedge), QSPNX (AQR factor), PMAIX/PMFKX (multi-asset income, same fund two classes). - 22 cross-checked: the four MUNIS (BTMIX, FHMIX, HICOX, USMSX) -> TAXABLE, everything else IRA except the macro FOFs (EGRIX MIXED, ETSIX IRA) and DMSZX (MIXED, 38% equity + 36% CLO). - MERGER-ARB TRAP: HMEZX + MERVX hold ~75% equity (looks tax- efficient) but their distributions are mostly SHORT-TERM gains (deals close <1 yr) -> IRA, not taxable. This is the one place the equity-looking book is misleading. - 250 candidates: 109 munis (TAXABLE), 127 IRA (bonds/credit/HY/loans), 6 equity TAXABLE (PHSTX, ANNPX, FKUTX, ALGRX, EBSAX, MCOAX), 7 MIXED, 1 FLEXIBLE. The candidate pool is credit-heavy, so most are IRA. App: Fund Lab -> "Tax location" expander (shortlist + cross-checked + 250-candidate tables, location filter). Output: fundlab/taxplan_results.json. The last 1099-DIV is the final arbiter for any MIXED fund. ### Drawdown-resilience screen (fundlab/drawdown.py, 2026-08-27) Scenarios DETECTED from IVV (S&P 500) - one worst peak->trough per calendar year since 2022, min depth 8% (2024's Aug-5 dip and 2023's rate shock are just under 10%, so a 10% floor would silently drop them): - 2022 bear mkt 2022-01-03 -> 2022-10-12 -24.5% - 2023 rate shock 2023-07-31 -> 2023-10-27 -9.9% - 2024 vol spike 2024-07-16 -> 2024-08-05 -8.4% - 2025 tariff crash 2025-02-19 -> 2025-04-08 -18.8% - 2026 Q1 drawdown 2026-01-28 -> 2026-03-30 -8.9% Fund return = its own NAV, first print after the peak to the last print on/before the trough (per-fund dates, no reindexing). 2,384 funds screened; the 250 CANDIDATEs ranked by # scenarios positive. FINDINGS: - Positive in ALL 5: only 7 funds, and ALL are ultra-short/cash (BILS, QCMMRX, PULS, FHCOX, FHMIX, SAFEX, COIAX). Being positive through every equity drawdown is mostly a DURATION property, not alpha - the honest read of the 5/5 tier. - The interesting tier is 4/5 WITH real 5y alpha: - HMEZX merger arb +1.5% (2022) +3.1% (2023) +0.1% (2024) -0.4% (2025) +0.4% (2026), t5 +7.1, corr +0.14 - the standout: genuinely positive in the two biggest equity crashes. - MERVX merger arb +0.2/+2.6/0.0/+0.5/+0.4, t5 +2.7, corr +0.19. - CBHCX market-neutral -5.4 (2022) but +3.1 (2023) +4.5 (2026), t5 +2.4 - a true equity hedge. - SCFZX securitized credit -2.6 (2022) then ~flat/small, t5 +8.4, corr +0.16. - ENIAX SIIT opportunistic t5 +10.1 (highest alpha in the set), only small 2025 dip. - WMNUX -2.6 (2022) then ~flat, t5 +6.9. - RCTIX -5.6 (2022, its one weak spot) then positive x4, t5 +5.6. - EBSAX Campbell Systematic Macro: +35.9% in the 2022 bear market, +5.0% in 2026 Q1, but -4.1 (2024) -2.7 (2025) - a 2022/2026 macro winner, 3/5. App: Fund Lab -> "Drawdown resilience" expander (scenario table + candidate table sorted by # positive). Output: fundlab/drawdown_results.json. ### N-PORT cross-check (fundlab/xcheck.py, 2026-08-27) 21 of 22 top candidates resolved to their ACTUAL holdings (qcmmrx = money-market account, no holdings to parse). Pipeline hard-won facts: - The fund's NPORT is usually filed under the fund's OWN registrant (browse-edgar ticker->CIK), not the 497-cover family/trust CIK (EV: trust 1552324 vs fund 745463). covers.json of the own CIK gives the exact series name -> the ONLY reliable disambiguator between sibling funds ("...Absolute Return Fund" vs "...Advantage Fund" share 6 of 7 words; token-overlap alone can't tell them apart). - Big trusts file dozens of NPORT-Ps per quarter (one per fund); a flat newest-30 window misses the fund's own filing. Take all filings on the 4 most recent distinct dates. - The submissions API points at the XSL-RENDERED view (xslFormNPORT-P_X01/primary_doc.xml, a 5-20MB HTML page); the raw schema data (seriesName/invstOrSec/netAssets) sits at accession-root primary_doc.xml (5KB-1.5MB). Strip the xsl prefix. - The raw file is malformed XHTML (CSS in