Markets: A Rigorous Course

Exercise · m02_tails.py

Heavy tails, the Hill estimator, Jarque-Bera, Normal vs Student-t VaR

Implement every function, then press Run tests.

numpy and scipy.stats only.

Run locally

From the repository root, with the exercises environment installed:

cd exercises && .venv/bin/python -m pytest modules/M02-statistics-of-returns/tests/test_m02_tails.py

Tests (7)

  • test_moment_standard_errors_under_normality
  • test_jarque_bera
  • test_hill_on_exact_pareto
  • test_hill_on_student_t
  • test_hill_on_normal_is_large
  • test_var_normal_vs_t
  • test_sigma_event_black_monday

Theory: M02 notes. Run with Ctrl/⌘+Enter.

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.

Create a free account

Reading is open to everyone. An account keeps your progress, answers, notes and code drafts, and gives you the study plan, the journals and the simulator. Running code needs an account approved for it.