What (One-liner)
Peaks-Over-Threshold EVT: returns → losses → 90th-percentile threshold → GPD fit on exceedances → closed-form VaR + ES. scipy.stats.genpareto primary path with a historical-quantile fallback. Single-name extreme-loss tail estimation.
How to use
Drag evt_tail_var_v1 onto the Canvas (POST /algorithms/evt_tail_var/run); params.returns required, plus optional confidence=0.99 and threshold_quantile=0.9. Typical wiring:
price_factor → (derive returns) → evt_tail_var_v1 → risk overlay / reportCore formulas
losses = -returns
threshold = quantile(losses, 0.9) # POT threshold
exceedances = losses[losses > threshold] - threshold
# GPD fit (scipy):
shape, loc=0, scale = genpareto.fit(exceedances, floc=0)
# VaR (Pickands-Balkema-de Haan inverse):
p = 1 - confidence
VaR_EVT = threshold + (scale/shape) * ((n/n_u * p)**(-shape) - 1)
if |shape| > 1e-8 else threshold + scale * log((n/n_u) * p)
# Expected Shortfall:
ES_EVT = (VaR + scale - shape*threshold) / (1 - shape) if shape < 1
= NaN if shape >= 1Rounding contract: values → 6; shape → 4.
Assumptions & applicability
Assumptions: stationary tail + i.i.d. exceedances + ≥30 returns + ≥5 exceedances + scipy numerical stability + threshold_quantile=0.9 default suited to ~N(0,σ) data.
Fits: reinforcing single-name historical VaR (EVT extrapolation), GPD shape as a fat-tail indicator, regulatory reporting.
Does not fit: portfolio VaR (use portfolio_risk_metrics), intraday, non-stationary series, bootstrap-CI needs, rigorous threshold selection.
Input / Output contract
{returns, confidence?=0.99, threshold_quantile?=0.9} → Primary: {var_evt, es_evt, confidence, threshold, n_exceedances, gpd_shape, gpd_scale, historical_var, _method='POT_GPD (scipy.stats.genpareto)'}; Fallback: {var_evt, es_evt, confidence, _method='historical_quantile_fallback'}; Error: {error, message}.
Known limitations
- threshold_quantile=0.9 default is ad-hoc: academic POT picks thresholds via mean-excess plots; this operator fixes 90%
- No threshold diagnostic (shape is threshold-sensitive; sweep it yourself)
- No bootstrap CI (point estimates)
- Serial correlation untreated (GARCH filter unimplemented)
- ES = NaN when shape >= 1 (client must tolerate)
- Fallback uses historical quantile, no EVT extrapolation
- Fallback ES definition is non-standard (
sort_desc[:int(n*(1-c))].mean()) - No explicit convergence diagnostics on the scipy fit (silent → fallback)
References
McNeil-Frey (2000) EVT for financial VaR + Coles (2001) Statistical Modeling of Extreme Values + Embrechts-Klüppelberg-Mikosch (1997) Modelling Extremal Events + Pickands (1975) Pickands-Balkema-de Haan theorem. Full citations in the frontmatter.
Golden Test
tests/golden/fixtures/tier3/evt_tail_var/ — scipy reference, 1e-6 tolerance, 11 tests:
fat_tail_student_t— t(df=4) 500 obs → positive shapenormal_returns_99— near-zero shape, ≈ historical_varnormal_returns_95— same data, different confidence → different VaRinjected_outliers— 5 fixed extreme losses → shape ≈ 0.6, ES >> VaRtoo_short_returns— < 30 obs → errorinsufficient_exceedances— threshold=0.99 → < 5 exceedances → errordeterminism— 5 reruns identicalconfidence_monotonic— VaR(0.9) < VaR(0.95) < VaR(0.99)rounding_contract— round-6 values / round-4 shaperesult_structure_primary— 9 fixed keysn_exceedances_positive— > 5; threshold matches exactly
Changelog
- 1.0.0 (2026-04-20) — First Active (Tier 3 batch 2.3)

