Stress Testing and Scenario Analysis
Stress testing answers the question that statistical risk measures dodge: not "how bad is a typical bad day," but "how much do I lose in a specific catastrophe, and would it end me?" Where Value at Risk and CVaR summarize the tail of a fitted distribution, stress testing and scenario analysis abandon the distribution entirely and ask what happens under named, mechanistic shocks — 1987, 2008, March 2020, a 200bp rate jump, a funding-market freeze. This guide covers the four scenario types that matter, the all-important problem of correlation breakdown, reverse stress testing, and a concrete Python implementation that shocks a real multi-asset book.
Why distribution-based risk is not enough
VaR and volatility are estimated from history and assume the future resembles the sampled past. That assumption fails exactly when it matters. Three structural problems make purely statistical risk dangerous on its own:
- Sample blindness. A 2-year lookback that contains no crisis produces a
reassuringly small VaR. The model literally cannot see a shock larger than the largest one in its window.
- Stationarity assumption. Covariance matrices estimated in calm regimes
understate both volatility and correlation in stress. The inputs are conditional on a regime that ends.
- Linearity and Gaussianity. Parametric risk assumes well-behaved tails and
linear exposures. Options books, leverage, and path-dependent products break both assumptions — see jump-diffusion models and extreme value theory for why the tails are fatter than a normal model admits.
Stress testing complements these by being mechanistic and explicit. You name a shock, propagate it through the book, and read the loss. There is no claim that the scenario is probable — only that it is possible and that you should know its cost.
The four scenario types
1. Historical scenario replay
Take the actual returns (or price changes) of every risk factor over a real crisis window and apply them to today's positions. The strength is realism: the cross-asset moves are internally consistent because they actually happened together. The weakness is that history gives you a small number of crises, and the next one will not be identical.
Useful canonical windows: the 1987 crash (one-day equity collapse), the 1998 LTCM / Russia default (correlation and liquidity), 2008 (credit and funding), the 2010 flash crash (microstructure), August 2015 (vol-of-vol, the XIV precursor), February 2018 ("Volmageddon"), and March 2020 (everything-correlated dash-for-cash).
2. Hypothetical shocks
Specify factor moves directly: "equities -20%, credit spreads +150bp, USD +5%, oil -30%, 10y yields +100bp." This lets you probe scenarios that have no historical precedent (a sovereign default in a specific country, a particular policy mistake). The danger is internal inconsistency — it is easy to specify a combination of moves that violates no-arbitrage or that has never co-occurred for good reason.
3. Factor-based / sensitivity stress
Express the portfolio's exposures as factor betas (see factor investing and beta/CAPM) and shock the factors. This is the scalable approach for large books: instead of shocking thousands of instruments, you shock a dozen factors and let the beta matrix translate. The loss is exposure · factor_shock, plus second-order terms for nonlinear positions.
4. Reverse stress testing
Invert the question. Instead of "what do I lose in scenario X," ask "what scenario produces a loss of X" — where X is the loss that breaches a covenant, triggers a margin call, or wipes out a defined fraction of capital. Reverse stress testing finds the cheapest path to ruin: the smallest, most plausible shock that breaks you. It is the most useful and most neglected of the four because it surfaces hidden concentrations you did not know dominated your risk.
Correlation breakdown: the point of the whole exercise
The single most important fact in stress testing is that correlations are not stable — they move toward 1 (in the loss direction) precisely when you need diversification most. A portfolio that looks well-diversified under normal-regime correlations can have most of its "independent" bets collapse into one trade in a crisis. The 2008 and March 2020 episodes both featured assets that were nearly uncorrelated in calm markets selling off together in the dash for liquidity.
This means a stress test that uses calm-regime correlations understates crisis loss. The fix is to use stressed correlations — either lifted from a historical crisis window or constructed by shifting the off-diagonal entries toward their crisis values while keeping the matrix valid (positive semi-definite). Ignoring this is the most common way stress tests give false comfort.
A concrete portfolio stress in Python
Below is a factor-based stress that handles the three things people usually get wrong: it uses stressed (not calm) correlations, it keeps the shocked covariance matrix positive semi-definite, and it adds a nonlinear (gamma) term for the options sleeve so the loss is not just a linear approximation.
import numpy as np
import pandas as pd
def stress_portfolio(exposures, factor_shocks, calm_corr, stress_corr,
factor_vol, gamma=None, blend=0.7):
"""
exposures : Series, dollar exposure (delta) of book to each factor
factor_shocks : Series, the shock applied to each factor (in std units)
calm_corr, stress_corr : DataFrames, factor correlation matrices
factor_vol : Series, per-factor daily vol (decimal)
gamma : optional Series of dollar gamma per factor (2nd-order P&L)
blend : weight on stressed correlations (crisis migration)
"""
factors = exposures.index
# 1. Blend toward stressed correlations, then repair to nearest PSD matrix
C = blend * stress_corr.loc[factors, factors].values \
+ (1 - blend) * calm_corr.loc[factors, factors].values
C = nearest_psd(C)
# 2. Translate std-unit shocks into return-space shocks
shock_ret = (factor_shocks.loc[factors].values
* factor_vol.loc[factors].values)
# 3. First-order (delta) P&L, with correlation linking the shocks
# Decompose the shock through the correlation structure so that
# co-moving factors reinforce rather than cancel.
L = np.linalg.cholesky(C)
correlated_shock = L @ shock_ret
delta_pnl = float(exposures.loc[factors].values @ correlated_shock)
# 4. Second-order (gamma) P&L for nonlinear positions
gamma_pnl = 0.0
if gamma is not None:
gamma_pnl = 0.5 * float(
gamma.loc[factors].values @ (correlated_shock ** 2))
total = delta_pnl + gamma_pnl
return {"delta_pnl": delta_pnl, "gamma_pnl": gamma_pnl,
"total_pnl": total}
def nearest_psd(A):
"""Project a symmetric matrix to the nearest positive semi-definite one."""
B = (A + A.T) / 2
w, V = np.linalg.eigh(B)
w = np.clip(w, 0, None)
return (V * w) @ V.T
The blend parameter is the honest dial: at blend=0 you are using calm correlations and lying to yourself; at blend=1 you assume full crisis migration. Running the same shock across blend from 0 to 1 shows how much of your apparent diversification is regime-dependent. The gamma term matters because a short-options book that looks delta-neutral can lose large amounts to a big move even with zero net delta — the convexity is the risk.
Building a scenario suite
A usable stress program is a suite run on a schedule, not a one-off. A reasonable starting set for a multi-asset book:
| Scenario | Type | Key shocks | What it tests |
|---|---|---|---|
| 2008 replay | Historical | Equity -45%, credit +400bp, vol +300% | Credit + funding tail |
| March 2020 replay | Historical | Equity -30% fast, all corr → 1, liquidity | Dash-for-cash, correlation breakdown |
| Rate shock | Hypothetical | +200bp parallel, curve steepening | Duration / carry exposure |
| Vol spike | Factor | VIX 15 → 50, term structure inverts | Short-vol / short-gamma sleeve |
| Liquidity freeze | Hypothetical | Spreads ×5, funding +300bp | Leverage, rollover risk |
| Reverse: -25% capital | Reverse | Solve for cheapest shock | Hidden concentration |
Run the suite daily, store the time series of stressed P&L per scenario, and alert when any scenario loss breaches a fraction of capital or when the composition of the worst scenario changes — that drift often signals a new concentration creeping into the book before it shows up anywhere else.
How stress testing connects to sizing
Stress losses should feed back into position sizing, not just sit on a dashboard. Two practical links:
- Stress-based limits. Cap positions so the worst-suite scenario loss stays below
a defined fraction of equity. This is more robust than a VaR limit because it does not depend on a fitted tail and it captures nonlinearity.
- Drawdown coupling. A scenario that would push you past your
maximum drawdown tolerance is a position-sizing constraint, not just a risk report. If the 2008 replay loses more than your drawdown budget, you are too large today, regardless of how calm the current vol reads.
Reverse stress testing is the highest-leverage habit here. Most blowups are not
"we knew the risk and accepted it" — they are "we did not know that combination of
positions was our dominant risk." Solving for the cheapest path to ruin surfaces
exactly that blind spot.
Failure modes of stress testing itself
Stress testing is not immune to fooling yourself:
- Scenario anchoring. Re-fighting the last war. A suite built entirely from past
crises misses the novel one. Pair historical replay with hypothetical and reverse scenarios.
- Calm-correlation comfort. Using normal-regime correlations is the most common
silent error; it makes every diversified book look safe.
- Ignoring liquidity and funding. A price stress that assumes you can exit at
marked levels is fiction in a crash. Add a liquidity-haircut and a funding-cost shock; the act of liquidating moves the market against you.
- Static positions. Real losses come from forced deleveraging — margin calls
beget selling begets more margin calls. A single-step shock misses this feedback; multi-step or path-dependent stress captures more of it.
- No action threshold. A stress report that never triggers a position change is
theater. Tie scenario losses to hard limits.
Conclusion
Stress testing and scenario analysis are the mechanistic complement to statistical risk: they trade probability for specificity, telling you what a named catastrophe costs rather than how often a generic bad day occurs. Build a suite that mixes historical replay, hypothetical shocks, factor-based sensitivity, and — most importantly — reverse stress testing to find your cheapest path to ruin. Always use stressed correlations, account for nonlinear (gamma) exposure, and add liquidity and funding shocks so the loss is realistic. Most of all, wire the results into hard risk limits and your drawdown budget. A stress test that never changes a position is decoration; one that occasionally forces you to cut size before a crisis is the cheapest insurance a trading book can buy.
