← Back to Algorithmic Glossary

Fat Tails (Kurtosis) in Python

The tendency of financial return distributions to exhibit more frequent and severe extreme events than a normal distribution predicts.

Definition

Fat Tails, formally characterized by excess kurtosis (leptokurtosis), describe the empirical reality that financial returns are not normally distributed. Extreme events — crashes, flash crashes, liquidity crises — occur far more frequently than Gaussian models predict. A normal distribution has a kurtosis of 3 (excess kurtosis of 0). Daily equity returns typically exhibit excess kurtosis of 4–10, meaning tail events are exponentially more probable than standard models assume. This single statistical fact invalidates a vast proportion of academic portfolio theory when applied naively to live markets.

Quantitative Formula

Kurt=1Ni=1N(RiRˉ)4(1Ni=1N(RiRˉ)2)2Kurt = \frac{\frac{1}{N}\sum_{i=1}^{N}(R_i - \bar{R})^4}{\left(\frac{1}{N}\sum_{i=1}^{N}(R_i - \bar{R})^2\right)^2}

Where NN is the number of observations, RiR_i is the return at period ii, and Rˉ\bar{R} is the mean return. Excess Kurtosis is defined as Kurt3Kurt - 3: a value of 0 indicates normality, positive values indicate fat tails (leptokurtic), and negative values indicate thin tails (platykurtic). Skewness =1N(RiRˉ)3σ3= \frac{\frac{1}{N}\sum(R_i-\bar{R})^3}{\sigma^3} complements kurtosis by measuring the asymmetry of the distribution.

Why It Matters in Backtesting

A backtesting framework that assumes normal returns will compute VaR, position sizes, and stop losses that are catastrophically too tight. The 2008 financial crisis produced daily moves that a Gaussian model would classify as '25-sigma events' — events that should occur once in the lifetime of the universe. In practice, a strategy must be stress-tested against fat-tail scenarios explicitly, and risk models should use Student-t or Pareto distributions for tail estimation instead of Gaussian assumptions.

Python Implementation

import numpy as np
    import pandas as pd
    from scipy import stats

    def analyze_tail_risk(returns: pd.Series) -> dict:
        """
        Analyzes distribution tail characteristics using kurtosis, skewness,
        normality tests, and empirical tail event frequency.
        """
        excess_kurtosis = stats.kurtosis(returns.dropna())  # Fisher definition (normal=0)
        skewness = stats.skew(returns.dropna())
        # Jarque-Bera normality test
        jb_stat, jb_p_value = stats.jarque_bera(returns.dropna())
        # Empirical tail frequency vs. Gaussian prediction
        std = returns.std()
        mean = returns.mean()
        empirical_3sigma = (returns.abs() > 3 * std).mean()
        gaussian_3sigma = 2 * (1 - stats.norm.cdf(3))  # ~0.27%
        tail_ratio = empirical_3sigma / gaussian_3sigma if gaussian_3sigma > 0 else np.inf
        return {
            "excess_kurtosis": excess_kurtosis,
            "skewness": skewness,
            "is_leptokurtic": excess_kurtosis > 0,
            "jarque_bera_statistic": jb_stat,
            "normality_rejected_p005": jb_p_value < 0.05,
            "empirical_3sigma_frequency": empirical_3sigma,
            "gaussian_3sigma_frequency": gaussian_3sigma,
            "tail_amplification_factor": tail_ratio  # >1 = fatter than normal
        }

Test this in a live environment

Stop running Jupyter notebooks locally. Paste this Fat Tails (Kurtosis) code directly into Valetha's Strategy Lab and run a full historical backtest in seconds.

Open the Python Strategy Lab

Ready to find your edge ?

Start for Free