Files
signal-platform/docs/research/fip-breadth-ic.md
T
dennisthiessen 2311999e57 research: park Phase B fip breadth; race guard and compact evidence
Log the 21:14 orphan as a snapshot-build race, rewrite the context table to
authoritative ICs only, and soften the vol-tilt warning. Add extender completion
manifest + breadth refuse guard; strip intermediate/orphaned reports; park the
thread (no book sim, no deploy).
2026-07-19 00:32:20 +02:00

11 KiB
Raw Blame History

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 (todays 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 todays 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 reports 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.980.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 1800 (senior) 0.035 2.99 Winner leg
Tier 8011500 (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 papers 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 books territory, fips 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)

# 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.