Tail-Risk Metrics¶
risk
¶
Tail-risk metrics: value at risk, expected shortfall and Monte Carlo estimation.
These are the quantities practitioners ask for, rather than the distributions underneath them. Two points recur and are easy to get wrong:
Expected shortfall is infinite when the mean does not exist. For a Pareto
tail with alpha <= 1 there is no finite answer, and returning a large
number instead of inf would be worse than useless: it would look like a
result. Every function here checks and reports inf.
A Monte Carlo estimate without a standard error is not usable. The whole
point of these metrics is the far tail, where few samples land, so
:func:monte_carlo_tail_risk always reports the uncertainty alongside the
estimate.
expected_shortfall
¶
Expected shortfall: the mean loss given that value at risk is exceeded.
Also called conditional value at risk. Where value at risk reports a threshold, this reports the average of what lies beyond it, which is the question that matters when the tail is heavy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dist
|
Any
|
A distribution with a |
required |
level
|
float
|
Confidence level in the open interval (0, 1). |
required |
method
|
str
|
|
'auto'
|
nodes
|
int
|
Quadrature nodes for the numeric path. |
20000
|
Returns:
| Type | Description |
|---|---|
float
|
The expected shortfall, or |
float
|
mean. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> from heavytails import Cauchy, Pareto
>>> round(expected_shortfall(Pareto(alpha=2.0, xm=1.0), 0.99), 4)
20.0
>>> expected_shortfall(Pareto(alpha=0.5, xm=1.0), 0.99)
inf
>>> expected_shortfall(Cauchy(), 0.99)
inf
Source code in heavytails/risk.py
mean_exists
¶
Report whether a distribution has a finite mean.
Expected shortfall is a conditional mean, so it is infinite exactly when
this is False. The check is by family, since it depends on the parameters:
a Pareto tail has a finite mean only for alpha > 1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dist
|
Any
|
A distribution instance from this package. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the mean is finite. Unknown families are assumed to have one, |
bool
|
which is the less surprising default; the numeric path will produce a |
bool
|
very large value rather than a wrong finite one if that is wrong. |
Examples:
>>> from heavytails import Cauchy, Pareto
>>> mean_exists(Pareto(alpha=2.0, xm=1.0))
True
>>> mean_exists(Pareto(alpha=0.5, xm=1.0))
False
>>> mean_exists(Cauchy())
False
Source code in heavytails/risk.py
monte_carlo_tail_risk
¶
Estimate value at risk and expected shortfall by simulation, with errors.
An estimate of a tail quantity without a standard error is not usable: the whole point is the region where few samples land, so the uncertainty is large and varies with the level. This always reports it.
The standard error of the expected shortfall is the standard error of the
mean of the exceedances. The standard error of the value at risk uses the
asymptotic result for a sample quantile,
sqrt(p(1-p)/n) / f(VaR), evaluated with the density where the
distribution provides one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dist
|
Any
|
A distribution with |
required |
level
|
float
|
Confidence level in the open interval (0, 1). |
required |
n_samples
|
int
|
Number of variates to draw. |
100000
|
seed
|
int | None
|
Seed, for a reproducible estimate. |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dictionary with |
dict[str, Any]
|
standard errors, |
dict[str, Any]
|
expected shortfall is |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> from heavytails import Pareto
>>> result = monte_carlo_tail_risk(
... Pareto(alpha=2.0, xm=1.0), 0.99, n_samples=20000, seed=1
... )
>>> result["n_exceedances"]
200
Source code in heavytails/risk.py
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 | |
tail_conditional_expectation
¶
Tail conditional expectation, E[X | X > VaR].
For a continuous distribution this is the same quantity as
:func:expected_shortfall, and this function delegates to it. The two
names come from different literatures, actuarial and financial, and they
diverge only for distributions with an atom at the quantile, which none of
the continuous families here have.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dist
|
Any
|
A distribution with a |
required |
level
|
float
|
Confidence level in the open interval (0, 1). |
required |
**kwargs
|
Any
|
Passed to :func: |
{}
|
Returns:
| Type | Description |
|---|---|
float
|
The tail conditional expectation. |
Source code in heavytails/risk.py
value_at_risk
¶
Value at risk: the quantile of the loss distribution at level.
This is exactly dist.ppf(level), and exists as a named function because
the terminology is what practitioners search for, and because pairing it
with :func:expected_shortfall makes the distinction between the two
explicit.
Value at risk says how large a loss is exceeded with probability
1 - level. It says nothing about how large the exceedances are, which
is what expected shortfall answers and why the two are usually reported
together.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dist
|
Any
|
A distribution with a |
required |
level
|
float
|
Confidence level in the open interval (0, 1), for example 0.99. |
required |
Returns:
| Type | Description |
|---|---|
float
|
The quantile. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples: