Why this method matters
RiskModels is more than a descriptive analytics layer. The ERM3 engine is built around a few choices that matter for practical quantitative use:
- Time-safe construction — the engine avoids common forward-contamination errors such as recycled tickers, snapshot shares, and retroactive universe contraction.
- Security Master discipline — ticker-level outputs sit on a point-in-time identity layer that supports symbol changes, classification lookup, and historically defensible shares data.
- Hierarchical structure — market, sector, and subsector are modeled explicitly rather than folded into a single flat factor view.
- Executable hedge ratios — published hedge outputs are designed to work with liquid raw ETFs at trade time, not only with orthogonalized synthetic factors.
- Adjusted return series — split- and dividend-adjusted returns keep the decomposition economically consistent across long horizons and corporate actions.
These choices do not remove normal model risk, but they explain why the model suits backtests, neutralization workflows, and portfolio diagnostics rather than surface-level screening. They are also what make it safe to put behind an app or agent: a structured, time-stamped model to call and cite, rather than risk commentary an LLM has to improvise from memory.
The big picture
What is a hierarchical factor model?
A hierarchical factor model breaks down stock returns into layers of systematic risk, from broad to granular:
- Market (L1):how much does the overall market (S&P 500) explain?
- Sector (L2): how much additional variance comes from industry sectors — technology, financials, healthcare?
- Subsector (L3): how much comes from narrower industries within sectors — semiconductors, biotech?
- Residual: what is left is idiosyncratic risk — the stock-specific component.
At each level the model produces three metrics:
| Label | Name | What it tells you |
|---|---|---|
| HR | Hedge Ratio | Dollars of ETF to trade per $1 of stock to neutralize that factor |
| ER | Explained Risk | Percentage of the stock’s variance explained by that factor |
| RR | Residual Risk | Variance not explained by factors — the idiosyncratic remainder |
Why three levels?
- Three levels align with how institutional investors think: market timing, sector rotation, and stock selection.
- Too many levels lead to overfitting and unstable estimates.
- Too few — just market — miss important sectoral dynamics. A tech stock is not simply “market plus noise.”
Key insight: each level captures incremental explanatory power not already explained by higher levels, achieved through orthogonalization.
One word, two axes: residual risk vs. residual return
“Residual” names the factor-model remainder ε — a daily return series. Two different numbers derive from that one series, and they answer different questions. Reading one as the other is the most common misreading of this model.
| Quantity | Axis | Definition | Question it answers |
|---|---|---|---|
| Residual risk share | Risk (variance) | Var(ε) / Var(r) — a share of variance; sums with the factor ERs to 1.0 | How much of the risk is stock-specific? |
| Residual return | Return (realized) | The compounded ε over a window — a return in percent | How much return did stock selection add? |
A position can carry a high residual risk share and a negative residual return, or the reverse. The two are independent. Both appear in the reconciliation section below for the same name on the same day: Walmart’s residual risk share is 0.98 while its L3 residual return is +0.95% — one is a fraction of variance, the other is a percentage of price. Never read a bare percentage across axes.
One further asymmetry, and it is the reason portfolio numbers look nothing like position numbers: residual risk diversifies away as names are added, because residuals are largely uncorrelated across stocks, while factor risk does not. A book’s residual risk share is therefore far smaller than the average of its holdings’.
Every quantity here is also qualified by level. A column labelled L2 sector HR is the hedge ratio for the sector ETF at level two; L3 subsector ER is the variance explained by the subsector factor; L3 RR is the stock-specific return left after all three levels.
The three levels: L1, L2, L3
The cascade
The core idea: regress, take the residual, regress again. Each level strips out one more layer of systematic risk, and the leftover ε feeds into the next level.
| Step | Regression | What it captures |
|---|---|---|
| L1 | r_s = β_m · r_m + ε₁ | Broad market (SPY) exposure |
| L2 | ε₁ = β_s · r_s* + ε₂ | Sector exposure (incremental to market) |
| L3 | ε₂ = β_u · r_u* + ε₃ | Subsector exposure (incremental to L2) |
Notation: r_s = stock return, r_m = SPY return, r_s* = cleaned sector ETF return, r_u* = cleaned subsector ETF return.
Example: if NVDA has β_m = 1.3 and SPY returns +1%, we expect NVDA to move +1.3% from market exposure alone. The leftover ε₁ goes to L2 (technology, XLK). What remains, ε₂, goes to L3 (semiconductors, SOXX). The final residual ε₃ is the stock-specific return — what the three factor levels do not explain.
Orthogonalization: why we clean the factors
Sector ETFs like XLK are not independent of the market — XLK has its own market beta. Using raw XLK returns at L2 would double-count the market exposure already captured at L1.
The fix: before regressing at each level, strip out higher-level exposures using link betas (λ):
Here λ(A→B) is the beta of ETF A regressed on ETF B (or its cleaned version), precomputed from historical data. This ensures each β captures only the incremental effect of its own level — no double-counting.
Beta estimation: Huber + Vasicek peer adjustment
Each level’s β is fit by a 252-day rolling Huber-M regression on the orthogonalized factor — robust to fat tails (binary events, earnings gaps, single-day reclassifications) without sacrificing efficiency on the bulk of normal trading days.
For L2 (sector) and L3 (subsector) only, the Huber estimate is then Vasicek-shrunktoward a log-mcap-weighted peer mean within the stock’s 4-digit industry cohort:
with α = σ²ᵢ / (σ²ᵢ + τ²), where σ²ᵢ is the per-stock β-noise variance and τ² is the cross-sectional weighted variance across peers. Short-history and small-cap names are pulled more strongly toward the cohort mean (large α); mega-caps with long stable estimates barely move (α ≈ 0).
L1 (market β_m) is not shrunk. Out-of-sample validation against 5,910 stocks over 19 years shows market-β shrinkage actively hurts residual variance for most stocks (a 41% win rate, well below coin-flip). Market β is the most over-determined coefficient in the cascade; pulling it toward peers introduces bias without reducing variance. The unshrunk Huber estimate is preserved for audit and reversibility. Methodology research and out-of-sample evidence: see the ERM3 research notes.
Hedge ratios: making it tradeable
What are hedge ratios?
A hedge ratio (HR) tells you how many dollars of an ETF to trade per $1 of stock position to neutralize a specific factor exposure.
| HR sign | Action | Meaning |
|---|---|---|
| Negative | Short the ETF | Hedge out factor exposure |
| Positive | Long the ETF | Add factor exposure |
L1: market only
If β_m = 1.2, short $1.20 of SPY per $1 long the stock.
L2: market + sector
The sector HR is the direct beta; the market HR is adjusted because shorting the sector ETF also implicitly shorts the market:
L3: market + sector + subsector
The subsector HR is the direct beta; sector and market HRs are further adjusted for the exposure embedded in the subsector ETF:
Consistency check: applied to raw ETF returns, these adjusted HRs satisfy the replication identity — decomposition into factor contributions plus residual reconciles back to the actual stock return. Verified at runtime for every stock, every date.
Explained risk: variance decomposition
What is explained risk?
Explained Risk (ER) measures what percentage of a stock’s return variance comes from factor exposures. It is the R² from the factor regression:
The additive property
Because the factors are orthogonalized, ERs add up perfectly:
This is guaranteed by construction and is verified at runtime to within 0.1% as a data-integrity check.
The guarantee is a projection property, not a convenience: because each level’s exposure is fit by an honest orthogonal regression, the residual is left uncorrelated with every factor, so no variance is double-counted and none leaks into a cross-term. Every percentage point is attributed to exactly one level. Models that estimate exposures by shrinking them toward zero break this closure — the shrunk residual still carries factor variance, so the shares no longer sum to one and a per-factor number stops meaning “the share from this factor.” The decomposition is honest precisely because the betas are not bent to make it look tidy.
Vasicek is not Ridge — do not conflate them. The paragraph above is about shrinkage toward zero, which is what breaks closure. ERM3 does shrink L2 and L3 betas, but via Vasicek toward a log-mcap-weighted peer-cohort mean (empirical Bayes) — it borrows cross-sectional strength while preserving signal, and additivity still holds to the runtime 0.1% tolerance. L1 market β is left unshrunk, because out-of-sample evidence shows shrinking it hurts (41% win rate). ERM3 publishes both the raw projection ER, which is the additive one used for attribution, and a shrunk variant. So “no Ridge” is not “no shrinkage.”
Interpretation
- Low ER (below ~50%): high idiosyncratic risk — more alpha opportunity, or more diversifiable risk.
- High ER (85%+): the stock is mostly a leveraged sector bet — systematic risk dominates.
- If ER(L2) > ER(L1), sector dynamics dominate market timing for this stock.
Putting it together: the replication equation
The model decomposes any stock’s return using only raw ETF returns — no orthogonalization required at trading time:
where HRᵢ is the hedge-trade ratio for ETF i — dollars of ETF i per dollar of stock, and negative for a short — rᵢ is the raw total return of ETF i (not residualized), and ε is the idiosyncratic residual return. The exposure is −HRᵢ; the two forms above are the same statement. This is a mathematical identity, verified at runtime to within 0.1% for every stock on every date.
On sign: HRᵢ is what you trade and what the API returns; −HRᵢ is what the stock is exposed to. Adding HRᵢrᵢ to the stock return removes the factor; adding −HRᵢrᵢ reconstructs it. Do not use one symbol for both.
ε is not small by construction. It is whatever the factor exposures fail to explain, and for genuinely stock-specific names it is routinely the largest single share of variance — see the explained-risk section below, and the live decomposition on the home page, where residual regularly exceeds half of total variance.
Why this matters: you can hedge almost any stock or portfolio using only highly liquid ETFs. No custom baskets, no exotic derivatives — just SPY, sector ETFs, and subsector ETFs.
Multi-period attribution: the geometric bridge
The replication equation decomposes a single day perfectly. Attributing a cumulative return over months or a year needs more care, because daily returns compound multiplicatively, not additively:
Naively summing daily factor contributions over a year overstatesthe compound return. The gap is volatility drag (Jensen’s inequality): for a stock with 39% annualized volatility, the arithmetic sum can diverge from compound gross by roughly 7 percentage points over one year.
The fix exploits the hierarchy directly. At each level, compound returns as if only factors through that level exist, then take telescoping differences between adjacent levels:
| Bar | Formula | What it captures |
|---|---|---|
| Market | prod(L1) − 1 | Compound return from the market factor alone |
| Sector | prod(L2) − prod(L1) | Incremental compound return from adding sector |
| Subsector | prod(L3) − prod(L2) | Incremental compound return from adding subsector |
| Residual | prod(G) − prod(L3) | Compound idiosyncratic return after all factors |
| Gross | prod(G) − 1 | Exact compound gross return |
The four bars sum exactly to the gross compound return — an algebraic identity (intermediate terms cancel), not an approximation.
Key property: the ordering matters. Market → Sector → Subsector → Residual must follow the strict L1 → L2 → L3 hierarchy, mirroring the sequential regression cascade.
Worked example: Walmart (WMT)
Illustrative values, chosen for a clean walk-through of the hedge-ratio construction.
Step 1 — regression betas
| Parameter | ETF | Value |
|---|---|---|
| β_m | SPY | 0.50 |
| β_s | XLP (Consumer Staples) | 0.30 |
| β_u | PBJ (Food & Beverage) | 0.20 |
Step 2 — link betas
| Relationship | Meaning | Value |
|---|---|---|
| XLP → SPY | Consumer Staples’ market beta | 0.60 |
| PBJ → SPY | Food & Beverage’s market beta | 0.40 |
| PBJ → XLP | Food & Beverage’s sector beta | 0.70 |
Step 3 — build hedge ratios (bottom-up: L3 → L2 → L1)
| Level | ETF | Direct beta | Link adjustment | Final HR |
|---|---|---|---|---|
| L1 Market | SPY | 0.50 | +0.176 | −0.324 |
| L2 Sector | XLP | 0.30 | +0.14 | −0.16 |
| L3 Subsector | PBJ | 0.20 | — | −0.20 |
Verification — sample day
With SPY +1.00%, XLP +0.80%, PBJ +1.20%, and WMT +1.10%:
That +0.408% is the residual return — the stock-specific return left after neutralizing every factor exposure. A persistently positive residual is evidence relevant to selection skill; a negative one is underperformance that cannot be blamed on the market. Reading a single residual as alpha skips the part that matters: breadth, controls, and out-of-sample validation.
Reconciliation against live output
The example above uses round numbers so the link-beta mechanics are legible. Round numbers are not evidence, so the identity is also checked against production ERM3 output on every edit to this section. Run it yourself:
RISKMODELS_API_KEY=... python3 scripts/riskmodels/verify_methodology_identity.py WMTWMT, 2026-07-27 — model 3.0, factor set SPY_uni_mc_3000, factors SPY / XLY / XRT. Raw dividend-adjusted returns for the day:
| Series | Return | L3 hedge ratio | Contribution |
|---|---|---|---|
| WMT | +2.073628% | — | — |
| SPY (market) | +0.021653% | +0.367435 | +0.007956% |
| XLY (sector) | +1.307010% | +0.080587 | +0.105320% |
| XRT (subsector) | +2.907712% | −0.415512 | −1.208182% |
| Σ HRᵢrᵢ | −1.094906% | ||
The engine publishes its own residual return for the same day as l3_rr = +0.948911%. Two reconciliations matter here and they answer different questions.
Is the identity right? The engine also publishes its factor contribution, cfr = Σβᵢrᵢ = −Σ HRᵢrᵢ, so the identity can be evaluated entirely in the engine’s own numbers: ε = r_s − cfr. That reconciles to 8.2 × 10⁻¹⁰ at L1 and 1.9 × 10⁻⁹ at L3 — floating-point agreement. The identity is exact.
Can an outside reader rebuild it? Reconstructing Σ HRᵢrᵢ from the published hedge ratios and the published raw ETF total returns agrees to 8.2 × 10⁻¹⁰ at L1, where the hedge is a single SPY leg, and to 3.0 × 10⁻⁴ at L3. The L2 and L3 gap is reconstruction error, not model error: it enters through the ETF return series used for the sector and subsector legs, and it sits inside the 0.1% runtime tolerance this page claims. Both figures are shown below so neither is hidden.
| Level | Published rr | Identity r_s − cfr | Rebuilt r_s + Σ HRᵢrᵢ | Pre-2026-07 form |
|---|---|---|---|---|
| L1 | +2.0751562% | +2.0751563% (8.2 × 10⁻¹⁰) | +2.0751563% (8.2 × 10⁻¹⁰) | +2.0720988% (3.1 × 10⁻⁵) |
| L2 | +1.7759090% | +1.7759092% (1.6 × 10⁻⁹) | +1.7726684% (3.2 × 10⁻⁵) | +2.3745866% (6.0 × 10⁻³) |
| L3 | +0.9489114% | +0.9489116% (1.9 × 10⁻⁹) | +0.9787217% (3.0 × 10⁻⁴) | +3.1685333% (2.2 × 10⁻²) |
Reproducing this exactly.Every number above comes from one endpoint, and it is pinned to a date rather than to “latest”, so it keeps working: GET /ticker-returns?ticker={T}&years={N} returns a daily series in which each row carries returns_gross together with that day’s l1/l2/l3 hedge ratios, explained-risk shares, and cfr. Read the row where date = 2026-07-27 from four calls — WMT, SPY, XLY, XRT — with any years window that spans the date:
curl -H "Authorization: Bearer $RISKMODELS_API_KEY" \
"https://riskmodels.app/api/ticker-returns?ticker=WMT&years=2" \
| jq '.data[] | select(.date=="2026-07-27")'Use GET /metrics/WMT only for the current teo — it returns the latest date, not a historical one, and the per-day values in /ticker-returns agree with it to 10⁻¹⁶ on the overlapping date. The published residual returns quoted above are l1_rr and l3_rr on that same row.
The last column is why the sign convention matters. Writing the identity as r_s − Σ HRᵢrᵢ — as this page did before 2026-07-28 — misses the published residual by 2.2 percentage points at L3, roughly seventy times the error of the correct form. The arithmetic in the worked example was always right; only the stated identity was wrong.
Two things the same check establishes, both load-bearing elsewhere on this page. Explained-risk shares sum to 1.0 to within 6 × 10⁻⁸ at every level on the production, Vasicek-shrunk betas— closure survives peer-mean shrinkage. And WMT’s residual share of variance on this date is 0.98: the residual is not small by construction, it is simply what the factors do not explain.
Choosing a level: the L* rule
L1, L2, and L3 are not stacked layers — each is a standalone hedge solution with its own market/sector/subsector ratios. By construction, total explained risk is monotonic in level: ER(L3) ≥ ER(L2) ≥ ER(L1). So “always pick L3” looks optimal.
In practice it is not. For mega-cap names where the subsector ETF effectively is the stock, the L3 subsector hedge ratio gets unstable: the marginal explained return from the third ETF can be under 1%, and the hedge ratios that absorb a near-zero variance contribution flicker noisily month to month.
L* (L-star) picks the simplest level whose marginal explained return clears a threshold θ:
The decision is made per (ticker, month). The default threshold is θ = 0.01 (1%) — chosen via a side study across 275 tickers (the 25 largest by market cap in each of 11 GICS sectors) over a 60-month window: monthly point-read inputs at 1% won or tied in 10 of 11 sectors on forward 12-month realized explained return, and 12-month smoothing degraded results by up to 176 bps in Energy and Financials.
When L* picks a lower level, the hedge is smaller and simpler, not a subset of the L3 hedge — each level’s ratios are internally consistent within that level’s regression. When L* = L1 — usually idiosyncratic-heavy names — neither the sector nor the subsector ETF clears the bar: the model is telling you the residual is most of the story.
Appendix — applications, signals, and comparisons
Not part of the specification. Everything below is illustrative or commercial: how the outputs get used, a signal derived from the residual, and a vendor comparison. It changes on a different cadence from the model itself, so it is kept out of the normative sections above — a methodology should be stable, while applications and comparisons should not be constrained by a spec review.
Applications in trading
- Direct hedging.Unlike academic factor models that output abstract “factor loadings,” the model gives actionable hedge ratios executable with liquid ETFs on any brokerage platform.
- Tax-efficient risk scaling. To reduce tech exposure without selling a position and triggering capital gains, short XLK proportionally — the adjusted hedge ratios ensure you are not accidentally double-hedging the market.
- Selection-skill measurement. The residual ε at L3 is the stock-specific return. A persistently positive residual is evidence relevant to selection skill; a negative one is underperformance that cannot be blamed on “the market was down.” It becomes a skill claim only with breadth, controls, and out-of-sample validation — see Part III.
Residual mean-reversion signal
The L3 residual — the part of a return left after market, sector, and subsector exposures are hedged out — is, by construction, the genuinely idiosyncratic component. The residual mean-reversion signalmeasures how stretched that residual is right now: a trailing 5-day cumulative L3 residual return, z-scored by the stock’s own 60-day residual volatility. A large negative value means the stock has under-performed its cascade-implied path over the past week; a large positive value means it has over-performed.
This is a combo-input factor — a building block for multi-signal alpha stacks, not a standalone strategy. In a 5-year backtest the cross-sectional decile long-short carries a gross Sharpe near 0.79 (rising to ~1.28 within the high-subsector-ER quintile), but under realistic market-impact costs the net-of-cost capacity caps below ~$1M of book size:
| Book size | Net Sharpe (decile long-short) |
|---|---|
| $0.5M | ~0.0 |
| $1M | ~−0.2 |
| $5M | ~−1.0 |
| $25M | ~−2.8 |
The signal is informative pre-cost; it earns its place inside a diversified signal portfolio where many uncorrelated factors combine, not as a strategy traded on its own. The reversion is materially cleaner for stocks that track their subsector tightly — a residual deviation only means “temporary dislocation” when the stock normally tracks its cohort. This is informational analytics, not investment advice.
How this compares to traditional risk models
| Feature | RiskModels | Barra / Axioma |
|---|---|---|
| Factor composition | Directly tradeable liquid ETFs (SPY, XLK, …) | Synthetic factors (value, momentum, PCA-derived) |
| Hedging execution | Short the ETF directly | Requires custom factor-mimicking baskets |
| Model structure | Hierarchical, orthogonal per level | Multivariate, hundreds of factors |
| Responsiveness | Short lookback — responsive to market shifts | Long history — stable but slower to adapt |
| Primary use case | Active hedging, tactical PM | Institutional reporting, long-term attribution |
Glossary
| Term | Definition |
|---|---|
| L1 (Market) | First level: broad market (SPY) exposure |
| L2 (Sector) | Second level: sector-specific exposure (XLK, XLF, …) |
| L3 (Subsector) | Third level: granular industry exposure (SOXX, XBI, …) |
| β (beta) | Sensitivity coefficient: β = Cov(r_s, r_f) / Var(r_f) |
| HR | Dollar amount of ETF per $1 of stock to neutralize a factor |
| ER | Variance fraction explained by factors: 1 − Var(ε) / Var(r) |
| λ (link beta) | Beta between ETFs at different levels |
| Orthogonalization | Removing higher-level exposure from lower-level factors |
| ε (residual) | Idiosyncratic return unexplained by any factor — stock-specific alpha |
For programmatic workflows — multi-ticker batches, xarray factor cubes, portfolio aggregation — and the live decomposition endpoints, see the API documentation and Python SDK at riskmodels.app. This note is expository and not investment advice.
← Return to overviewChangelog
This page is the canonical specification of the model and is cited in allocator files, so substantive changes are recorded rather than silently edited. Current: ERM3 Methodology v3.2, effective 2026-02-01, last reviewed 2026-07-28, applying to ERM3 (Equity Risk Model v3).
| Date | Change |
|---|---|
| 2026-07-28 | Added a live-output reconciliation section: the identity is now checked against production ERM3 output (WMT, 2026-07-27) by scripts/riskmodels/verify_methodology_identity.py, which agrees with the engine's published residual return to 8.2e-10 at L1 and 3.0e-4 at L3. Corrected the replication identity to distinguish the hedge-trade ratio HRᵢ from the exposure −HRᵢ; the worked example was already numerically right. Removed the claim that the residual is small by construction. Added the Vasicek-vs-Ridge clarification to the closure section. Replaced residual-as-alpha language with selection-skill evidence language. |
| 2026-02-01 | Technical Note RM-2026-01 — initial published specification. |