# Broad-universe fip_id IC research (Phase B) **Status:** **Parked / closed for now.** Unconditional fip not green; mom-conditional lead logged; breadth-momentum thesis challenged. No book sim until reopen. **Production impact:** none. Display card remains context-only. No deploy from this work. **Artifacts:** research log + compact reports + env-gated harness hooks; tooling stays for a future reopen. ## Scope - Research only — production universe, gate, scanner, schedule unchanged. - Snapshot: `research.sqlite` (~4,650 tickers = prod + nasdaq_all extend). - Liquid mask: top **1,500** by point-in-time 63d median $vol, price ≥ **$5**/week. - **Completion manifest required:** extender writes `.manifest.json`; breadth runners refuse without a matching complete manifest (see §Race guard). ## Caveats - Survivorship bias (today’s constituents, history backfilled). - IEX volume undercount → relative $vol rank only. - Pool skew: Nasdaq-heavy; missing pure NYSE mid-caps. - Do not mix multi-signal tables across universe baselines. - **Do not cite orphaned 21:14 numbers** (see below). --- ## Fingerprint (505-name prod) | | Expected | Observed | |---|---:|---:| | mean IC | −0.045 | **−0.045** | | t-stat | −2.9 | **−2.91** | | weeks / N / reliable | ≥12 / ~500 / true | 35 / 497.7 / true | **Pass.** Formula + pipeline trustworthy. Residual momentum on the same fingerprint (what the production book ranks on): **IC +0.055 / t +1.98**. --- ## The orphan (21:14) — root cause | Source | fip IC (liquid ~1500) | t | |---|---:|---:| | Orphan run 21:14 (removed from tree; was `fip-breadth-20260718-211440-breadth.json`) | **+0.0575** | **+5.12** | | Single-sourced recompute on complete snapshot (2026-07-19) | **−0.0168** | **−1.85** | That is a **sign disagreement** on the same intended quantity. Method rule: the number you cannot reconcile is the number you cannot use. ### Verdict: orphaned — raced the snapshot build **Not** “orphaned, unexplained.” The mechanism is derivable from the table itself: 1. **Code was not the difference.** Reconcile shows the old harness path and the new shared filter produce **identical** results on current data (−0.0168 / −1.85). The implementation fork is closed. 2. **Data was the difference.** On today’s complete snapshot the liquid mask **binds in 97.1% of weeks** at top-N = 1,500. Dense signals (e.g. `vol_6m`) post-mask at **exactly 1,500**. The orphaned report’s `vol_6m` averaged **~1,475** cross-section — a masked run on complete data cannot do that. At 21:14 the eligible pool was smaller than 1,500 and the mask never bound. 3. **Timeline fits.** Extender fixes landed ~20:32 / 20:34; full fetch takes ~30 minutes; breadth run fired **21:14** against a partially built `research.sqlite`. Every number in that report was computed on an incomplete universe. **Do not cite +0.0575 / t +5.12.** It survived less than six hours of contact with project discipline — that is the system working, not time wasted. The orphan JSON was **deleted from the tree** (still in Git history) so it cannot be re-imported as evidence. **Kept artifacts** | File | Role | |---|---| | `reports/fip-reconcile-20260719-000520.json` | Authoritative single-sourced ICs (compact; membership dumps stripped) | | `reports/fip-breadth-20260718-211440-fingerprint.json` | Prod fingerprint pass | ### Race guard (same class as calendar truncation) | Piece | Behavior | |---|---| | `extend_snapshot_universe.py` | Clears any prior manifest on start; on full completion writes `.manifest.json` with `complete=true`, ticker / OHLCV / rank_only counts, `finished_at`. `--limit` smoke runs write `complete=false`. | | `run_fip_breadth_research.py` / `run_fip_breadth_diagnostics.py` | **Refuse** breadth mode unless a matching complete manifest exists and live counts equal the recorded totals. | Helper: `scripts/research_snapshot_manifest.py`. --- ## Authoritative unconditional liquid fip (post-reconciliation) | metric | value | |---|---:| | mean_ic | **−0.0168** | | ic_t_stat | **−1.85** | | weeks | 35 | | avg_cross_section (**post-mask IC sample**) | 1471.2 | | avg_raw_pool | 3214.4 | | avg_eligible_pre_mask | **2338.4** | | mask_binds_pct | **97.1%** | | reliable | true | **Mask binds hard** on complete data (eligible ≫ 1500). Post-mask IC N for fip is ~1471 because not every liquid name has a valid 12-1 fip path — that is signal availability, not a non-binding mask. Contrast orphan `vol_6m` avg N ~1475 vs complete-data `vol_6m` avg N **1500**. Harness `_signal_evaluation` vs manual IC through the same filter: **exact match** (−0.0168 / −1.85). **Iron rule unconditional:** **not green** (|IC| 0.017 < 0.03), correct mild-negative sign. --- ## Context table (orphaned 21:14 vs authoritative) — kill the myth numbers The context table died with the orphan. **−0.16 must not survive in the log.** | signal (liquid ~1500) | orphaned (21:14) | authoritative (shared filter) | consequence | |---|---:|---:|---| | **vol_6m** | −0.16 / t **−6.1** | **−0.048 / t −1.36** | “High-vol tilt harmful on breadth” **downgrades from finding to directional hypothesis** — not significant | | **raw mom** (`mom_12_1`) | +0.10 / t +4.6 | **+0.046 / t +1.91** | Below iron-rule bar on this pool | | **resid mom** (`mom_12_1_resid`) | +0.04 / t +2.3 | **+0.029 / t +1.33** | Ditto, and weaker than raw | ### Breadth-momentum thesis — challenged That last pair is the sobering one. Momentum on liquid breadth is **marginal**. The “more breadth strengthens the momentum t-stat” thesis that motivated Phase B is **empirically wrong on this pool**: same 35 weeks, triple the names, residual-mom t-stat **fell** versus the 505-name fingerprint (**0.055 / 1.98** → **0.029 / 1.33**). The clean momentum edge lives in the large-cap universe already traded. Meanwhile the strongest reliable signal on liquid breadth is now **mom-conditional fip** (−0.088 / −4.58) — but a fip tilt presupposes a breadth momentum book worth tilting, and that is no longer free. --- ## Compositional story (supported) `fip_id = sign(PRET)×(%neg−%pos)` pools: - **Continuous winners** → want **negative** IC - **Continuous bleeders** → want **positive** IC | check | IC | t | read | |---|---:|---:|---| | Prod-universe subset inside liquid | **−0.044** | **−2.88** | Matches fingerprint → compositional, not regime change | | Tier 1–800 (senior) | **−0.035** | **−2.99** | Winner leg | | Tier 801–1500 (junior) | **+0.014** | +1.25 | More bleeder / junk weight | | Lagged membership (prior-week $vol) | −0.010 | −0.93 | Same sign as same-week; not a +5σ leak artifact | **Do not log “on Nasdaq, jumpy paths outperform.”** That would mythologize an orphaned +0.06. --- ## Platform-relevant test: momentum-conditional fip Among liquid top-1500, keep **mom_12_1 ≥ P80** (~294 names/week): | metric | value | |---|---:| | mean_ic | **−0.0879** | | ic_t_stat | **−4.58** | | ic_positive_pct | 22.9% | | weeks | 35 | | reliable | **true** | Computed on the **same single-sourced path** as the authoritative −0.017. This is the paper’s claim and the only version a gate could consume. | Decision | | |---|---| | Unconditional fip | **Closed** for production | | Mom-conditional fip | **Alive as book-tilt candidate only** — and only after a baseline breadth book proves itself | | Display card | Stays | | Production change | **None** | --- ## Vol-tilt warning (softened) | signal (liquid, single-sourced) | IC | t | |---|---:|---:| | vol_6m | −0.048 | **−1.36** | | mom_12_1 | +0.046 | +1.91 | | mom_12_1_resid | +0.029 | +1.33 | High-vol names **tend** to underperform on this pool relative to a clean S&P-like book — that is a **directional hypothesis**, not a finding. Production **80/20 high-vol tilt** was validated on S&P-like names. If the universe ever broadens in production, re-validate that tilt; do not treat the orphaned −0.16 / t −6.1 as evidence. --- ## What this means for the book experiment A fip tilt presupposes a breadth momentum book worth tilting — **that is no longer free.** **Caution against over-reacting the other way:** modest cross-sectional IC does not preclude a good book. The 505-name book turns resid-mom IC ~0.055 into Sharpe ~2 because the gate trades the **extreme tail**, not the linear sort. The breadth book might still work; it just has to **prove it** before the fip arm means anything. If the baseline cannot clearly beat the existing production book’s territory, fip’s future is a footnote regardless of −4.58. ### Parked next step (if reopened): pre-registered two-arm design Not started — **design only**, pre-register before any sim: | Arm | Definition | |---|---| | **A — baseline** | Top-quintile residual (or raw — pick one and lock) momentum book on liquid-1500; **no fip**; honest costs; next-open or near-close fills; production-like capacity / risk / stops | | **B — +fip tilt** | Same book + mom-conditional fip tilt (among mom winners, prefer smoother paths / negative fip_id) | | Grade on | Spec | |---|---| | Split | Entry-date train / validation (`BACKTEST_HOLDOUT_SPLIT` naming — not pristine holdout) | | Metrics | Sharpe + Mertens/Lo SE, PSR, **DSR**; max DD; turnover; cost drag | | Promote bar | Arm A must be in production-book territory first; Arm B must beat A on validation with DSR-aware multiple-testing honesty | | Fail-closed | If A fails, fip is a footnote; do not shop tilts on a dead baseline | --- ## How to re-run (research branch only) ```powershell # 1) Full extend writes completion manifest (required) .\.venv\Scripts\python.exe scripts\extend_snapshot_universe.py ` --source backtest_snapshots\prod.sqlite ` --output backtest_snapshots\research.sqlite # 2) Breadth / diagnostics refuse without matching manifest .\.venv\Scripts\python.exe scripts\run_fip_breadth_diagnostics.py ` --research-snapshot backtest_snapshots\research.sqlite ` --prod-snapshot backtest_snapshots\prod.sqlite ` --workers 6 --allow-spawn ``` --- ## Bottom line 1. Formal iron-rule screen: **not green** either before or after reconciliation. 2. **+0.0575 / +5.12 is orphaned: raced the snapshot build** — authoritative unconditional liquid fip is **−0.017 / −1.9**; mask binds (~97%) on complete data. 3. Context-table myths die with the orphan: **vol −0.16 is not real**; authoritative vol is **−0.048 / t −1.36** (directional only). 4. Compositional tug-of-war is the right story; jumpiness premium is not. 5. **Breadth does not strengthen residual-mom t-stat** on this pool (0.055/1.98 → 0.029/1.33). 6. **Mom-conditional −0.088 / −4.6 stands** on the single-sourced path → optional next step is a **pre-registered two-arm breadth book** (baseline first), not a gate wire-in. 7. Manifest guard is in place so the race cannot recur silently.