From conditional exceedances to a loss quantile
Define loss as and choose a high threshold . For excesses given , the generalised Pareto approximation gives
on and , with scale . The limit is exponential. Positive gives a heavy right tail and negative gives a finite upper endpoint. Estimate the exceedance rate from the fraction of observations above . For a target tail probability , set . This gives
For , use . The approximation comes from modelling only the tail beyond a sufficiently high threshold, so check parameter stability across plausible choices of . Too low a threshold can bias the fit. Too high a threshold leaves few observations and wide uncertainty.
The sign of the shape parameter also affects what can be extrapolated. With , the fitted tail extends indefinitely and decays like a power. With , the formula has an upper endpoint at . An estimated negative shape does not establish a hard upper bound on real market losses. It means that this fitted tail implies one. Check how much the endpoint and high quantiles move when you change the threshold or fit on a different period, especially when a few observations determine the estimate.
What a 99% estimate inherits from the threshold choice
Suppose , , , and . The estimated 99% loss quantile uses , giving
The model estimates that losses exceed 5.28% on about 1% of comparable days. This is a model quantile. The chance of a cluster of exceedances over several days depends on their serial dependence. To model time-varying daily risk, you can fit the tail to standardised innovations after estimating conditional volatility, then transform the innovation quantile back into today's return units.
Evaluate the fitted tail quantile
The example starts with synthetic heavy-tailed losses in decimal units. For a market study, replace the synthetic array with losses from a calibration window and compare forecasts against subsequent exceedances.
import numpy as np
from scipy.stats import genpareto
rng = np.random.default_rng(39)
losses = 0.012 * rng.standard_t(df=4, size=4_000)
u = np.quantile(losses, 0.95)
excess = losses[losses > u] - u
p_u = len(excess) / len(losses)
xi, _, beta = genpareto.fit(excess, floc=0)
p = 0.01
if not 0 < p < p_u:
raise ValueError("Tail probability must lie below the exceedance rate.")
q = u + genpareto.isf(p / p_u, c=xi, scale=beta)
print("99% loss quantile:", q)