# 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 `<snapshot>.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 `<output>.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.
