"""Tests for scripts/var_breach.py — VaR breach rate + Kupiec POF (jepa-fx-risk#12). Golden tests first: verify the math before wiring it into train.py. """ import importlib.util import math from pathlib import Path import pytest _SCRIPT = Path(__file__).parent.parent / "scripts" / "var_breach.py" def _import(): spec = importlib.util.spec_from_file_location("var_breach", _SCRIPT) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) return mod @pytest.fixture() def mod(): return _import() # --------------------------------------------------------------------------- # var_breach_rate # --------------------------------------------------------------------------- class TestVarBreachRate: def test_zero_breaches(self, mod): # 0.02 < 0.01×2.326=0.02326 → no breach rate, _ = mod.var_breach_rate([0.01, 0.01], [0.02, 0.02]) assert rate == 0.0 def test_all_breach(self, mod): # 0.03 > 0.02326 → all breach rate, _ = mod.var_breach_rate([0.01, 0.01], [0.03, 0.03]) assert rate == 1.0 def test_golden_two_of_ten(self, mod): pred = [0.01] * 10 actual = [0.01] * 10 actual[0] = 0.03 # breach actual[2] = 0.03 # breach rate, kupiec_p = mod.var_breach_rate(pred, actual) assert abs(rate - 0.2) < 1e-9, f"rate={rate}" assert kupiec_p < 0.05, f"kupiec_p={kupiec_p}" # strong reject def test_perfect_calibration(self, mod): # n=100, 1 breach → p_hat=0.01=p0=0.01 → LR=0 → kupiec_p≈1 pred = [0.01] * 100 actual = [0.015] * 100 actual[0] = 0.025 # 0.025 > 0.02326 → breach rate, kupiec_p = mod.var_breach_rate(pred, actual) assert abs(rate - 0.01) < 1e-9 assert kupiec_p > 0.9, f"kupiec_p={kupiec_p}" def test_boundary_at_var_is_not_breach(self, mod): # exactly at VaR_99 is NOT a breach (strict >) z99 = 2.326 var = 0.01 * z99 rate, _ = mod.var_breach_rate([0.01], [var], z99=z99) assert rate == 0.0 def test_empty_returns_zero_one(self, mod): rate, kupiec_p = mod.var_breach_rate([], []) assert rate == 0.0 assert kupiec_p == 1.0 def test_metric_key_no_whitespace(self, mod): key = mod.METRIC_KEY assert key == key.strip(), f"metric key has surrounding whitespace: {key!r}" assert " " not in key, f"metric key contains space: {key!r}" def test_metric_key_is_canonical(self, mod): assert mod.METRIC_KEY == "VaR_breach_rate_99_oos_regime_cond" # --------------------------------------------------------------------------- # kupiec_pvalue # --------------------------------------------------------------------------- class TestKupiecPValue: def test_perfectly_calibrated(self, mod): # p_hat == p0 → LR=0 → p-value=1 p = mod.kupiec_pvalue(100, 1, p0=0.01) assert p > 0.99, f"p={p}" def test_strong_reject_high_breach(self, mod): # 20% breach when 1% expected → p << 0.05 p = mod.kupiec_pvalue(100, 20, p0=0.01) assert p < 0.001, f"p={p}" def test_zero_breaches_not_nan(self, mod): p = mod.kupiec_pvalue(100, 0, p0=0.01) assert not math.isnan(p) assert 0 <= p <= 1.0 def test_all_breaches_not_nan(self, mod): p = mod.kupiec_pvalue(10, 10, p0=0.01) assert not math.isnan(p) assert p < 0.001 # extremely unlikely def test_zero_observations(self, mod): p = mod.kupiec_pvalue(0, 0) assert p == 1.0