Exercise · m02_tails.py
Heavy tails, the Hill estimator, Jarque-Bera, Normal vs Student-t VaR
not run yet
Not saved: sign up to keep your code"""Exercise: heavy tails, the Hill estimator, Jarque-Bera, Normal vs Student-t VaR
(notes §2.5-2.8).
Implement every function, then run from the repo root:
cd exercises && .venv/bin/python -m pytest modules/M02-statistics-of-returns/tests/test_m02_tails.py
numpy and scipy.stats only.
"""
from __future__ import annotations
import numpy as np
from scipy import stats # noqa: F401 (you will need stats.norm and stats.t)
def sample_skew_kurt(r) -> tuple[float, float]:
"""Sample skewness and excess kurtosis from central moments with divisor n:
m_k = (1/n) sum (r_t - rbar)^k, S = m3 / m2^{3/2}, K = m4 / m2^2 - 3.
Under iid normality sd(S) ~ sqrt(6/n), sd(K) ~ sqrt(24/n) (notes Prop. 2.10).
Return (S, K).
"""
raise NotImplementedError
def jarque_bera(r) -> tuple[float, float]:
"""Jarque-Bera statistic JB = (n/6) (S^2 + K^2 / 4) with S, K from
sample_skew_kurt, and its asymptotic p-value from chi-square with 2 dof
(closed form: P(chi2_2 > x) = exp(-x/2)). Return (JB, p).
"""
raise NotImplementedError
def hill_estimator(r, k: int) -> float:
"""Hill (1975) estimator of the tail index alpha of |r| (notes Prop. 2.12).
Sort X = |r| in decreasing order X_(1) >= X_(2) >= ... >= X_(n). Then
alpha_hat = 1 / [ (1/k) sum_{i=1}^{k} ln X_(i) - ln X_(k+1) ].
This is the MLE of alpha for a Pareto tail above the threshold X_(k+1).
Raise ValueError unless 1 <= k < n.
"""
raise NotImplementedError
def hill_se(alpha_hat: float, k: int) -> float:
"""Asymptotic standard error of the Hill estimator: alpha / sqrt(k)."""
raise NotImplementedError
def var_normal(sigma: float, level: float = 0.99, mu: float = 0.0) -> float:
"""One-period Value-at-Risk at confidence `level`, reported as a positive
loss, when the return is N(mu, sigma^2): VaR = -(mu + sigma * Phi^{-1}(1 - level)).
"""
raise NotImplementedError
def var_student_t(sigma: float, nu: float, level: float = 0.99, mu: float = 0.0) -> float:
"""VaR when the return is mu + s * T_nu with T_nu a standard Student-t (nu > 2)
and s chosen so the standard deviation equals sigma: s = sigma sqrt((nu-2)/nu).
VaR = -(mu + s * t_nu^{-1}(1 - level)).
"""
raise NotImplementedError
def sigma_event(move: float, sigma: float) -> tuple[float, float]:
"""How extreme is a one-day move under a normal model?
Return (z, log10 p) with z = move / sigma and p = P(Z > |z|) for Z ~ N(0,1).
Use stats.norm.logsf to avoid underflow (p can be 1e-90).
"""
raise NotImplementedError
No runs yet
The stub's functions raise
NotImplementedError until you write them. Fill in one, press
Run tests, and watch its tests turn green.Reference solution
Available after your first run, with an account approved for code runs.