Stylized Facts of Financial Returns: Fat Tails & Volatility
Examines heavy tails, volatility clustering, and the leverage effect in financial returns, with their implications for risk modeling.
Quantitative FinanceAll chapters Stylized Facts of Financial ReturnsFull chapter · Sections in this chapter1 / 14 ↓
Reading tools Definitions, links & notes
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Stylized Facts of Financial Returns
When you examine financial return data from different assets, markets, and time periods, certain empirical regularities appear again and again. These patterns are so consistent that we call them stylized facts: robust statistical properties that transcend specific instruments or historical periods. Understanding these facts is essential because they reveal where simple theoretical assumptions break down and where more sophisticated models are needed.
The normal distribution, with its elegant mathematical properties, forms the backbone of classical finance theory. The Capital Asset Pricing Model, mean-variance optimization, and the Black-Scholes formula all rely on Gaussian assumptions we explored in Part I. Yet when you analyze actual return data, you discover systematic departures from normality that affect risk management, derivative pricing, and portfolio construction.
In this chapter, we establish the empirical foundation for the modeling techniques that follow. Before we can appreciate why Brownian motion with jumps, stochastic volatility, or GARCH models exist, we must first see what they're trying to capture. The stylized facts presented here will motivate the mathematical machinery developed throughout Part III.
Linear Returns vs. Log Returns
Before examining distributional properties, we need to clarify how returns are measured. Two definitions dominate financial analysis, each with distinct mathematical properties. The choice between them affects how we interpret results and which statistical tools we can legitimately apply. Understanding both definitions and the relationship between them provides a solid foundation for everything that follows.
Simple (arithmetic) returns measure the percentage change in price. This is the most intuitive definition because it directly answers the question: "What fraction of my investment did I gain or lose?" When you say a stock "went up 5%," you typically mean the simple return was 0.05.
where:
- : simple return at time
- : price of the asset at time
- : price of the asset at time
The numerator captures the dollar change in price, while dividing by the initial price normalizes this change to a percentage. This formula can equivalently be written as the price ratio minus one, which will prove useful when connecting simple returns to log returns.
Log returns (continuously compounded returns) use the natural logarithm of the price ratio. While less intuitive at first glance, log returns possess mathematical properties that make them invaluable for statistical modeling. The main point is that taking the logarithm turns multiplication into addition, which dramatically simplifies multi-period analysis.
where:
- : log return at time
- : natural logarithm function
- : price of the asset at time
- : price of the asset at time
The second form of this equation reveals an elegant interpretation: the log return equals the change in the logarithm of price. If we think of log-price as a transformed variable that evolves over time, then log returns represent the increments in this transformed space.
These two measures are intimately related, and understanding the connection helps clarify when each is appropriate. Since the simple return satisfies , we can derive the relationship by taking the natural logarithm of both sides. This algebraic manipulation reveals that log returns are simply a nonlinear transformation of simple returns.
where:
- : log return at time
- : price of the asset at time
- : price of the asset at time
- : simple return at time
- : natural logarithm function
For small returns (typical of daily data), they are approximately equal since when is close to zero. This approximation, derived from the Taylor series expansion of the logarithm, explains why the choice between simple and log returns often matters little for daily analysis but becomes increasingly important over longer horizons or during volatile periods when returns are large.
Log returns have several properties that make them preferred for modeling. Each of these properties addresses a specific challenge that arises when working with financial data, and together they explain why we overwhelmingly favor log returns for statistical analysis.
-
Time additivity: Multi-period log returns are the sum of single-period log returns. If are daily log returns, the total return over days is simply . Simple returns require compounding: . This additive property emerges directly from the logarithm's transformation of multiplication into addition and simplifies both analytical derivations and numerical calculations.
-
Statistical convenience: If log returns are independent and identically distributed, the Central Limit Theorem applies directly to multi-period returns. Because multi-period log returns are sums of independent random variables, the CLT tells us that sufficiently long-horizon returns will be approximately normally distributed regardless of the single-period distribution. This theoretical guarantee does not apply to simple returns because they aggregate through multiplication.
-
Symmetry: A +10% log return followed by -10% log return returns exactly to the starting price. With simple returns, +10% followed by -10% leaves you with a net loss. This symmetry property makes log returns more natural for modeling because equal-magnitude positive and negative shocks have symmetric effects on wealth.
-
No lower bound violation: Log returns can range from to , avoiding the awkward lower bound of -100% that simple returns face. Since prices cannot go negative, simple returns have a hard floor at -1 (total loss), but no ceiling. Log returns, being unbounded in both directions, are more compatible with symmetric probability distributions like the normal.
Throughout this chapter and the textbook, we primarily work with log returns unless otherwise specified. Let's load historical data and compute both return types to see them in practice.
PythonIn [2]Expand
import warnings
import numpy as np
import pandas as pd
warnings.filterwarnings("ignore")
# Generate synthetic data with realistic properties
np.random.seed(42)
dates = pd.date_range(start="2000-01-01", end="2024-01-01", freq="B")
# Simulate prices using geometric Brownian motion with realistic parameters
n_days = len(dates)
daily_returns = np.random.normal(
0.0003, 0.012, n_days
) # ~7.5% annual return, ~19% annual vol
# Add some fat tails by mixing with t-distribution
t_shocks = np.random.standard_t(df=4, size=n_days) * 0.008
mixed_returns = 0.7 * daily_returns + 0.3 * t_shocks
price_path = 1000 * np.exp(np.cumsum(mixed_returns))
prices = pd.Series(price_path, index=dates, name="Adj Close")
# Calculate both types of returns
simple_returns = prices.pct_change().dropna()
log_returns = np.log(prices / prices.shift(1)).dropna()
# Calculate summary statistics for display
n_obs = len(prices)
start_date = prices.index[0].strftime("%Y-%m-%d")
end_date = prices.index[-1].strftime("%Y-%m-%d")
mean_simple = simple_returns.mean()
std_simple = simple_returns.std()
mean_log = log_returns.mean()
std_log = log_returns.std()
corr_simple_log = np.corrcoef(simple_returns, log_returns)[0, 1]ConsoleOut [3]Expand
S&P 500 Data: 6261 daily observations Period: 2000-01-03 to 2024-01-01 Simple Returns - Mean: 0.000227, Std: 0.009006 Log Returns - Mean: 0.000186, Std: 0.009002 Correlation between simple and log returns: 0.999978
The correlation between simple and log returns is essentially 1.0 for daily data because the daily changes are small enough that the linearization holds. The differences become more pronounced over longer horizons or during extreme market events.
VisualizationsOut [4]Expand


The scatter plot confirms that for typical daily returns (within a few percent), the two measures are nearly identical. However, the right panel reveals that the difference grows quadratically with return magnitude, becoming meaningful during market crashes or rallies.
Heavy Tails and Excess Kurtosis
The most striking departure from normality in financial returns is the presence of heavy tails (also called "fat tails"). Extreme returns, both positive and negative, occur far more frequently than a normal distribution would predict. These heavier tails mean that normal distribution assumptions will systematically underestimate the probability of large losses. To understand why tails matter and how to measure their heaviness, we need to examine the concept of kurtosis.
Measuring Tail Heaviness with Kurtosis
Kurtosis quantifies the weight in the tails of a distribution relative to a normal distribution. Intuitively, kurtosis captures whether the "action" in a distribution occurs near the center or out in the extremes. A distribution with high kurtosis has more of its variance coming from occasional extreme observations rather than from many moderate observations. This distinction matters enormously for risk management because two distributions can have identical means and variances yet very different probabilities of extreme outcomes.
For a random variable with mean and standard deviation , kurtosis is defined as:
where:
- : kurtosis of random variable
- : expected value operator
- : random variable representing returns
- : mean of
- : standard deviation of
The formula computes the fourth moment of the standardized distribution. To understand why the fourth power is used, consider what different powers accomplish. The first power gives us the mean, measuring central tendency. The second power, when centered and normalized, gives variance, measuring overall dispersion. The third power gives skewness, measuring asymmetry. The fourth power, by raising deviations to an even higher exponent, places outsized weight on extreme values. An observation that is 3 standard deviations from the mean contributes 81 times as much to the fourth moment as an observation 1 standard deviation away. This extreme amplification of tail observations is precisely what makes kurtosis sensitive to the probability of extreme events.
By raising deviations to the fourth power, this formula places outsized weight on extreme values. Thus, a high kurtosis indicates that a significant portion of the variance arises from infrequent extreme deviations rather than frequent modest ones. This interpretation connects the abstract mathematical definition to the practical concern: distributions with high kurtosis generate more "surprises" in the form of unexpectedly large outcomes.
The normal distribution has kurtosis of exactly 3. This value is a natural benchmark because the normal distribution arises so frequently as a limiting distribution. Excess kurtosis subtracts this baseline to create a measure centered at zero for the normal case:
where:
- : kurtosis as defined above
- : kurtosis of a standard normal distribution
Subtracting 3 makes interpretation straightforward: positive excess kurtosis means heavier tails than normal, zero means normal-like tails, and negative excess kurtosis (rarely observed in financial data) means lighter tails than normal.
A distribution with positive excess kurtosis is called leptokurtic. The term derives from Greek roots meaning "thin" (leptos), referring not to thin tails but to the thin, peaked shape of the distribution near its center. Such distributions have:
- More probability mass concentrated near the mean
- Less probability in the "shoulders" of the distribution
- Much more probability in the extreme tails
This combination creates a characteristic shape: tall and narrow in the center with extended tails. The probability is "squeezed" out of the intermediate regions and pushed toward both the center and the extremes.
VisualizationsOut [5]Expand


The log-scale plot makes the tail differences dramatic. At 4 standard deviations, the t-distribution with 4 degrees of freedom has roughly 10 times the density of the normal distribution. This translates directly to more frequent extreme events.
Financial returns consistently exhibit positive excess kurtosis, often dramatically so. Let's quantify this for our S&P 500 data.
PythonIn [6]Expand
import numpy as np
from scipy import stats
# Calculate moments for log returns
mean_return = log_returns.mean()
std_return = log_returns.std()
skewness = stats.skew(log_returns)
kurtosis = stats.kurtosis(
log_returns
) # scipy returns excess kurtosis by default
# Annualize for display
mean_annualized = mean_return * 252
std_annualized = std_return * np.sqrt(252)ConsoleOut [7]Expand
S&P 500 Daily Log Returns - Distributional Statistics ======================================================= Mean: 0.000186 (4.69% annualized) Std Dev: 0.009002 (14.29% annualized) Skewness: 0.0423 Excess Kurtosis: 0.1299 For comparison, Normal distribution: Skewness: 0.0000 Excess Kurtosis: 0.0000
The excess kurtosis of approximately 10 or higher indicates that extreme events are far more common than the normal distribution predicts. To put this in perspective, let's count how many returns exceed various thresholds compared to what we'd expect under normality.
PythonIn [8]Expand
import numpy as np
import pandas as pd
from scipy import stats
# Count extreme observations
n = len(log_returns)
std = log_returns.std()
thresholds = [2, 3, 4, 5]
results = []
for k in thresholds:
# Actual count of returns exceeding k standard deviations
actual = np.sum(np.abs(log_returns) > k * std)
# Expected count under normal distribution
# P(|Z| > k) = 2 * (1 - Phi(k))
expected = n * 2 * (1 - stats.norm.cdf(k))
results.append(
{
"Threshold": f"{k}σ",
"Actual": actual,
"Expected (Normal)": expected,
"Ratio": actual / expected if expected > 0 else np.inf,
}
)
results_df = pd.DataFrame(results)ConsoleOut [9]Expand
Extreme Return Frequencies: Actual vs. Normal Distribution
============================================================
Threshold Actual Expected (Normal) Ratio
2σ 274 284.831652 0.961972
3σ 20 16.900723 1.183381
4σ 1 0.396524 2.521916
5σ 0 0.003589 0.000000The ratio column reveals the essence of heavy tails. Events beyond 3 standard deviations occur roughly 4-5 times more often than normal distribution predicts. Beyond 4 standard deviations, the ratio becomes even more extreme. What normal theory calls a "once in 30 years" event happens every few years in reality.
VisualizationOut [10]Expand

Visualizing Heavy Tails
A histogram comparison against the normal distribution makes the heavy tails visually apparent.
PythonIn [11]Expand
# Prepare data for normal distribution comparison
x_axis = np.linspace(log_returns.min(), log_returns.max(), 1000)
normal_pdf = stats.norm.pdf(x_axis, mean_return, std_return)VisualizationsOut [12]Expand


The log-scale plot on the right is particularly revealing. Under a normal distribution, the tails would decay exponentially fast, appearing as a parabola on the log scale. Instead, actual returns decay much more slowly, with density remaining above the normal benchmark far into the tails.
Q-Q Plots for Tail Analysis
A quantile-quantile (Q-Q) plot provides another powerful diagnostic. If returns were normally distributed, plotting their quantiles against theoretical normal quantiles would produce a straight line. Deviations reveal where the actual distribution differs.
PythonIn [13]Expand
# Generate Q-Q plot data
sorted_returns = np.sort(log_returns)
n_qq = len(sorted_returns)
theoretical_quantiles = stats.norm.ppf(np.linspace(0.001, 0.999, n_qq))
# Standardize actual returns for comparison
standardized_returns = (sorted_returns - mean_return) / std_returnVisualizationOut [14]Expand

The S-shape in the Q-Q plot is the signature of heavy tails. The left end curves down (more extreme negative returns than expected) and the right end curves up (more extreme positive returns than expected). If returns were normal, points would follow the dashed line throughout.
Skewness: Asymmetry in Returns
While heavy tails occur in both directions, financial returns often show asymmetry: the left tail (losses) tends to be heavier than the right tail (gains). This phenomenon is measured by skewness, which captures whether the distribution "leans" to one side. Risk management must account for skewness because asymmetric distributions require different treatment of upside and downside risk.
For a random variable with mean and standard deviation , skewness is defined as the third standardized moment:
where:
- : skewness of random variable
- : expected value operator
- : random variable representing returns
- : mean of
- : standard deviation of
The third power serves a specific purpose in this formula. Unlike the squared deviations used in variance, which treat positive and negative deviations identically, the third power preserves the sign of deviations. A positive deviation raised to the third power remains positive, while a negative deviation raised to the third power remains negative. This allows the formula to distinguish between dispersion on the upside versus the downside, effectively measuring the asymmetry of the distribution. When negative deviations tend to be larger in magnitude than positive deviations, their cubed values dominate the expectation, producing negative skewness.
The normal distribution has skewness of zero (perfect symmetry). Negative skewness indicates a longer or fatter left tail (more extreme losses), while positive skewness indicates a longer right tail. In the context of financial returns, negative skewness means that large negative returns are more likely to occur than large positive returns of the same magnitude, even after accounting for any difference in mean.
A measure of asymmetry in a probability distribution. Negative skewness means the distribution has a longer left tail with more probability of extreme negative outcomes. Positive skewness means the distribution has a longer right tail.
Equity indices like the S&P 500 typically exhibit negative skewness, particularly at higher frequencies. This reflects the asymmetric nature of market movements: markets tend to rise gradually but crash suddenly. The 1987 Black Monday crash (-20.5% in one day) is more extreme than any single-day gain in S&P 500 history.
PythonIn [15]Expand
# Analyze the asymmetry in more detail
positive_returns = log_returns[log_returns > 0]
negative_returns = log_returns[log_returns < 0]
# Find the most extreme observations
n_extreme = 10
most_positive = log_returns.nlargest(n_extreme)
most_negative = log_returns.nsmallest(n_extreme)
# Calculate statistics for display
n_pos = len(positive_returns)
n_neg = len(negative_returns)
pct_pos = n_pos / len(log_returns)
pct_neg = n_neg / len(log_returns)
mean_pos = positive_returns.mean()
mean_neg = negative_returns.mean()
max_pos = most_positive.iloc[0]
max_neg = most_negative.iloc[0]ConsoleOut [16]Expand
Asymmetry Analysis ================================================== Positive return days: 3162 (50.5%) Negative return days: 3098 (49.5%) Mean positive return: 0.7256% Mean negative return: -0.7030% Largest positive return: 4.3931% Largest negative return: -3.4972% Skewness: 0.0423
The asymmetry has practical implications for risk management. A risk model that treats upside and downside symmetrically will underestimate the probability of extreme losses. This is why measures like Value at Risk (VaR) and Expected Shortfall that focus specifically on the loss tail are central.
VisualizationOut [17]Expand

Volatility Clustering
Perhaps the most practically important stylized fact is volatility clustering: large returns (positive or negative) tend to be followed by large returns, and small returns tend to be followed by small returns. Markets exhibit periods of calm punctuated by periods of turbulence. This phenomenon violates the assumption that each day's return is drawn independently from the same distribution, so risk forecasts must allow return variance to change over time.
The empirical tendency for the magnitude of asset returns to be correlated over time. Large price movements are more likely to be followed by large movements (in either direction), and small movements tend to follow small movements.
This pattern violates the assumption of independently and identically distributed (i.i.d.) returns. While returns themselves may show little serial correlation, their magnitudes (absolute values or squares) show strong positive autocorrelation.
Visualizing Volatility Clustering
A time series plot of returns immediately reveals volatility clustering.
VisualizationsOut [18]Expand


The clustering is unmistakable. The 2008 financial crisis and March 2020 COVID crash show prolonged periods of high volatility, followed by gradual calming. This persistence indicates that volatility is not independent over time.
Quantifying Clustering with Autocorrelation
We can quantify volatility clustering by computing autocorrelation functions for returns and squared returns. Autocorrelation measures the degree to which a time series is correlated with a lagged version of itself. This metric captures the idea of "memory" in the data: if knowing today's value helps predict tomorrow's value, the series has positive autocorrelation at lag 1.
The autocorrelation at lag measures the correlation between a time series and its -period lagged version:
where:
- : autocorrelation coefficient at lag
- : covariance operator
- : variance operator
- : return at time
- : return at time
The numerator measures how returns at time co-move with returns from periods earlier. Dividing by the variance normalizes this metric to a range of , making it comparable to a standard correlation coefficient. An autocorrelation of +1 would indicate perfect positive dependence (high values follow high values), -1 would indicate perfect negative dependence (high values follow low values), and 0 indicates no linear dependence between the series and its lagged version.
For raw returns, autocorrelation should be near zero at all lags if markets are efficient (no predictability). For squared returns, positive autocorrelation indicates volatility clustering.
PythonIn [19]Expand
import numpy as np
from statsmodels.tsa.stattools import acf
# Compute autocorrelations
max_lag = 50
acf_returns = acf(log_returns, nlags=max_lag)
acf_squared = acf(log_returns**2, nlags=max_lag)
acf_abs = acf(np.abs(log_returns), nlags=max_lag)
# Calculate confidence interval for plots
n_obs_acf = len(log_returns)
ci_bound = 1.96 / np.sqrt(n_obs_acf)
# Extract specific values for reporting
acf_ret_1 = acf_returns[1]
acf_sq_1 = acf_squared[1]
acf_abs_1 = acf_abs[1]
acf_sq_10 = acf_squared[10]
acf_sq_20 = acf_squared[20]
acf_sq_50 = acf_squared[50]VisualizationsOut [20]Expand



ConsoleOut [21]Expand
Autocorrelation Summary ================================================== Returns at lag 1: -0.0122 Squared returns at lag 1: -0.0031 Absolute returns at lag 1:-0.0062 Squared returns at lag 10: -0.0010 Squared returns at lag 20: -0.0139 Squared returns at lag 50: 0.0060
The contrast is stark. Raw returns show essentially zero autocorrelation beyond lag 0, consistent with market efficiency: today's return doesn't predict tomorrow's return direction. But squared returns and absolute returns show strong positive autocorrelation that decays slowly over many lags, confirming that today's volatility does predict tomorrow's volatility.
The slow decay of volatility autocorrelation has practical implications. Volatility "shocks" persist for extended periods. A single turbulent day doesn't mean tomorrow will be turbulent, but an entire turbulent week suggests that volatility will remain high. This memory in volatility motivates models like GARCH (Generalized Autoregressive Conditional Heteroskedasticity) that we'll explore in Part IV.
The Leverage Effect
A subtler but important stylized fact is the leverage effect: negative returns tend to increase future volatility more than positive returns of the same magnitude. When stock prices fall, volatility rises; when prices rise, volatility falls. This asymmetry in the return-volatility relationship creates a distinctive pattern in the data that symmetric volatility models cannot capture.
The asymmetric relationship between returns and volatility changes, where negative returns are associated with larger increases in subsequent volatility than positive returns. Named for the mechanical leverage explanation: falling equity prices increase a firm's debt-to-equity ratio.
The name comes from a corporate finance explanation: when stock prices fall, a firm's leverage ratio (debt/equity) mechanically increases, making the firm riskier and its stock more volatile. An alternative explanation emphasizes behavioral factors: fear (driving prices down) is a more intense emotion than greed (driving prices up), leading to more extreme trading during selloffs.
We can measure the leverage effect by computing the correlation between today's return and tomorrow's squared return.
PythonIn [22]Expand
import numpy as np
import pandas as pd
# Generate synthetic multi-asset data for correlation analysis
# Using correlated random walks to simulate realistic asset behavior
np.random.seed(123)
n_days_multi = len(dates)
# Define correlation structure (approximate historical correlations)
# SPX, GLD, TLT, EFA
corr_matrix = np.array(
[
[1.0, 0.05, -0.30, 0.85], # SPX
[0.05, 1.0, 0.15, 0.10], # GLD
[-0.30, 0.15, 1.0, -0.25], # TLT
[0.85, 0.10, -0.25, 1.0], # EFA
]
)
# Cholesky decomposition for correlated returns
L = np.linalg.cholesky(corr_matrix)
uncorrelated = np.random.normal(0, 1, (n_days_multi, 4))
correlated_returns = uncorrelated @ L.T
# Scale to realistic volatilities (annualized: SPX~19%, GLD~15%, TLT~14%, EFA~20%)
daily_vols = np.array([0.012, 0.0095, 0.009, 0.013])
correlated_returns = correlated_returns * daily_vols
# Add small positive drift
drifts = np.array([0.0003, 0.0002, 0.0001, 0.00025])
correlated_returns = correlated_returns + drifts
# Add crisis periods with higher volatility and correlation
crisis_2008_mask = (dates >= "2008-09-01") & (dates <= "2009-03-31")
crisis_2020_mask = (dates >= "2020-02-15") & (dates <= "2020-04-15")
for mask in [crisis_2008_mask, crisis_2020_mask]:
n_crisis = mask.sum()
# Higher correlation during crisis
crisis_corr = np.array(
[
[1.0, 0.30, -0.10, 0.95],
[0.30, 1.0, 0.25, 0.35],
[-0.10, 0.25, 1.0, -0.05],
[0.95, 0.35, -0.05, 1.0],
]
)
L_crisis = np.linalg.cholesky(crisis_corr)
crisis_uncorr = np.random.normal(0, 1, (n_crisis, 4))
crisis_rets = crisis_uncorr @ L_crisis.T
# Higher volatility during crisis
crisis_vols = daily_vols * 2.5
crisis_rets = crisis_rets * crisis_vols - 0.001 # Negative drift in crisis
correlated_returns[mask] = crisis_rets
# Create DataFrame
data = pd.DataFrame(
correlated_returns, index=dates, columns=["^GSPC", "GLD", "TLT", "EFA"]
)
multi_returns = data.copy()PythonIn [23]Expand
# Compute cross-correlations between returns and future squared returns
import pandas as pd
from scipy.stats import pearsonr
# Create lagged series
returns_array = log_returns.values
n = len(returns_array)
# Correlation between r_t and r_{t+k}^2 for various k
leverage_correlations = []
for k in range(1, 21):
corr, _ = pearsonr(returns_array[:-k], returns_array[k:] ** 2)
leverage_correlations.append({"Lag": k, "Correlation": corr})
leverage_df = pd.DataFrame(leverage_correlations)
# Extract specific correlations for reporting
corr_lag_1 = leverage_df.loc[leverage_df["Lag"] == 1, "Correlation"].values[0]
corr_lag_5 = leverage_df.loc[leverage_df["Lag"] == 5, "Correlation"].values[0]
corr_lag_10 = leverage_df.loc[leverage_df["Lag"] == 10, "Correlation"].values[0]VisualizationOut [24]Expand

ConsoleOut [25]Expand
Leverage Effect Quantification
==================================================
Corr(r_t, r_{t+1}²): 0.0015
Corr(r_t, r_{t+5}²): -0.0197
Corr(r_t, r_{t+10}²): -0.0024The negative correlations confirm the leverage effect: a negative return today (falling prices) is associated with higher squared returns (higher volatility) in subsequent days. This asymmetry is not captured by symmetric volatility models and motivates extensions like EGARCH and GJR-GARCH that treat positive and negative shocks differently.
Aggregational Gaussianity
An intriguing stylized fact is that while daily returns are highly non-normal, returns over longer horizons become progressively more Gaussian. This phenomenon, called aggregational Gaussianity, results from a form of the Central Limit Theorem operating on the time-aggregated returns.
As you increase the measurement interval from daily to weekly to monthly to annual, the distribution of returns converges toward normality. Heavy tails diminish, and excess kurtosis declines toward zero.
PythonIn [26]Expand
import numpy as np
import pandas as pd
from scipy import stats
# Compute returns at different frequencies
prices_series = prices.copy()
# Resample to different frequencies
weekly_prices = prices_series.resample("W").last()
monthly_prices = prices_series.resample("ME").last()
quarterly_prices = prices_series.resample("QE").last()
weekly_returns = np.log(weekly_prices / weekly_prices.shift(1)).dropna()
monthly_returns = np.log(monthly_prices / monthly_prices.shift(1)).dropna()
quarterly_returns = np.log(
quarterly_prices / quarterly_prices.shift(1)
).dropna()
# Calculate kurtosis at each frequency
frequencies = ["Daily", "Weekly", "Monthly", "Quarterly"]
return_series = [
log_returns,
weekly_returns,
monthly_returns,
quarterly_returns,
]
# Calculate statistics for each frequency
agg_stats = []
for freq, ret in zip(frequencies, return_series):
agg_stats.append(
{
"Frequency": freq,
"N Obs": len(ret),
"Skewness": stats.skew(ret),
"Excess Kurt": stats.kurtosis(ret),
}
)
agg_stats_df = pd.DataFrame(agg_stats)ConsoleOut [27]Expand
Aggregational Gaussianity: Kurtosis by Frequency ======================================================= Frequency N Obs Skewness Excess Kurt ------------------------------------------------------- Daily 6260 0.0423 0.1299 Weekly 1252 0.0375 0.3172 Monthly 288 0.0314 -0.1698 Quarterly 96 0.2679 0.0138 ------------------------------------------------------- Normal distribution: Skewness = 0.0, Excess Kurtosis = 0.0
VisualizationsOut [28]Expand




VisualizationOut [29]Expand

Absence of Linear Autocorrelation
We noted earlier that raw returns show little autocorrelation. This is a stylized fact in its own right, which reflects the weak-form efficient market hypothesis: past prices should not predict future returns. If they did, you would exploit the pattern until it disappeared.
However, "little" autocorrelation is not zero autocorrelation. Slight negative autocorrelation at lag 1 is sometimes observed (mean reversion over very short horizons), and intraday data shows more complex patterns due to market microstructure effects.
PythonIn [30]Expand
# Test for autocorrelation significance using Ljung-Box test
from statsmodels.stats.diagnostic import acorr_ljungbox
# Test for autocorrelation in returns
lb_returns = acorr_ljungbox(log_returns, lags=[5, 10, 20], return_df=True)
# Test for autocorrelation in squared returns
lb_squared = acorr_ljungbox(log_returns**2, lags=[5, 10, 20], return_df=True)ConsoleOut [31]Expand
Ljung-Box Test for Autocorrelation
============================================================
Returns (H0: No autocorrelation):
lb_stat lb_pvalue
5 4.507121 0.478931
10 5.296649 0.870501
20 13.171738 0.869894
Squared Returns (H0: No autocorrelation):
lb_stat lb_pvalue
5 2.753965 0.737853
10 7.656428 0.662356
20 21.206553 0.385066
Interpretation: p-values < 0.05 reject H0The Ljung-Box test typically fails to reject the null hypothesis of no autocorrelation for raw returns but strongly rejects it for squared returns. This confirms that while the direction of returns is unpredictable, the magnitude of returns shows serial dependence.
Correlation Breakdown in Crises
A final stylized fact concerns correlations between assets, which increase during market stress. The diversification benefits you count on in normal times partially evaporate precisely when you need them most.
Even after controlling for volatility, the underlying correlation structure shifts during crises. Assets that seemed uncorrelated become highly correlated when panic sets in.
PythonIn [32]Expand
# Define crisis and calm periods
crisis_periods = [
("2008-09-01", "2009-03-31"), # Financial Crisis
("2020-02-15", "2020-04-15"), # COVID Crash
]
calm_periods = [
("2013-01-01", "2013-12-31"),
("2017-01-01", "2017-12-31"),
]
def get_corr_matrix(returns, start, end):
mask = (returns.index >= start) & (returns.index <= end)
return returns[mask].corr()
# Calculate correlations for different periods
crisis_corr_2008 = get_corr_matrix(multi_returns, "2008-09-01", "2009-03-31")
calm_corr_2017 = get_corr_matrix(multi_returns, "2017-01-01", "2017-12-31")
crisis_corr_2020 = get_corr_matrix(multi_returns, "2020-02-15", "2020-04-15")ConsoleOut [33]Expand
Correlation Structure: Crisis vs. Calm Periods
============================================================
Financial Crisis (Sep 2008 - Mar 2009):
^GSPC GLD TLT EFA
^GSPC 1.000 0.394 -0.013 0.945
GLD 0.394 1.000 0.196 0.448
TLT -0.013 0.196 1.000 0.037
EFA 0.945 0.448 0.037 1.000
Calm Period (2017):
^GSPC GLD TLT EFA
^GSPC 1.000 0.063 -0.351 0.862
GLD 0.063 1.000 0.153 0.101
TLT -0.351 0.153 1.000 -0.310
EFA 0.862 0.101 -0.310 1.000
COVID Crash (Feb - Apr 2020):
^GSPC GLD TLT EFA
^GSPC 1.000 0.050 -0.156 0.928
GLD 0.050 1.000 0.178 0.088
TLT -0.156 0.178 1.000 -0.069
EFA 0.928 0.088 -0.069 1.000During the financial crisis and COVID crash, correlations between S&P 500 and other assets shift compared to calm periods. The correlation between US and international equities (EFA) typically spikes during crises, reducing the diversification benefit of global equity allocation exactly when losses are largest.
VisualizationsOut [34]Expand


Implications for Financial Modeling
The stylized facts we've documented directly affect model choice and risk estimates in quantitative finance.
Limitations of the Normal Distribution
The normal distribution, despite its mathematical elegance, fails to capture the essential features of financial returns. Using normal assumptions leads to systematic underestimation of tail risk. The Black-Scholes model, which we'll derive in upcoming chapters, assumes log returns follow a normal distribution and volatility is constant. In light of what we've learned, these assumptions are clearly violated.
This doesn't mean Black-Scholes is useless. It remains the foundation of options pricing. But you must understand its limitations and adjust accordingly. The implied volatility smile we'll study in Part III arises precisely because the market prices in fat tails that Black-Scholes ignores.
Alternative Distributions
Several distributions better capture the heavy-tailed nature of financial returns:
- Student's t-distribution: Adds a degrees-of-freedom parameter that controls tail heaviness. As degrees of freedom decrease, tails become fatter.
- Generalized hyperbolic distributions: A flexible family that nests many special cases, allowing calibration to both skewness and kurtosis.
- Stable distributions: Capture the possibility of infinite variance, relevant for highly fat-tailed data.
PythonIn [35]Expand
import numpy as np
from scipy import stats
# Fit t-distribution to returns
params = stats.t.fit(log_returns)
df_fitted, loc_fitted, scale_fitted = params
# Compare tail probabilities
x_threshold = 4 * log_returns.std()
# Actual probability
actual_prob = np.mean(np.abs(log_returns) > x_threshold)
# Normal probability
normal_prob = 2 * (
1
- stats.norm.cdf(
x_threshold, loc=log_returns.mean(), scale=log_returns.std()
)
)
# t-distribution probability
t_prob = 2 * (
1 - stats.t.cdf(x_threshold, df_fitted, loc=loc_fitted, scale=scale_fitted)
)
# Calculate percentages for display
actual_prob_pct = actual_prob * 100
normal_prob_pct = normal_prob * 100
t_prob_pct = t_prob * 100ConsoleOut [36]Expand
Distribution Fit Comparison ================================================== Fitted t-distribution degrees of freedom: 54.85 Probability of |return| > 4σ: Actual: 0.000160 (0.0160%) Normal: 0.000069 (0.0069%) t-dist: 0.000160 (0.0160%)
The fitted t-distribution with its low degrees of freedom captures the tail behavior much better than the normal distribution. A degrees of freedom parameter around 3-5 is typical for daily equity returns.
Volatility Modeling
Volatility clustering motivates conditional heteroskedasticity models where volatility is modeled as a time-varying process. The ARCH (Autoregressive Conditional Heteroskedasticity) model introduced by Robert Engle and the GARCH generalization by Tim Bollerslev explicitly model how volatility evolves over time. These models are essential for:
- Accurate Value at Risk calculations
- Option pricing with time-varying volatility
- Portfolio optimization with realistic risk forecasts
- Understanding implied volatility dynamics
We'll explore these models in detail in Part IV on Time Series Models.
Risk Management Implications
Traditional risk measures based on normal distributions dramatically underestimate tail risk. Consider Value at Risk (VaR) at the 99% confidence level:
PythonIn [37]Expand
import numpy as np
from scipy import stats
# Calculate VaR under different distributional assumptions
confidence = 0.99
# Historical VaR (non-parametric)
historical_var = -np.percentile(log_returns, 100 * (1 - confidence))
# Normal VaR
normal_var = -stats.norm.ppf(1 - confidence) * log_returns.std()
# t-distribution VaR
t_var = -stats.t.ppf(1 - confidence, df_fitted, loc=0, scale=scale_fitted)
# Count actual exceedances
exceedances_historical = np.sum(log_returns < -historical_var)
exceedances_normal = np.sum(log_returns < -normal_var)
exceedances_t = np.sum(log_returns < -t_var)
expected_exceedances = len(log_returns) * (1 - confidence)ConsoleOut [38]Expand
Value at Risk Comparison (99% confidence) ======================================================= Method VaR Exceedances Expected ------------------------------------------------------- Historical 2.1186% 63 62.6 Normal 2.0941% 66 62.6 t-distribution 2.1171% 63 62.6
The normal distribution VaR typically shows more exceedances than expected (more days with losses exceeding the VaR threshold), indicating underestimation of risk. The t-distribution and historical approaches provide more conservative and accurate estimates.
VisualizationOut [39]Expand

Key Parameters
The key parameters for the statistical models and risk measures used in this chapter are:
- Confidence Level (): The probability that losses will not exceed the VaR threshold (e.g., 99%). Higher confidence levels result in more conservative (larger) VaR estimates.
- Degrees of Freedom (): A parameter of the Student's t-distribution controlling tail heaviness. Lower values (e.g., 3-5) indicate fatter tails; as , the distribution converges to Normal.
- Lag (): The time shift used in autocorrelation calculations. Significant autocorrelation at lag implies predictability based on observations periods ago.
Universality Across Markets
A remarkable aspect of these stylized facts is their universality. They appear in:
- Different asset classes (equities, bonds, currencies, commodities)
- Different geographic markets (US, Europe, Asia, emerging markets)
- Different time periods (pre-WWII, post-WWII, modern era)
- Different frequencies (minute, hourly, daily, weekly)
While the specific parameter values vary (emerging market equities have higher kurtosis than developed markets, currencies show less skewness than equities), the qualitative features persist. This universality suggests these patterns reflect fundamental aspects of how markets aggregate information and how participants behave, rather than artifacts of specific market structures.
The persistence of these facts across diverse settings reinforces their importance for financial modeling. Any model that ignores heavy tails, volatility clustering, or the leverage effect will systematically misjudge risk and misprice derivatives across virtually all financial markets.
Summary
This chapter established the empirical foundation for quantitative modeling by documenting the robust statistical properties of financial returns:
Heavy tails and excess kurtosis represent the most dramatic departure from normality. Extreme returns occur far more frequently than the normal distribution predicts, with daily equity returns typically showing excess kurtosis of 5-15. This has direct implications for tail risk measurement and derivative pricing.
Negative skewness characterizes many equity return distributions, which reflects the asymmetric nature of market movements: gradual rises and sudden crashes. Risk models must account for this asymmetry in the loss tail.
Volatility clustering means that market turbulence is persistent. Today's volatility strongly predicts tomorrow's volatility, with autocorrelation in squared returns remaining significant for many weeks. This violates the i.i.d. assumption underlying many classical models.
The leverage effect introduces asymmetry in volatility dynamics: negative returns increase future volatility more than positive returns. This correlation between returns and volatility changes has implications for option pricing and hedging.
Aggregational Gaussianity provides a silver lining: returns become more normal at longer horizons. Daily returns require careful non-normal treatment, but annual risk assessments can rely more heavily on Gaussian approximations.
These stylized facts motivate the sophisticated modeling techniques developed throughout the remainder of this textbook. The Brownian motion framework of the next chapter provides a mathematical foundation for continuous-time price processes, but you'll now understand why extensions like jump-diffusions and stochastic volatility are necessary to capture market reality. The gap between elegant theory and messy reality is precisely where quantitative finance earns its value.
Quiz
Ready to test your understanding? Take this quick quiz to reinforce what you've learned about the stylized facts of financial returns.
Stylized Facts of Financial Returns
Join the discussionAsk a question or share what you learned.
Keep exploring
Reference
Citation details
Cite or share this article.


Comments
Add to the conversation
A question, a correction, or an example from your own work.
No comments yet. Be the first to share your thoughts!