VaR Backtesting: Kupiec and Christoffersen Tests

A 99% one-day Value-at-Risk forecast says that losses should exceed the threshold on about 1% of eligible days. It is a probability forecast, not a statement that losses cannot be larger. Backtesting asks two separate questions: did exceptions occur at the promised frequency, and did they arrive independently? Start with the definition in Value at Risk; a test cannot rescue a VaR series computed with stale prices or the wrong holding-period convention.

Build the exception sequence

Align yesterday's forecast with today's realized P&L, define I_t = 1 if loss exceeds VaR, and exclude days on which the portfolio or model was materially unavailable. For probability p, there are x=sum(I) exceptions in T observations.

PropertyTestNull hypothesis
CoverageKupiec POFException probability is p
IndependenceChristoffersen INDExceptions are iid over time
Conditional coveragePOF + INDBoth are true

Kupiec's proportion-of-failures test

The likelihood ratio compares the fixed model probability p with the observed rate x/T:

LR_pof = -2 log(((1-p)^(T-x) p^x) /
               ((1-x/T)^(T-x) (x/T)^x))

Under regularity conditions it is asymptotically chi-squared with one degree of freedom. Rejecting means too many or too few exceptions; too few can indicate an excessively conservative model, but it is not automatically a commercial problem. With 250 days at 99%, the expected count is only 2.5, so exact binomial intervals are often more informative than asymptotics.

Christoffersen independence

Coverage can look correct while exceptions cluster during volatility spikes. Count transitions n00, n01, n10, n11 in the binary sequence. Under independence the chance of tomorrow's exception is one probability; under the alternative it depends on today's state. The likelihood-ratio statistic compares those likelihoods. Conditional coverage is LRcc = LRpof + LR_ind and has two degrees of freedom.

import numpy as np
from scipy.stats import chi2

def kupiec(exceptions, p=0.01):
    I = np.asarray(exceptions, dtype=int); T, x = len(I), I.sum()
    phat = np.clip(x / T, 1e-12, 1 - 1e-12)
    lr = -2 * np.log(((1-p)**(T-x)*p**x) / ((1-phat)**(T-x)*phat**x))
    return lr, chi2.sf(lr, 1)

For production, calculate likelihoods in log space and handle zero transition counts.

Interpretation and limits

A non-rejection is not proof that risk is correct: tests have low power for short samples, and a model can pass frequency while badly understating the size of tail losses. Pair VaR backtests with expected-shortfall, P&L attribution, traffic-light counts, and stress scenarios. Diagnose clusters rather than merely recalibrating a quantile: they may reveal volatility persistence, missing jump risk, or a portfolio mapping error.

Implementation discipline

The model is only one layer of the trade. Store input timestamps, vendor identifiers, contract specifications, FX conversion conventions, and the exact version of each risk model alongside every signal. A result that cannot be reproduced from raw inputs is not a research result; it is an anecdote. Use point-in-time constituent data, distinguish an indicative quote from an executable quote, and make the decision clock explicit. For an execution-sensitive strategy, a close price or composite midpoint is usually an unattainable benchmark rather than a fill.

Evaluate the full distribution, not just a headline Sharpe: drawdown, tail loss, turnover, capacity, exposure concentration, and degradation during the stressed periods that motivated the idea. Split design, parameter choice, and final evaluation across separate samples. If alternatives were explored, record them and account for the search using the Deflated Sharpe Ratio. Finally, run a paper or shadow phase with the same order, reconciliation, and limit logic planned for production. A backtest should establish a conditional expectation, not a promise of a tradable return.

Key takeaways

  • Kupiec tests whether VaR exceptions occur at the stated unconditional rate.
  • Christoffersen detects exception clustering that coverage tests miss.
  • Preserve strict forecast/P&L timestamp alignment and document exclusions.
  • Small 99% samples demand exact intervals and economic judgment, not binary p-value worship.
  • Backtest exception frequency, severity, attribution, and stressed behavior together.
  • Escalate repeated exceptions through defined model review, limits, and governance procedures.
#VaR #backtesting #Kupiec test #Christoffersen test #market risk