Part 11 / 18 · Updated July 2026
Explaining Risk Changes: What Changed My Momentum Exposure?
On this page
Chapter 9 ended with a finding and a lever: an unintended −0.332 momentum exposure, and the observation that halving the AXIOM underweight would remove roughly a third of tracking error. Suppose the manager pulls the lever. The pre-trade calculator prices the trade at current exposures and promises a momentum line of −0.210. A month later the report prints −0.239. Momentum’s share of active variance has risen, 22.0% to 24.0%, even though tracking error fell from 5.42% to 3.68%. Nobody traded momentum.
Every production risk report shows the changes against prior periods, and the first question asked of any number is why it moved. This chapter is the machinery for answering it: how much of a report-over-report change came from trading, how much from the market, how much from the model itself, and how to tell them apart before the morning meeting.
One framing note: Chapter 10 froze exposures across its quarter to isolate the linking problem, and flagged the freeze as a simplification. This chapter is about exactly what that freeze hides.
11.1 What can move between two reports
The report is a function of a handful of inputs: holdings, benchmark weights, exposures, covariances. Every change traces to one of them.
| Input | Changes because | Typical cadence |
|---|---|---|
| holdings | trades, price drift | continuous |
| benchmark | price drift, index rebalances | drift daily, rebalance quarterly |
| exposures | descriptors reprice, measurement windows roll, re-standardization, redefinition | daily to monthly |
| and | EWMA updates from new returns, scheduled re-estimation, model version change | daily update, re-estimation slower |
A fifth bucket, universe and corporate-action events (a merger closes, a name delists, coverage changes), produces the ugliest breaks but is bookkeeping rather than mathematics. Chapter 17 covers the plumbing.
flowchart LR
T["trades"] --> W["holdings w_p"]
P["prices"] --> W
P --> B["benchmark w_b"]
P --> D["descriptors"]
CAL["calendar: windows roll"] --> D
D --> XX["exposures X"]
STD["re-standardization"] --> XX
NR["new factor returns"] --> FF["covariance F"]
VER["model version"] --> XX
VER --> FF
W --> R["risk report"]
B --> R
XX --> R
FF --> R
The uncomfortable rows are the bottom ones. Trades are the only input the manager controls, and they are frequently the smallest mover. Your momentum exposure changes when a year-old return falls out of the 12-1 window, and when other stocks move, because standardization is relative (Chapter 3). None of that requires touching the book.
11.2 The -side: trades vs. drift
Bookkeeping comes before any model math. Prices move, so weights move on their own:
and the same repricing hits the benchmark’s cap weights. Trades are whatever remains after drift: . Splitting this way matters because a manager who does nothing still has changing active weights. Portfolio and benchmark drift at different rates.
Mini example, month 1: AXIOM returned +4.2% in a month the benchmark made +1.82% and the portfolio +1.18%, so AXIOM’s benchmark weight grew faster than its portfolio weight and the active position drifted from −0.1439 to −0.1466. The underweight widened with no order placed. The manager then buys 0.0733 of AXIOM (half the drifted gap), funded pro-rata from the other nine names, 7.3% one-way turnover, landing at −0.0733.
11.3 The exposure line decomposes exactly
Here is the one place in this chapter where change attribution is exact rather than conventional. Exposure is linear in each argument, , so its change splits identically into three pieces:
The positions term splits further into drift and trade using §11.2. The data term carries every change in : repriced descriptors, rolled windows, re-standardization. The interaction term is small when steps are small and genuinely belongs to both sides when it isn’t. For the mini example month (every number from section 13 of the source code):
| Factor | old | drift | trade | data | interaction | new |
|---|---|---|---|---|---|---|
| MKT | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 |
| TECH | −0.145 | −0.002 | +0.056 | 0.000 | 0.000 | −0.091 |
| FIN | 0.095 | 0.000 | −0.034 | 0.000 | 0.000 | 0.061 |
| CONS | 0.050 | +0.001 | −0.022 | 0.000 | 0.000 | 0.030 |
| VALUE | 0.385 | +0.003 | −0.131 | −0.005 | +0.002 | 0.254 |
| MOM | −0.332 | −0.002 | +0.124 | −0.063 | +0.034 | −0.239 |
| SIZE | −0.275 | −0.003 | +0.080 | −0.007 | +0.003 | −0.202 |
Rows sum left to right. Three readings:
The trade did what trades do. Buying back a tech mega-cap bought momentum (+0.124), bought TECH (+0.056), bought size (+0.080), and sold a third of the value tilt (−0.131, from 0.385 to 0.254). The TE fix spent intended exposure to remove unintended exposure, which is what a one-name manual trade always does. Chapter 12’s optimizer removes the momentum bet while constraining VALUE to stay put. That is the difference between a lever and an optimizer.
The data term moved the line with zero trades. Thirteen months ago AXIOM fell 9% on an earnings miss. That month just rolled out of its 12-1 momentum window while a +7% month rolled in at the near end, so AXIOM’s measured momentum jumped from +0.32 to +0.48 (z-score +1.198 to +1.551) while nothing special happened to its price this month. The book’s largest underweight became 0.35σ more momentum-like, and the financial overweights deteriorated the same way (FIDELIS’s z fell by −0.207), for a combined −0.063 on the line. Note the clock: the 12-1 window excludes the most recent month, so month 1’s returns are not even in it yet. This change was scheduled a year in advance.
The interaction: +0.034, because the manager bought the very stock whose measured momentum then jumped. The overlap belongs equally to the trade and the data, which is what an interaction term is for. Report it as its own line. Folding it into either parent hides a real effect.
Re-standardization can have an impact too. New cap weights shift each style column by a constant (here +0.016 on VALUE, −0.017 on MOM, −0.006 on SIZE). A uniform shift multiplied into active weights that sum to zero gives exactly zero: benchmark-relative exposures are immune to the standardization frame. Total exposures are not. The portfolio’s total MOM moves by the full −0.017 from re-standardization alone. If your total-risk report and active-risk report disagree about whether standardization “did something,” both are right.
And the reconciliation the chapter opened with: the calculator promised −0.210 (old exposures, post-trade weights: −0.332 − 0.002 + 0.124). The report prints −0.239. The gap is the data term (−0.063) plus the interaction (+0.034). The calculator wasn’t wrong. It answered a question about yesterday’s .
11.4 The 2×2 first cut
Before the fine decomposition, one cheap diagnostic: rerun yesterday’s book under today’s model, and today’s book under yesterday’s model.
| TE | old model | new model |
|---|---|---|
| old book | 5.42% | 5.61% |
| new book | 3.60% | 3.68% |
Within a column the model is fixed, so the row-to-row difference is what the book did (trading plus drift). Within a row the book is fixed, so the column-to-column difference is what the world and the model did. The off-diagonal cells are counterfactuals, and the top-right one is the useful headline: had the manager not traded, TE would have printed 5.61%, up on the month, because the momentum accident deepened on its own.
The same grid is the standard instrument for a model-version day: when the vendor ships an update, the “old book, new model” cell measured across standing portfolios is the version jump, and it should be published before PMs discover it (Chapter 15 on version governance, Chapter 16 on communicating changes).
11.5 The level: a waterfall with a convention
Tracking error is not linear in anything, so its change has no exact three-way split. The workhorse is sequential substitution: swap inputs from old to new one at a time, in pipeline order (prices, then trades, then exposures, then covariances), and charge each step with the TE change it causes. Swapped cumulatively, the steps sum to the total by construction. The arbitrariness hides in the ordering. Run it backwards to see how much: the reverse column applies the same steps in the opposite order (covariance first, drift last), so read it bottom-up, 5.42% -> 5.38% -> 5.61% -> 3.63% -> 3.68%.
| Step | forward | TE after | reverse | TE after |
|---|---|---|---|---|
| start | 5.42% | 5.42% | ||
| drift | +0.05 | 5.47% | +0.05 | 3.68% |
| trade | −1.87 | 3.60% | −1.99 | 3.63% |
| exposure updates | +0.10 | 3.70% | +0.23 | 5.61% |
| covariance update | −0.02 | 3.68% | −0.04 | 5.38% |
The exposure step is +0.10 in one order and +0.23 in the other, because in reverse it applies to the un-traded book still carrying the full AXIOM underweight. This is the cross-term problem of §9.1 again, across inputs instead of factors: any per-input attribution of a nonlinear quantity is a convention for allocating overlaps. The practice: pick pipeline order because it matches how production actually rebuilds the report, disclose it, and when two orderings disagree materially, report the spread or their average rather than pretending the number is unique.
A first-order cross-check catches most errors in the waterfall itself. Each step has a cheap marginal estimate against its base state:
- Trade step: gives −1.95 vs. −1.87 exact. Chapter 9’s one-name shortcut, , misses the funding leg entirely. “Recompute, don’t extrapolate” now has numbers attached: the extrapolation was 9% off with a funded trade and 7% turnover, and it gets worse fast.
- Exposure step: gives +0.10, matching exact to the reported precision.
- Covariance step: gives −0.02, again matching.
Small steps linearize well. The trade, at 7% turnover, is already at the edge. A model-version jump does not linearize at all; for that case use §11.4’s grid rather than a marginal.
11.6 The model moves on its own
The covariance step was small this month (−0.02), but it is never zero, because the estimator breathes. One EWMA update (half-life 12 months, Chapter 8) from the month-1 factor returns:
| Factor vol (% ann) | MKT | TECH | FIN | CONS | VALUE | MOM | SIZE |
|---|---|---|---|---|---|---|---|
| old | 16.00 | 9.00 | 7.00 | 5.00 | 4.00 | 6.00 | 4.00 |
| new | 15.62 | 8.77 | 6.88 | 4.86 | 3.91 | 6.05 | 3.89 |
The market factor returned +1.82% in a month whose stipulated vol implies a typical move of : a calm observation, so market vol decays. MOM returned +1.96% against a typical 1.7%: slightly hot, so momentum vol ticks up. Every vol in the table moved with zero trades and zero data changes, and the mirror effect is scheduled too: when a crisis month eventually rolls out of the estimation window, risk forecasts drop with no news that day. PMs read that as “the model changed its mind”. The honest description is “February 2020 left the sample.”
The month-2 report assembled from all of it: TE 3.68%, specific share 41.0%, and factor shares TECH 10.8%, FIN 4.2%, CONS 0.5%, VALUE 14.8%, MOM 24.0%, SIZE 4.7%. Which resolves the opening riddle: the momentum level improved (−0.332 to −0.239) while its share worsened (22.0% to 24.0%), because the trade removed even more of everything else, and the slightly higher momentum vol nudged the same direction. Shares are normalized. A share can deteriorate while every absolute number improves. Alert on both, and never let a share answer a question about a level.
(Held fixed throughout, and the report should say so: factor correlations and the specific-risk matrix . In production both update on the same cadence as the vols, with the same machinery.)
11.7 The change report in practice
The two artifacts worth automating, printed next to every morning report:
- The exposure delta table, §11.3’s format: one row per factor, columns for drift, trade, data, interaction, reconciling exactly to the reported change. The data column is the one that generates questions, so split it on demand into repricing, window roll, and re-standardization (§11.3’s frame argument says the last is zero for active books, a fact worth printing once a quarter when someone asks).
- The TE waterfall, §11.5’s format, with the ordering convention stated in the header and the 2×2 corner cells as a sanity frame.
Triage, when a delta looks wrong, in this order: data error first (a bad price, a missed corporate action, a descriptor restatement), model artifact second (version change, window event, re-standardization), real change last. The order reflects base rates. And the escalation rule falls out of §11.4: any change that survives in the “old book, new model” cell is not something the desk did, so route it to whoever owns the model.
11.8 Summary
- Exposure changes decompose exactly: positions + data + interaction, with positions splitting into drift and trades. Risk-level changes don’t. A waterfall needs a declared ordering, and forward-vs-reverse measures how much the convention is worth (+0.10 vs +0.23 on the exposure step here).
- The data term is where surprises live. A year-old crash rolling out of a momentum window moved this book’s MOM line by −0.063 with no trade. Re-standardization moves total exposures but cancels exactly in active space.
- The 2×2 rerun (old/new book × old/new model) is one extra risk run and answers the PM’s actual question: the no-trade counterfactual here was TE 5.61%, up on the month.
- Level and mix move independently: TE fell 5.42% → 3.68% while MOM’s variance share rose 22.0% → 24.0%. The report printed −0.239 against a promised −0.210, and the whole gap was the calendar.
The next two chapters rewind to the month-1 book, so every number reconciles against the appendix dataset (§18.5): Chapter 12 fixes the momentum accident properly, with an optimizer instead of a lever, and Chapter 13 removes exposures with instruments instead of rebalancing.
Try it: in section 13 of mini_example.py, set r_drop[0] = 0.0 (no year-old AXIOM crash to roll out of the window) and rerun. Most of the data term evaporates and the report prints close to what the calculator promised. The gap was the calendar, not the market.