The empirical distribution of daily returns
▸ Pretest — guess, even if you don't know
Suppose you plot a histogram of SPY daily returns from 1993–present. Compared to a normal distribution with the same mean and standard deviation, what do you expect?
Building intuition: what do daily returns actually look like?
For SPY since inception (1993):
- Mean: ~0.04% per day (~10% annualized).
- Standard deviation: ~1.2% per day (~19% annualized).
- Skewness: ~ −0.2 (slightly negative).
- Kurtosis: ~10–15 depending on the sample window (normal would be 3 — several multiples of normal).
- Worst day in the SPY sample: October 15, 2008, about −9.8% (the S&P 500 index itself had a −20% day on October 19, 1987 — before SPY existed).
- Best days in the SPY sample: October 13, 2008 (
+11.6%) and October 28, 2008 (+10.8%) — both during the financial crisis, not during rallies. Extreme up-days cluster inside crises too.
A normal distribution with the same mean and standard deviation would predict that a single-day move beyond ±4σ should happen once every 16,000 days (about once every 60 years). In reality, SPY has seen many such days in just 30 years.
Why histograms can fool you
A histogram of daily returns looks bell-shaped at a glance. The center mass fits the normal nicely. The deception happens in the tails — there are so few observations beyond 3σ that the histogram bins are almost empty, and the eye sees "matches normal" when in reality those bins should be many orders of magnitude emptier under normality.
The right tool to see this is a Q-Q plot (quantile-quantile). On a Q-Q plot, you compare the empirical quantiles of returns against the theoretical quantiles of a normal distribution. If returns were normal, the plot would be a straight diagonal line.
For real stock returns, the line is straight in the middle and bends sharply away from the diagonal at the extremes — both tails curve outward. This bending is the visual signature of fat tails.
Stylized facts (the "always true" patterns)
Rama Cont (2001) catalogued the stylized facts of asset returns — properties that show up across nearly all liquid markets:
- Absence of autocorrelation in returns (you can't predict tomorrow's return from today's at short horizons in efficient markets).
- Strong autocorrelation in squared/absolute returns (volatility clustering — covered in A2-02).
- Heavy tails (always).
- Aggregate Gaussianity — return distributions become more normal as you look at longer aggregation periods (monthly, yearly). Daily is the most non-normal regime.
- Asymmetry between up and down moves (negative skew).
- Leverage effect — vol rises after price falls (downside moves trigger higher vol than upside moves of the same size).
- Volume/volatility correlation — high volume days are usually high volatility days.
These are not surprises in any equity market we'll consider. If your model assumes returns are normal and iid, you are missing every single one of these.
Why this matters for the curriculum
Almost every topic from this point forward connects back to these properties:
- Backtest methodology (D4) — fat tails mean a small number of extreme outcomes dominate strategy returns. Subsampling and resampling fix this; ignoring it doesn't.
- Risk management (D6) — Value-at-Risk under normality systematically underestimates extreme losses. Expected shortfall (ES) and stress testing fix this.
- Portfolio theory (D3) — mean-variance optimization implicitly assumes elliptical (normal-ish) returns. Real portfolios need correction.
- Options (B4 + D8) — implied volatility skew exists precisely because the market knows returns aren't normal.
Code snippet (for laptop later)
import yfinance as yf
import numpy as np
import scipy.stats as stats
spy = yf.download("SPY", start="1993-01-29")["Adj Close"]
returns = np.log(spy / spy.shift(1)).dropna()
print(f"Mean: {returns.mean():.5f} Std: {returns.std():.5f}")
print(f"Skew: {returns.skew():.3f} Kurt (excess): {returns.kurt():.3f}")
print(f"Worst: {returns.min():.3f} Best: {returns.max():.3f}")
# Q-Q plot
import matplotlib.pyplot as plt
stats.probplot(returns, dist="norm", plot=plt)
plt.show()
Draw it (your generative activity)
Today's generative activity is draw: sketch (in your head or on paper) what a Q-Q plot looks like for normal-distributed data, versus for fat-tailed data. The shape of the deviation from the diagonal is informative — get this picture lodged in your memory and you'll never confuse the two again.
Try it
Measure the shape of a distribution yourself — skewness (asymmetry) and excess kurtosis (tail weight):
Implement skew_kurt(x) returning the tuple (skewness, excess kurtosis) using the population formulas (ddof=0): with mu = mean(x), m2 = mean((x-mu)**2), m3 = mean((x-mu)**3), m4 = mean((x-mu)**4), skewness = m3 / m2**1.5 and excess kurtosis = m4 / m2**2 - 3. A normal distribution scores (0, 0); fat tails push excess kurtosis positive.
⧉ Review cardWhat is leptokurtosis?
⧉ Review cardWhat is a Q-Q plot used for?
⧉ Review cardName three stylized facts about returns.
⧉ Review cardWhy does aggregating returns to longer horizons (e.g., monthly) make them look more normal?
◈ Calibration check
Could you describe the empirical distribution of daily equity returns to someone in 3 sentences?
1 = guessing · 5 = could teach it
⏻ End of lesson
Mark it read to book its 4 review cards into your deck.