Information analysis
The rest of the spine assumes an information coefficient and predicts an
information ratio. src/analytics/information.py (+ services.compute_information)
measures them and confronts the prediction with reality — the most honest
diagnostic in the system.
A backtest tells you what happened. Information analysis tells you why, and whether it was skill or luck — the only thing that predicts whether it recurs.
A measurement, never a control input to the order path. The measured IC is surfaced as a recommendation for alpha scaling that a human applies.
Information coefficient
At each rebalance t, the IC is the cross-sectional correlation between the alpha
known at t and the realized residual return over (t, t+horizon]:
IC_t = corr( α_{·,t} , r^res_{·,t+1} )
Two disciplines are load-bearing:
- Residual, not raw, return. Correlating alpha with raw forward return rewards
beta, not skill — so the benchmark/
βcomponent is stripped first, consistent with how the alpha was neutralized. - Strict forward alignment.
IC_tpairsαattwith returns realized aftert. An off-by-one (usingr_tinstead ofr_{t+1}) is a leak that manufactures spectacular fake IC — a regression test pins that the IC function is alignment-sensitive.
The summary is mean_IC, IC_vol, and the honesty gate IC_tstat = mean_IC / (IC_vol/√P): a fine mean IC with a t-stat below ~2 is a few lucky
periods, not skill. Both Pearson and rank-IC are reported.
Breadth — not the name count
predicted_IR = mean_IC · √BR_eff
BR_eff ≈ (rebalances/yr) · N / (1 + (N−1)·ρ̄)
BR_naive = (rebalances/yr)·N treats every name as an independent bet. BR_eff
deflates it by the average pairwise correlation ρ̄ (taken from the risk model's
Σ): perfectly correlated bets (ρ̄→1) collapse to one bet per rebalance. Reporting
BR_eff vs BR_naive exposes the single most common self-deception in quant —
thinking you have 500 bets when you have 3.
The reconciliation
The report puts the predicted IR next to a realized IR — computed from the actual period returns of the standardized-alpha portfolio over the same forward windows — so the gap is visible. Against it sit the research-integrity guardrails:
IR standard error SE(IR) ≈ √((1 + IR²/2) / T_years)
multiple-testing P(any |t| > 2 in m trials) = 1 − 0.95^m
sanity ceiling realized IR > 2 on public data ⇒ suspect a bug/leak
A 3-year backtest has SE(IR) ≈ 0.6, so an IR of 0.5 is statistically
indistinguishable from 0 — the report shows IR with that band, not as a point
estimate, and counts how many configs were tried (--n-trials) so one lucky backtest
doesn't read as skill.
Attribution and capacity
With the factor risk model and cost model in place, two more diagnostics close the loop:
- Factor vs specific attribution. At each rebalance the realized return
cross-section is projected onto the factor exposures; the portfolio's return splits
into a factor part (
w·fitted) and a specific part (w·(R−fitted)) that sum to it exactly. This answers "was my return from cheap factor tilts (momentum, size) or genuine name selection?" — and checks whether 005's neutralization actually worked. Reported asfactor_return/specific_returnper rebalance. - Capacity. As capital scales, square-root impact cost grows ∝ √capital, so the net
alpha is monotone-decreasing in capital.
construct_portfolioreports thecapacity_capital— the size at which net active return crosses zero — by bisection over the proposed portfolio's impact cost.
Two more ride the same report:
- IC-uncertainty level shrink. The measured IC is itself estimated; the report
echoes
level_shrink_factor=g/(g+1)withg = T_eff·IC²— the fraction of the naive alpha level that survives that estimation error — plus the shrunk IC to deploy.T_effdeflates the rebalance count for horizon overlap. See the forecast-refinement-v2 section. - Equal-risk-contribution monitor.
risk_bucket_diagnosticbuckets the paper active book by residual vol and flags a monotone variance-share gradient — the fingerprint of a mis-scaled (wrong-Case) alpha.
Where it runs
services.compute_information scans the window, samples rebalances, measures the IC
series and realized portfolio returns, and assembles the report. The CLI
(python main.py info) and the read-only MCP tool compute_information route through
it. See the usage guide.