Contagion, or less than advertised
When markets fall, correlations rise, and we call that contagion. I recomputed the classic proof on the S&P 500 and the CAC 40, then obtained the exact same picture in a simulated world where, by construction, nothing propagates.
Labels S&P 500 × CAC 40 Forbes-Rigobon 2002 9023 trading days conditional correlations no dependencies
The finding
The phrase comes back with every crisis: "in times of stress, everything becomes correlated". It has a standard proof, and the proof fits in one chart. Take the daily closes of the S&P 500 and the CAC 40, 9023 common trading days from 5 March 1990 to 13 August 2026; turn them into log returns smoothed with a two-day moving average, to absorb the time-zone gap between New York and Paris. Then sort the days into ten deciles of US return magnitude, from calmest to wildest, and measure the correlation inside each one.
The curve rises, without hesitation: from 0.09 in the calmest decile to 0.82 in the wildest, against a full-sample correlation of 0.64. On stormy days, Paris follows New York almost step for step. There, we are told, is contagion, measured.
the plotted values
| Decile | n | Median magnitude | δ | Correlation |
|---|---|---|---|---|
| 1 | 902 | 0.03% | −1.00 | 0.091 |
| 2 | 903 | 0.10% | −0.98 | 0.112 |
| 3 | 902 | 0.18% | −0.95 | 0.254 |
| 4 | 902 | 0.25% | −0.90 | 0.245 |
| 5 | 903 | 0.34% | −0.81 | 0.363 |
| 6 | 902 | 0.44% | −0.68 | 0.483 |
| 7 | 902 | 0.56% | −0.49 | 0.535 |
| 8 | 902 | 0.73% | −0.10 | 0.603 |
| 9 | 903 | 0.98% | 0.63 | 0.693 |
| 10 | 902 | 1.53% | 5.22 | 0.816 |
The reversal
Before concluding, a counter-test. I build a simulated Gaussian pair of the same size, whose true correlation is constant by construction, equal to 0.64, the real pair's full-sample value. No regimes, no crises, no propagation: the tamest world one can draw at random. The same decile procedure produces the same rising curve, from 0.04 in the first decile to 0.86 in the last, and the analytic prediction, drawn as a solid line, threads through the Monte-Carlo points. The chart that "proved" contagion therefore proves nothing on its own: it rediscovers a property of the estimator, not a property of markets.
the plotted values
| Decile | n | Median magnitude | δ | Correlation | Formula |
|---|---|---|---|---|---|
| 1 | 902 | 0.06 | −0.99 | 0.042 | 0.061 |
| 2 | 903 | 0.19 | −0.96 | 0.190 | 0.162 |
| 3 | 902 | 0.32 | −0.89 | 0.261 | 0.262 |
| 4 | 902 | 0.46 | −0.79 | 0.369 | 0.360 |
| 5 | 903 | 0.61 | −0.63 | 0.452 | 0.452 |
| 6 | 902 | 0.77 | −0.41 | 0.548 | 0.538 |
| 7 | 902 | 0.94 | −0.12 | 0.603 | 0.617 |
| 8 | 902 | 1.15 | 0.33 | 0.710 | 0.694 |
| 9 | 903 | 1.44 | 1.10 | 0.771 | 0.771 |
| 10 | 902 | 1.96 | 3.36 | 0.862 | 0.867 |
The mechanism
The mechanism fits in a single line: y = βx + ε, with ε independent of x. Conditioning on the days when x is large changes neither β nor the noise; it only changes the signal-to-noise ratio. In a wild decile, the part of y explained by x weighs more against the same noise, and the sample correlation rises, mechanically, with no structural parameter having moved.
Writing δ for the relative excess variance of x in the subsample, the conditional correlation is ρA = ρ·√(1 + δ) / √(1 + δ·ρ²): it rises with δ, and with δ alone.
the full derivation
- Model:
y = βx + ε, ε independent of x with variance σ²; A is the conditioning event, defined on x alone. Cov(x, y | A) = β·Var(x | A), because ε does not depend on x.Var(y | A) = β²·Var(x | A) + σ², for the same reason.- Hence
ρA = β·√Var(x | A) / √(β²·Var(x | A) + σ²). - Set
δ = Var(x | A) / Var(x) − 1and divide top and bottom by the unconditional standard deviation of y, which brings out ρ. - What remains is
ρA = ρ·√(1 + δ) / √(1 + δ·ρ²): the whole effect goes through δ.
A trap met along the way shows where the effect really lives. One of the first tests multiplied both series by three, stormy days included, and expected the correlation to rise: it did not move by a single decimal place, Pearson correlation being invariant under changes of scale. What the conditioning amplifies is not the size of the returns, then, but the share of the common shock against a noise that does not grow with it: inflate x alone, leaving ε untouched, and the raw correlation soars while the corrected one returns ρ. The whole question of contagion sits in that ratio, not in the turbulence itself.
The correction
The formula inverts: if the rising curve is only the image of a constant correlation seen through δ, then it can be straightened. That is the Forbes-Rigobon correction, applied here decile by decile to the real data. From the third to the tenth decile, where the estimator is tight, the rise of the first chart vanishes: the corrected curve has no overall slope left and stays within 0.15 of the full-sample value. In the two lowest deciles, by contrast, the inversion divides by a number close to zero and amplifies noise instead of removing the bias: the first corrected decile comes out at 0.88, with an interval running from 0.46 to 0.96, which is to say a point that says nothing. The intervals in the figure say so on their own; the flattening, for its part, is only claimed from the third decile onwards.
The last decile is worth pausing on. There the correction does more than flatten: the corrected value drops to 0.49, below the 0.64 reference, and its interval, from 0.46 to 0.53, excludes it. Two readings are compatible with that point. Either the inversion, driven by this decile's δ of 5.2, corrects beyond what was needed; or the raw excess of the wildest days really was volatility bias through and through, down to the last point. The page reports what comes out of the computation and does not decide between the two.
the plotted values
| Decile | n | Median magnitude | δ | Correlation | Corrected |
|---|---|---|---|---|---|
| 1 | 902 | 0.03% | −1.00 | 0.091 | 0.881 |
| 2 | 903 | 0.10% | −0.98 | 0.112 | 0.648 |
| 3 | 902 | 0.18% | −0.95 | 0.254 | 0.760 |
| 4 | 902 | 0.25% | −0.90 | 0.245 | 0.615 |
| 5 | 903 | 0.34% | −0.81 | 0.363 | 0.668 |
| 6 | 902 | 0.44% | −0.68 | 0.483 | 0.701 |
| 7 | 902 | 0.56% | −0.49 | 0.535 | 0.665 |
| 8 | 902 | 0.73% | −0.10 | 0.603 | 0.622 |
| 9 | 903 | 0.98% | 0.63 | 0.693 | 0.601 |
| 10 | 902 | 1.53% | 5.22 | 0.816 | 0.493 |
Move the threshold: the raw climbs, the corrected stays put.
What is left
Deciles mix every era together; to look at the crises themselves, I switch to a rolling correlation over 60 trading days. The raw curve climbs to 0.92 at the height of the crises. The corrected curve tells another story: averaged over each episode, it sits at 0.44 in autumn 2008 and 0.58 in February-April 2020, both below the full-sample line.
These means are descriptive, and it matters to say why. Two successive windows share 59 of their 60 days: the 84 windows of autumn 2008 amount to one or two independent observations, not 84. The two-day smoothing also autocorrelates the returns, 0.45 at the first lag, so that a 60-day window carries about 23 effective observations. And it is precisely in a crisis that δ becomes huge, a median of 8.3 in autumn 2008, which is exactly where the inversion amplifies noise the most. February-April 2020 makes the point: the standard deviation of the corrected windows there reaches 0.21, and 18 of the 51 windows go above the reference. "Below the reference" is a statement about a noisy average, not about every window.
The solid argument lies elsewhere, and it is an a fortiori one. The correction's variance reference is the full sample, crises included, not a calm period: the correction therefore under-corrects by construction. If extra propagation were added to the mechanism, above the reference is where it would have ample room to survive; yet, averaged over each episode, nothing survives. That an under-correcting adjustment lands below is, moreover, no paradox: at huge δ, averaging a noisy and roughly concave transform pulls downward, and the windows mix regimes, those ending in early September 2008 still looking back at a calm summer. The careful reading fits in one sentence: most of the crisis-time surge in correlations is the mechanical effect of volatility, not proof of extra propagation.
the plotted values
| Episode | Raw | Corrected |
|---|---|---|
| autumn 2008 | 0.761 | 0.438 |
| February-April 2020 | 0.792 | 0.578 |
| full sample | 0.641 | 0.641 |
What the computation assumes
- The shock is assumed to come from the S&P 500. Conditioning on the CAC 40's magnitude would give different numbers; the direction of conditioning is an economic choice, not a statistical truth.
- Homoscedastic ε, no omitted variable. Both assumptions are false in practice: an omitted common factor whose variance rises in a crisis biases the correction, in a direction that depends on the factor's weight in each index.
- The two-day moving average creates artificial autocorrelation. It is there to absorb the time-zone gap, and it raises the level: on single days, without smoothing, the full-sample correlation is 0.52 instead of 0.64. The Forbes-Rigobon correction says nothing about that level shift; the decile mechanics, though, are the same with or without smoothing.
- The Monte-Carlo is Gaussian. That is a tamer world than markets, with no fat tails; and the interval bootstrap is i.i.d., which likely understates their width.
- Trading desks usually condition on a market's rolling volatility, not on the day's magnitude. The day-magnitude version isolates the mechanism; the last figure covers the other reading.
- The data is taken as is. Yahoo Finance closes, dividends not reinvested, Pearson correlation on log returns: nothing here is total return, and another vendor would give neighbouring but not identical numbers.
Out of scope, finally: dynamic conditional correlations of the DCC-GARCH kind, copulas, correlations implied by option prices. So many doors left open for a follow-up post. The starting point remains the Forbes and Rigobon article, "No Contagion, Only Interdependence" (Journal of Finance, 2002), and the whole computation behind this page can be re-run from tools/contagion, on GitHub.
Every number on this page comes out of the same computation pipeline; the paired returns themselves are in contagion.json.