Common Probability Distributions in Finance › Fat-Tailed Distributions

Michael BrenndoerferOctober 21, 202521 min read
Quantitative FinanceAll chapters
Part 1 of 15Mathematical Foundations
Chapter 3Common Probability Distributions in Finance › Fat-Tailed Distributions
Reading section 5 of 8 · Fat-Tailed Distributions · 21 minSection 5 of 8 · 21 min

Book contents

Section 5 of 8 · 21 min read

Common Probability Distributions in Finance › Fat-Tailed DistributionsOne section · Sections in this chapter5 / 8 ↓
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 normal distribution is only one possible model for returns. When an observed or simulated return distribution places more probability in sufficiently remote tails than a fitted normal model, normal tail calculations can misstate extreme-event frequencies and downstream risk measures. The examples below use a seeded Student's t sample to show how to diagnose that mismatch without presenting synthetic observations as market data.

Evidence of Fat Tails

Here, "fat tails" means slower remote-tail decay than a specified normal benchmark. The comparison requires a common location/scale convention and sufficiently large thresholds; it does not order every finite-threshold probability. For models without a finite mean, tail behavior must be described relative to another location parameter rather than an undefined mean. Two useful diagnostics follow:

Kurtosis: The normal distribution has a kurtosis of 3 (or excess kurtosis of 0). Kurtosis is the fourth central moment divided by the variance squared. A higher value signals more weight on large standardized deviations overall, but it does not by itself order every tail probability or identify where probability moved between the center and shoulders.

Tail exponents: For power-law-tailed distributions, the probability of extreme events decays polynomially rather than exponentially. The tail exponent α\alpha characterizes how fast this decay occurs. A smaller α\alpha means slower polynomial decay and fatter tails, while a larger α\alpha means faster polynomial decay and thinner tails within the power-law family. For every finite α\alpha, however, the asymptotic tail remains heavier than a normal tail.

Out[22]:
Console
Simulated Daily Returns (Heavy-Tailed, t-distribution df=4)
==================================================
Number of observations: 6,000
Mean daily return: 0.0079%
Daily volatility: 1.3944%
Skewness: 0.086
Excess Kurtosis: 5.719

Extreme Event Frequency
--------------------------------------------------
Event             Observed  Expected (Normal)      Ratio
--------------------------------------------------
|r| > 3σ                80               16.2        4.9x
|r| > 4σ                30                0.4       78.9x
|r| > 5σ                 9                0.0     2616.4x

The seeded sample was generated from a heavy-tailed distribution, so its departure from the normal benchmark is expected rather than empirical evidence about markets. For this seed, absolute moves beyond four sample standard deviations occur 30 times, compared with about 0.38 expected in 6,000 independent normal draws, a ratio of about 79. Nine observations exceed five sample standard deviations, compared with about 0.0034 under that same normal benchmark. The exercise demonstrates how a known heavy-tailed data-generating process changes extreme-event counts; it does not estimate their frequency in an observed market.

The Student's t Distribution

The Student's t distribution provides a simple way to model fat tails while retaining many convenient properties of the normal distribution. William Sealy Gosset published small-sample work under the pseudonym "Student" while working as an experimental brewer at Guinness. In finance, a location-scale t model is one candidate for forecasting or fitting heavy-tailed returns; its suitability still needs to be checked.

Student's t Distribution

A probability distribution with heavier tails than the normal distribution, characterized by its degrees of freedom parameter ν\nu. As ν→∞\nu \to \infty, it converges to the normal distribution. For small ν\nu, it assigns higher probability to extreme events.

The probability density function of the standard t distribution with ν>0\nu>0 degrees of freedom is:

f(x)=Γ(ν+12)νπ Γ(ν2)(1+x2ν)−ν+12f(x) = \frac{\Gamma\left(\frac{\nu+1}{2}\right)}{\sqrt{\nu\pi}\,\Gamma\left(\frac{\nu}{2}\right)} \left(1 + \frac{x^2}{\nu}\right)^{-\frac{\nu+1}{2}}

This formula may look complex, but its structure shows the key difference between the t distribution and the normal distribution. While the normal distribution uses the exponential function e−x2/2e^{-x^2/2} which decays very rapidly, the t distribution uses a power function (1+x2/ν)−(ν+1)/2(1 + x^2/\nu)^{-(\nu+1)/2} which decays much more slowly. This slower decay is precisely what produces the heavier tails.

Each component:

  • xx: the value at which we evaluate the density
  • ν\nu: degrees of freedom parameter (controls tail thickness; lower ν\nu means fatter tails)
  • Γ(⋅)\Gamma(\cdot): the gamma function, generalizing the factorial to non-integer values (Γ(n)=(n−1)!\Gamma(n) = (n-1)! for positive integers)
  • Γ(ν+12)νπ Γ(ν2)\frac{\Gamma\left(\frac{\nu+1}{2}\right)}{\sqrt{\nu\pi}\,\Gamma\left(\frac{\nu}{2}\right)}: a normalization constant ensuring the density integrates to 1
  • (1+x2/ν)−(ν+1)/2(1 + x^2/\nu)^{-(\nu+1)/2}: the core term that produces polynomial rather than exponential tail decay

The polynomial decay (1+x2/ν)−(ν+1)/2(1 + x^2/\nu)^{-(\nu+1)/2} versus the normal's exponential decay e−x2/2e^{-x^2/2} is why the t distribution has heavier tails. To understand this intuitively, consider what happens as xx becomes large. For the normal distribution, doubling xx quadruples the magnitude of the exponent because (2x)2=4x2(2x)^2 = 4x^2, so the density decays extremely rapidly. For the t distribution, the density is asymptotically proportional to ∣x∣−(ν+1)|x|^{-(\nu+1)}, so doubling a large threshold reduces the density by about 2−(ν+1)2^{-(\nu+1)}. Its one-sided survival probability is proportional to x−νx^{-\nu}, so doubling a large threshold reduces that probability by about 2−ν2^{-\nu}. As ν→∞\nu \to \infty, the polynomial term converges to the exponential, and the t distribution approaches the normal.

Key properties of the t distribution:

  • Degrees of freedom (ν\nu): Controls tail thickness. Lower values mean fatter tails.
  • Convergence: As ν→∞\nu \to \infty, the t distribution approaches the standard normal.
  • Variance: For ν>2\nu > 2, the variance is ν/(ν−2)\nu/(\nu-2), which is always greater than 1. For 1<ν≤21<\nu\leq2, the mean exists but the variance is infinite. For 0<ν≤10<\nu\leq1, the mean and therefore the variance are undefined. The second raw moment diverges throughout 0<ν≤20<\nu\leq2.
  • Kurtosis: For ν>4\nu > 4, excess kurtosis is 6/(ν−4)6/(\nu-4), always positive and decreasing in ν\nu. For ν≤4\nu \leq 4, the kurtosis is undefined because the fourth moment does not exist.

When fitting returns, the degrees of freedom must be estimated or otherwise justified for the dataset and model; there is no universal range for financial returns. Values near 4 illustrate the family's moment boundary: for 0<ν≤40<\nu\leq4, the fourth moment diverges, while for ν>4\nu>4 it is finite. The seeded example uses ν=4\nu=4 deliberately.

Out[23]:
Visualization
Line plot of normal and t distribution PDFs for multiple degrees of freedom on a linear y-axis.
Normal and Student's t densities, all centered at zero and scaled to unit variance. Lower degrees of freedom give slower remote-tail decay; the scaling controls for overall variance.
Semilog-y plot of the same PDFs to emphasize tail differences.
The same unit-variance densities on a logarithmic density axis, exposing their remote-tail differences. Density heights are not exceedance probabilities.

The log-scale plot shows density heights, not probabilities beyond a threshold. At x=5x=5, the unit-variance t distribution with 3 degrees of freedom has about 633 times the normal density. Integrating beyond 5 gives a different comparison: its one-sided survival probability is about 5,650 times the normal survival probability. The normal probability is about 1 in 3.5 million observations; the scaled t probability is about 1 in 600. For independent daily draws and 252 trading days per year, the expected waiting times are roughly 13,900 trading years and 2.4 trading years. These are model-based averages, not event schedules.

Fitting the t Distribution to Simulated Returns

We can fit a location-scale t distribution to the seeded heavy-tailed sample and compare the log-likelihoods evaluated at the returned numerical fits. This does not establish a global maximum for the unrestricted t likelihood. Because the sample was generated from a t distribution, it is a reproducibility check and model-comparison demonstration, not evidence about observed market returns. For ν>2\nu>2, the t standard deviation is sν/(ν−2)s\sqrt{\nu/(\nu-2)}, where ss is its scale parameter; scale itself is not the standard deviation. For 0<ν≤20<\nu\leq2, no finite standard deviation exists.

In[24]:
Code
import numpy as np
from scipy import stats

# Fit a scaled t distribution to the simulated returns
# The t distribution has location, scale (not SD), and degrees of freedom.
# Use the returns array from the fat-tails-evidence cell
# Ensure returns is available and convert to numpy array
# Regenerate data to ensure availability (matching fat-tails-evidence cell)
np.random.seed(42)
n_days = 6000
daily_returns = np.random.standard_t(df=4, size=n_days) * 0.01 + 0.0003
returns_array = daily_returns

# Numerical likelihood fitting; no global-maximization guarantee.
t_params = stats.t.fit(returns_array)
t_nu, t_loc, t_scale = t_params

# Fit normal for comparison
norm_loc, norm_scale = stats.norm.fit(returns_array)

# Calculate log-likelihoods
t_loglik = np.sum(stats.t.logpdf(returns_array, t_nu, t_loc, t_scale))
norm_loglik = np.sum(stats.norm.logpdf(returns_array, norm_loc, norm_scale))
Out[25]:
Console
Distribution Fitting Results
==================================================

Normal Distribution:
  Location (μ): 0.000079
  Scale (σ): 0.013944
  Log-likelihood: 17,123

Student's t Distribution:
  Degrees of freedom (ν): 4.23
  Location: 0.000055
  Scale: 0.010172
  Log-likelihood: 17,526

Log-likelihood improvement: 403
The t distribution provides a substantially better fit to the data.

For this seed, the returned t fit has about 4.23 degrees of freedom and a log-likelihood about 403 higher than the fitted normal model. That difference is expected because the sample was generated from a t distribution. Exponentiating it gives a likelihood ratio evaluated at those fits, not posterior odds; posterior model odds require priors and marginal likelihoods.

Out[26]:
Visualization
Full-range histogram of all 6,000 simulated returns with overlaid normal and t density curves.
Fitted distributions compared with all 6,000 simulated heavy-tailed returns, including the seeded sample extremes. The Student's t distribution better captures the peaked center and fat tails.
Q-Q scatter plots of all 6,000 sample values versus fitted-normal and fitted-t theoretical quantiles with a 45-degree reference line.
Q-Q plots of all 6,000 simulated returns against the fitted normal and fitted t distributions, showing where the normal fit breaks down in the tails.

Power-Law Tails

Power-law notation provides a general way to describe polynomial tail decay. Student's t distributions already have power-law tails, but other regularly varying models can use a different exponent or slowly varying factor:

P(X>x)∼Cx−αas x→∞,C>0, α>0P(X > x) \sim Cx^{-\alpha} \quad \text{as } x \to \infty, \qquad C>0,\ \alpha>0

This asymptotic relationship describes the right-tail survival function for large thresholds. It includes the polynomial right tail of a Student's t distribution as a special case. For a nonnegative variable, the exponent controls positive moments; for a signed variable, existence of a full moment also depends on the left tail, which this one-sided relationship does not specify.

The components of this relationship are:

  • P(X>x)P(X > x): the probability that the random variable exceeds value xx (the survival function)
  • xx: a large threshold value
  • α\alpha: the tail exponent, which controls how fast probabilities decay. Some authors also call α\alpha a tail index; in another common extreme-value-theory convention, the tail index is ξ=1/α\xi=1/\alpha
  • ∼\sim: denotes asymptotic equivalence. The ratio of the left side to Cx−αCx^{-\alpha} approaches 1 as x→∞x \to \infty

A larger α\alpha means faster polynomial decay and a smaller α\alpha means slower decay. As an illustration, if α=3\alpha=3 and the asymptotic approximation is appropriate at both thresholds, doubling a sufficiently large threshold reduces its exceedance probability by approximately 2−3=1/82^{-3}=1/8. This is much slower than the exponential falloff of normal tails.

For comparison, the one-sided standard-normal survival probability falls by a factor of about 5.1×10105.1\times 10^{10} from 4σ to 8σ, roughly eleven orders of magnitude. Under the illustrative cubic law, the corresponding asymptotic reduction is only a factor of 8. Which approximation is appropriate must be established from a specified model or dataset rather than inferred from this contrast alone.

Power Law Distribution

A distribution where the probability of observing a value greater than xx decays as a power of xx: P(X>x)∝x−αP(X > x) \propto x^{-\alpha}. The tail exponent α\alpha determines how fat the tails are. Lower α\alpha means fatter tails.

Out[27]:
Visualization
Log-log plot comparing normal and unit-variance t survival functions with illustrative power-law tails aligned at a unitless threshold of one.
Tail decay on a common unitless threshold axis. The normal and unit-variance t curves are survival functions; the illustrative power-law tails are aligned to the normal survival probability at x=1 and do not define complete standardized distributions. Power-law tails appear as straight lines on the log-log scale, while the normal tail curves downward.

The implications of power-law tails are serious for risk management:

  • Moment thresholds require both tails: For p>0p>0 and a nonnegative variable with P(X>x)∼Cx−αP(X>x)\sim Cx^{-\alpha}, the right-tail contribution to E[Xp]E[X^p] is finite for p<αp<\alpha and diverges for p≥αp\geq\alpha. For a signed return, the displayed right-tail relation alone cannot guarantee a full mean or variance because the left tail is unspecified. If both tails have such power-law asymptotics with exponents αL\alpha_L and αR\alpha_R, absolute moments exist for p<min⁡(αL,αR)p<\min(\alpha_L,\alpha_R) and diverge at or above that minimum. In particular, when both exponents lie strictly between 2 and 4, the mean and variance are finite, while the fourth moment is not.
  • Slow convergence: Under i.i.d. sampling with finite variance (α>2\alpha > 2 in this tail model), the Central Limit Theorem applies, but its normal approximation can remain poor at practically available sample sizes.
  • Tail concentration and dependence: With power-law marginal tails, a small number of observations can dominate a sample risk estimate. Diversification may be weaker when those tails combine with strong common or tail dependence, but marginal tail thickness alone does not determine diversification benefits.

Implications for Risk Management

Fat tails change how we should think about risk:

VaR model error: A fitted normal model can underestimate sufficiently extreme heavy left-tail quantiles, but it need not underestimate VaR at every confidence level. The direction and magnitude depend on the fitted distributions, skewness, and confidence level. The seeded example below slightly overestimates 95% VaR and underestimates 99% VaR.

Expected shortfall: Under the continuous-loss convention used here, expected shortfall is the mean loss conditional on being at or beyond the VaR threshold. Terminology such as "Conditional VaR" varies across sources, and distributions with atoms require a quantile-integral definition to handle the boundary consistently. For continuous heavy-tailed models, ES can be substantially larger than VaR.

The code treats the sample as an empirical distribution with equal probability on each observation. VaR is its inverse-CDF loss quantile. ES averages exactly the worst 1−α1-\alpha probability mass, using a fractional boundary weight when necessary rather than including every loss tied at VaR. For example, losses 0,0,10,0,1 at 50% confidence give VaR 00 and ES 2/32/3, not the full-sample mean 1/31/3. Finite-sample VaR depends on the quantile convention; the inverse-CDF convention here differs from a linearly interpolated percentile. The ratio ES/VaR is undefined when VaR is zero.

In[28]:
Code
import numpy as np
from scipy import stats


def calculate_var_es(returns, confidence_levels=(0.95, 0.99)):
    """
    Empirical loss VaR and quantile-integral Expected Shortfall.

    VaR uses the inverse empirical CDF. ES averages exactly the upper
    (1-confidence) probability mass, including fractional boundary weights.

    Returns:
        Dictionary with VaR and ES for each confidence level
    """
    returns = np.asarray(returns, dtype=float)
    if returns.ndim != 1 or returns.size == 0 or not np.all(np.isfinite(returns)):
        raise ValueError("returns must be a nonempty, finite one-dimensional sample")
    losses = -returns
    descending_losses = np.sort(losses)[::-1]
    results = {}
    for cl in confidence_levels:
        if not (np.isfinite(cl) and 0 < cl < 1):
            raise ValueError("Confidence levels must lie strictly between 0 and 1")
        # Integer arithmetic preserves the supplied float's exact CDF rank.
        numerator, denominator = float(cl).as_integer_ratio()
        rank = (len(losses) * numerator + denominator - 1) // denominator
        var = descending_losses[len(losses) - rank]
        tail_mass = len(losses) * (1 - cl)
        weights = np.clip(tail_mass - np.arange(len(losses)), 0, 1)
        # Scale before averaging to avoid overflowing a finite weighted mean.
        loss_scale = np.max(np.abs(descending_losses))
        if loss_scale == 0:
            es = 0.0
        else:
            scaled_losses = descending_losses / loss_scale
            scaled_mean = np.dot(weights / tail_mass, scaled_losses)
            # Roundoff cannot move a weighted mean outside the sample range.
            scaled_mean = np.clip(scaled_mean, scaled_losses.min(), scaled_losses.max())
            es = scaled_mean * loss_scale
            cancellation_scale = np.dot(weights / tail_mass, np.abs(scaled_losses))
            if cl < 1e-8 or abs(scaled_mean) < 1e-8 * cancellation_scale:
                # Rare cancellation-sensitive tails: retain exact atom weights
                # and input floats, then round the final bounded mean once.
                from fractions import Fraction

                mass_numerator = len(losses) * (denominator - numerator)
                whole, remainder = divmod(mass_numerator, denominator)
                exact_total = sum(
                    (Fraction.from_float(float(x)) for x in descending_losses[:whole]),
                    Fraction(0),
                )
                if remainder:
                    exact_total += Fraction(
                        remainder, denominator
                    ) * Fraction.from_float(float(descending_losses[whole]))
                es = float(exact_total / Fraction(mass_numerator, denominator))
        # A zero VaR has no defined ES/VaR ratio.
        results[cl] = {"VaR": var, "ES": es, "ES/VaR": es / var if var != 0 else np.nan}
    return results


# Regenerate data to ensure availability
np.random.seed(42)
n_days = 6000
returns_array = np.random.standard_t(df=4, size=n_days) * 0.01 + 0.0003
norm_loc, norm_scale = stats.norm.fit(returns_array)

# Compare the simulated sample with a fitted normal model
simulated_risk = calculate_var_es(returns_array)

# Normal assumption
norm_var_95 = -stats.norm.ppf(0.05, norm_loc, norm_scale)
norm_var_99 = -stats.norm.ppf(0.01, norm_loc, norm_scale)


# For normal (reported as a positive loss), ES = -mu + sigma * phi(z) / alpha where phi is the pdf
def normal_es(alpha, mu, sigma):
    """Signed normal tail loss; alpha is return-tail mass, not confidence.

    The scaled tail ratio avoids forming tiny pdf/CDF terms separately.
    Double-precision quantile rounding still limits accuracy when the mean and
    tail terms nearly cancel. A nonfinite calculation after the affine fallback
    is reported as a numerical range failure, not proof about an exact residual.
    """
    from scipy.special import erfcx

    if not (np.isfinite(alpha) and 0 < alpha < 1):
        raise ValueError("alpha must lie strictly between 0 and 1")
    if not (np.isfinite(mu) and np.isfinite(sigma) and sigma >= 0):
        raise ValueError("mu must be finite and sigma nonnegative and finite")
    z = stats.norm.ppf(alpha)
    # phi(z)/Phi(z) through a scaled error function: neither tiny term is formed.
    tail_ratio = np.sqrt(2 / np.pi) / erfcx(-z / np.sqrt(2))
    with np.errstate(over="ignore", invalid="ignore"):
        es = -mu + sigma * tail_ratio
        if not np.isfinite(es):
            scale = max(abs(mu), sigma)
            es = (-(mu / scale) + (sigma / scale) * tail_ratio) * scale
    if not np.isfinite(es):
        raise FloatingPointError(
            "Normal ES calculation remains nonfinite after scaling"
        )
    return es


norm_es_95 = normal_es(0.05, norm_loc, norm_scale)
norm_es_99 = normal_es(0.01, norm_loc, norm_scale)
Out[29]:
Console
Risk Measure Comparison: Simulated Sample vs Normal Assumption
=================================================================
Measure                 Simulated       Normal        Ratio
-----------------------------------------------------------------
95% VaR                   2.1177%      2.2856%         0.93x
95% ES                    3.1541%      2.8682%         1.10x
99% VaR                   3.6797%      3.2358%         1.14x
99% ES                    5.0384%      3.7084%         1.36x
-----------------------------------------------------------------

The simulated 95% VaR is lower than the normal estimate,
while ES and the 99% measures are higher for this seed.

The table does not show uniform underestimation. For this seed, the simulated 95% VaR is about 7% lower than the fitted-normal estimate, while simulated 95% ES is about 10% higher. At 99%, simulated VaR is about 14% higher and simulated ES about 36% higher. The example therefore supports checking each risk measure and confidence level separately; it does not establish a universal multiplier or a claim about regulatory capital.

Out[30]:
Visualization
Grouped bar chart comparing simulated-sample and fitted-normal VaR and ES at 95% and 99% confidence levels.
Comparison of VaR and Expected Shortfall for a simulated heavy-tailed sample and its fitted normal model. The normal model slightly overestimates 95% VaR but underestimates ES and both 99% measures for this seed.