Stylized Facts of Financial Returns › The Leverage Effect

Michael BrenndoerferNovember 21, 20254 min read
Quantitative FinanceAll chapters
Part 5 of 15Stochastic Calculus and Pricing Theory
Chapter 25Stylized Facts of Financial Returns › The Leverage Effect
Reading section 6 of 14 · The Leverage Effect · 4 minSection 6 of 14 · 4 min

Book contents

Section 6 of 14 · 4 min read

Stylized Facts of Financial Returns › The Leverage EffectOne section · Sections in this chapter6 / 14 ↓
Reading tools Definitions & inline links

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

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.

Leverage Effect

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.

In[22]:
Code
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()
In[23]:
Code
# 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]
Out[24]:
Visualization
Bar chart of small positive and negative return-volatility cross-correlations across 20 lags.
Correlation between current returns and future squared returns at lags 1 through 20. The sample coefficients are small and alternate in sign, with negative values at several lags rather than a uniformly strong leverage pattern.
Out[25]:
Console
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.0024

The 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.