Skip to content

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 / report

Core 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 >= 1

Rounding 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

  1. threshold_quantile=0.9 default is ad-hoc: academic POT picks thresholds via mean-excess plots; this operator fixes 90%
  2. No threshold diagnostic (shape is threshold-sensitive; sweep it yourself)
  3. No bootstrap CI (point estimates)
  4. Serial correlation untreated (GARCH filter unimplemented)
  5. ES = NaN when shape >= 1 (client must tolerate)
  6. Fallback uses historical quantile, no EVT extrapolation
  7. Fallback ES definition is non-standard (sort_desc[:int(n*(1-c))].mean())
  8. 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 shape
  • normal_returns_99 — near-zero shape, ≈ historical_var
  • normal_returns_95 — same data, different confidence → different VaR
  • injected_outliers — 5 fixed extreme losses → shape ≈ 0.6, ES >> VaR
  • too_short_returns — < 30 obs → error
  • insufficient_exceedances — threshold=0.99 → < 5 exceedances → error
  • determinism — 5 reruns identical
  • confidence_monotonic — VaR(0.9) < VaR(0.95) < VaR(0.99)
  • rounding_contract — round-6 values / round-4 shape
  • result_structure_primary — 9 fixed keys
  • n_exceedances_positive — > 5; threshold matches exactly

Changelog

  • 1.0.0 (2026-04-20) — First Active (Tier 3 batch 2.3)

Verifiable intelligence for the decisions that demand scrutiny.