ENH: VaR backtests (Kupiec, Christoffersen, Basel traffic light) and a "maximum undetectable understatement" diagnostic
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 12/100
Research direction
This is a new feature proposal awaiting maintainer decisions on placement (arch/univariate/backtest.py beside diagnostics.py) and whether a method wrapper is wanted. Before any code, read the residual diagnostics added in #850 and the WaldTestStatistic return type they reuse. Done would mean the five standard VaR backtests plus max_undetectable_understatement, each tested against published Kupiec and Basel tables, with the maintainers' scope decisions settled first.
Written by the indexing model from the issue text.
Description
arch has tools for fitting models that produce VaR forecasts and for forecasting them, but nothing that backtests a VaR series against realized returns. I searched the current main branch for "backtest", "kupiec" and "christoffersen" and found no matches.
I would like to add a small set of standalone functions in arch.univariate, in the same shape as the residual diagnostics added in #850:
from arch.univariate import (
var_exceptions, # bool array: -returns > var
kupiec_pof, # LR test, WaldTestStatistic (df=1)
christoffersen_independence, # LR test, WaldTestStatistic (df=1)
christoffersen_conditional_coverage, # LR test, WaldTestStatistic (df=2)
traffic_light, # Basel zone for the last 250 days
max_undetectable_understatement,
)
The first five are textbook. The three likelihood-ratio tests return the existing WaldTestStatistic, so .stat, .pval and .critical_values work as they do elsewhere in the package; var_exceptions returns a boolean array and traffic_light returns a small result object with the zone, the count and the cumulative probability. The Kupiec non-rejection regions match the ones usually quoted from Kupiec (1995) for 99% VaR, and the traffic light cumulative probabilities reproduce Table 2 of the Basel Committee's 1996 backtesting framework.
The sixth function is the part I think is new. A passing backtest says the reported VaR was not rejected. It does not say how close it was. max_undetectable_understatement(returns, var) answers a concrete question: by what fraction could the whole VaR series have been scaled down, with the same returns, and still pass every selected test? The result also reports the extra exceptions that scaling would produce and which test binds first. A pass with a limit of 2% reads very differently from a pass with a limit of 25%.
Because the exception set only changes at the points 1 - loss/var, the search over proportional scalings is exact and needs one pass over the breakpoints, no optimizer. It is a lower bound on what a non-proportional series could hide, and the docstring says so. The tests include a brute-force grid check of the limit, and it also agrees with a bisection solver I wrote earlier on 34 simulated series to about 1e-16.
Three design points I would like your view on:
-
Placement. I put it in
arch/univariate/backtest.pynext todiagnostics.py, with docs under the utility page. If you would rather have it elsewhere, moving it is trivial. -
Kupiec is two-sided, so a bank that reports zero exceptions fails it for being too conservative. For the understatement question that is not informative, so
max_undetectable_understatementignores rejections caused by too few exceptions by default (ignore_too_few=True). -
Wrapper. On #845 you said you would rather have standalone functions with a lightweight method wrapper. These are standalone, and the first five would wrap onto
ARCHModelResultcleanly once a VaR series is supplied. I left the wrapper out of the sketch so the functions can be reviewed on their own, and will add it if you want it.
I have a working branch with tests (hand-computed Christoffersen values, the published Kupiec and Basel tables, property tests and a brute-force grid check of the understatement limit) and docs. I am happy to open a PR with the whole thing, or with only the five standard tests first if you prefer to review the new diagnostic separately. If this is out of scope for arch, I will publish it as a small standalone package instead.
- Dominant language
- Python
- Stars
- 1.6k
- Forks
- 298
- Avg merge
- 14h 46m
- Merged PRs (30d)
- 106
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from bashtage/arch
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 52/100
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 22/100
bashtage/arch#801 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
bashtage/arch#748 · 2 comments ·
Maintainers usually reply within 1 day
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
enhancement good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
elviskahoro/sdk-python-attio#144 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Maintainers usually reply within 1 day
-
RenyiELBO raises TypeError when model has no sample sitesPossibly taken @alibb007 claimed this today. Open
Difficulty 1/5 Under an hour Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
[Bug]: Four evaluation scripts unpack init_memmachine_params's five return values into three namesOpen
Difficulty 1/5 Under an hour Newbie friendliness 88/100
MemMachine/MemMachine#1911 ·
Maintainers usually reply within 3 days