Chapter 3 — Reshaping Statistics

Learning Statistics with Python

Prof. Xuhu Wan

Chapter 3 · Learning Statistics with Python

Reshaping Statistics

From averages to distributions: empirical and theoretical distributions, bootstrap confidence intervals and hypothesis tests, experimental design with A/B tests and bandits, linear and nonlinear association, and extreme value theory for the tails.

Prof. Xuhu Wan

ISOM, HKUST Business School · 2026 Edition

What This Chapter Builds

  • A mean is one number; a distribution is the whole story. You will build the empirical CDF from five numbers, smooth it into a KDE, and fit a Student-t to 4 309 days of Apple returns — then see the Normal fail by 10⁻³⁸.
  • The bootstrap: one sample, resampled with replacement, gives a standard error and a confidence interval for any statistic — even the kurtosis, which has no textbook formula worth trusting.
  • Tests as decisions: Shapiro–Wilk, Kolmogorov–Smirnov, a one-sample \(t\), a permutation test, and a rolling two-sample \(t\) that turns into a regime detector.
  • Experimental design: why an under-powered A/B test misses a real effect, how peeking turns 5 % into a coin flip, and when a bandit beats a test.
  • Association beyond Pearson: Spearman for monotone, distance correlation for anything, and tail co-exceedance for what happens on the worst days.
  • Extreme value theory on the Dow: block minima, peaks over threshold, a fitted GEV and the extreme VaR — the number that names your highest pain threshold.

Why this matters

Every quant decision is a statement about a distribution — its centre, its spread, its shape, its tail. Chapter 2 gave you clean frames; this chapter gives you the habit of predicting what the distribution should look like before you compute it, so that a 25-standard-deviation “surprise” is recognised for what it is: a wrong model, not bad luck.

Roadmap

Section Concept Tool
3.1 Population vs sample, ECDF, KDE, parametric fit, Q-Q, normality tests sample, np.sort, KernelDensity, stats.t.fit, probplot, shapiro, kstest, bimodality coefficient
3.2 Bootstrap SE and CIs, one-sample and two-sample tests, permutation, regime detection rng.integers(0, n, (B, n)), percentile and BCa, ttest_1samp, rng.shuffle, ks_2samp, rolling \(\hat t\)
3.3 Power, sequential testing, multi-armed bandits \(z\)-test, \(n = 2\sigma^2(z_\alpha+z_\beta)^2/\tau^2\), peeking simulation, ε-greedy, UCB1, Thompson
3.4 Linear, monotonic, nonlinear and tail association pearsonr, spearmanr, kendalltau, distance correlation via pdist, tail co-exceedance
3.5 Extreme value theory: GEV, block minima, POT, extreme VaR genextreme, genpareto, Hill estimator, return levels, EVaR

Population vs Sample and Distributions in Practice

A population parameter is a fact; a sample statistic is an estimate with a distribution of its own. You will build the empirical CDF from five numbers, read Apple’s fat tails off it, estimate a density two ways (bandwidth vs a formula), learn to see a Student-t in a Q-Q plot, and then let Shapiro–Wilk and Kolmogorov–Smirnov put numbers on what the eye saw.

Population vs sample: ddof=0 or 1?

The notebook’s 63 numbers are the whole population. We draw 10 without replacement (seed 1).

Why does pandas compute std() with ddof=1 by default?

  • Because \(n - 1\) is always closer to \(N\)
  • Because dividing by \(n\) underestimates the population variance from a sample
  • Because populations are never observed
  • It doesn’t — the default is ddof=0

Univariate EDA: Apple 2005–2022

Log return \(r_t = \ln S_t - \ln S_{t-1}\). Before running: is the excess kurtosis of daily returns positive or negative?

The excess kurtosis of Apple’s daily returns will be…

  • Large and positive — fat tails
  • About 0 — close to normal
  • Negative — thin tails
  • Undefined for returns

Excess kurtosis 5.99 and skew −0.28. Half the days sit between the two red lines — a band about 2 % wide — yet the axis runs from roughly −20 % to +14 %. That gap between the middle and the extremes is what the next slides quantify.

From theoretical to empirical

Textbooks hand you a density. Real work hands you a sample. The bridge between them is one line of arithmetic — you will build it from five numbers and then read Apple’s tails off it.

Build an ECDF from five numbers

You have the sample [3, 1, 4, 1, 5]. Define \(\hat F(x)\) = (fraction of the sample \(\leq x\)). Before any code, predict two values:

What fraction of [3, 1, 4, 1, 5] is \(\leq 3\)?

sum(v <= 3 for v in [3,1,4,1,5]) / 5

0.6

And \(\leq 1\)?

sum(v <= 1 for v in [3,1,4,1,5]) / 5

0.4

You just defined the ECDF

The object you built has a name. Given an i.i.d. sample \(X_1,\dots,X_n\), the empirical CDF is \[\hat F_n(x) = \frac{1}{n}\sum_{i=1}^{n}\mathbf 1\{X_i \le x\}.\] A step function. No assumptions about shape. It is the non-parametric maximum-likelihood estimate of the true CDF — and you computed it by hand.

Read the ECDF off Apple’s returns

A sorted sample’s \(i\)-th value sits at height \(i/n\). Studentise the 4 309 returns (\(z = (r - \bar r)/s\)) and sort them.

The ECDF height at the 2 155th of 4 309 sorted points — what is \(2155/4309\) to three decimals?

print(round(2155/4309, 3))

0.5

The median studentised return is −0.008, essentially zero. But 76.9 % of days lie within one standard deviation (a Normal says 68.3 %) and only 98.5 % within three (a Normal says 99.7 %): a taller peak and fatter tails. That is excess kurtosis 5.99, read off a staircase.

Why doesn’t a histogram do the job?

The notebook’s mixture sample (seed 7) is two Normals stuck together, one at −10 and one at +5. Predict what its ECDF looks like before you draw it.

The data is bimodal (two clusters). What will the ECDF curve look like?

  • A single smooth S-curve — bimodality is invisible in a CDF
  • A straight diagonal line
  • Two steep stretches separated by a flatter stretch
  • A bell shape

Run it and check your call

The curve climbs steeply through −10, then flattens between about −5 and 0 (the first quartile is −10.0, the median −5.2, the third quartile 5.0), then climbs again around +5. The rule you discovered: the ECDF has no tuning knob, so it cannot lie about shape. The flat stretch is the bimodality.

How fast does it converge?

The ECDF is built from \(n\) random points, so it is itself random. Will it land on the truth as \(n\) grows? Predict the worst-case gap for Apple’s \(n = 4\,309\).

The DKW bound on \(\Pr(\sup_x|\hat F_n - F| > 0.02)\) is \(2e^{-2n(0.02)^2}\). Evaluate it at \(n = 4309\), rounded to 3 decimals.

import math
print(round(2*math.exp(-2*4309*0.02**2), 3))

0.064

Glivenko–Cantelli (1933): \(\sup_x|\hat F_n(x) - F(x)| \xrightarrow{a.s.} 0\) — the ECDF converges uniformly to the truth. The DKW inequality is the finite-\(n\) version: with 4 309 days, the whole Apple staircase is within 0.02 of the true CDF with probability at least 93.6 %. This theorem is what the bootstrap (§3.2), the KS test and the KDE all lean on.

Kernel density estimation

The ECDF is a staircase. To get a smooth density, smear each point into a small bump and add them up. The width of the smear, \(h\), is the only choice that matters — and you will discover how much it matters.

Estimating a density without a formula: KDE

A kernel density estimate puts a small bump on every observation and adds them up. The bandwidth \(h\) is the only knob. Predict its effect on the notebook’s two-component mixture.

For a two-mode sample, what does a very small bandwidth do?

  • Produces the smoothest curve
  • Hides both modes
  • Produces a spiky curve that chases sampling noise
  • Has no effect: KDE is bandwidth-free

Estimating a density with a formula: fit a Student-t

Studentise the returns, then let scipy.stats.t.fit choose the degrees of freedom. Predict the meaning of the number before you see it.

If the fitted degrees of freedom come out near 3, what does that say?

  • The returns are close to normal
  • Tails so heavy that the fourth moment is not finite
  • The sample has only 3 observations
  • The mean is 3 standard deviations from zero

Your turn: degrees of freedom for Tesla

The notebook fits the same \(t\) to TSLA, CVX and BAC (df between 3 and 6 for every large stock). Do it for Tesla in 2020.

Studentised 2020 Tesla returns are in z_tsla. Set nu_tsla to the degrees of freedom returned by stats.t.fit (its first element).

Predict the Q-Q plot

A Q-Q plot puts sample quantiles against a reference distribution’s quantiles. A perfect match traces the diagonal. Apple’s returns have df ≈ 3.3 tails; predict what that does to the ends against a Normal reference.

The sample is heavier-tailed than Normal. The Q-Q plot will…

  • Lie perfectly on the diagonal
  • Curl away from the diagonal at both extremes (S-shape)
  • Be a horizontal line
  • Curve only on the right end

Testing for normality: look first (Q-Q)

A Q-Q plot compares sample quantiles with a reference distribution’s quantiles. Straight line = same shape.

Against the normal the tails peel away from the line on both sides — the classic S. Against the fitted \(t\) the points hug the line until the very last few, where the right tail is shorter than a \(t_{3.3}\) predicts.

Testing for normality: Shapiro–Wilk and Kolmogorov–Smirnov

The Q-Q plot showed where the departure is; a test puts a number on it. The notebook runs kstest(returns, stats.norm.cdf) and gets \(D \approx 0.47\). Predict why it is that large.

Why is the KS statistic 0.47 when raw returns are compared with stats.norm.cdf?

  • Because the returns are extremely non-normal in shape
  • Because the reference is N(0, 1) and the returns have sd ≈ 0.02 — a scale mismatch, not a shape test
  • Because KS cannot handle 4 000 observations
  • Because the returns contain NaN

Reading the tests

Null hypothesis: the population is normal. A tiny p-value rejects it: Shapiro–Wilk \(p \approx 10^{-38}\); KS on the studentised returns \(D = 0.075\), \(p \approx 10^{-21}\). Against the fitted \(t_{3.3}\) the KS test does not reject (\(D = 0.015\), \(p = 0.27\)) — that is what “the t fits” means in numbers. Shapiro–Wilk is the more powerful normality test; KS is the more general one — any CDF, or another sample (§3.2 uses the two-sample form).

A cheap bimodality detector

Before fitting anything, the bimodality coefficient \(b = (g_1^2 + 1)/(g_2 + 3)\) flags trouble when \(b > 5/9 \approx 0.555\). Predict the verdict on the mixture, then on Apple.

For the seed-7 mixture, \(g_1 = 0.381\) and \(g_2 = -1.214\). Is \(b\) above 0.555?

print((0.381**2 + 1) / (-1.214 + 3) > 0.555)

True

Mixture: \(b = 0.641\), flagged — negative kurtosis (−1.21) is the fingerprint of two separated humps. Apple: \(b = 0.120\), not flagged — one peak, fat tails. The detector tells you which problem you have before you choose a density.

What you discovered

  • A sample statistic estimates a population parameter; ddof=1 corrects the downward bias of the sample variance.
  • The ECDF is “fraction \(\le x\)” — built from five numbers, it converges uniformly (Glivenko–Cantelli; DKW: within 0.02 with probability ≥ 93.6 % at \(n = 4309\)). It showed Apple’s 76.9 % within ±1 sd against the Normal’s 68.3 %.
  • KDE trades bias for variance through the bandwidth; Silverman’s \(h \approx 0.2\) sd is a start, not an answer. A parametric fit trades flexibility for two numbers you can quote: Student-t, df ≈ 3.3.
  • The Q-Q plot’s S-curve is the eye’s kurtosis test. The formal tests agree: Shapiro–Wilk rejects the Normal at \(p \approx 10^{-38}\); KS against norm on raw returns tests scale, not shape — studentise first (\(D = 0.075\)); the fitted \(t_{3.3}\) is not rejected (\(D = 0.015\), \(p = 0.27\)).
  • The bimodality coefficient (0.64 for the mixture, 0.12 for Apple) tells a two-hump problem from a fat-tail problem before you choose a density.

Next: §3.2 — how sure are you of any of those numbers? Resample, and test.

Bootstrap Confidence Intervals and Hypothesis Testing

You have one sample. The bootstrap resamples it to reveal how much any statistic would wobble — no formula needed. Then the classical tests: was a year’s mean return zero, was one year different from the next, do two stocks share a distribution — and a two-sample test recomputed every day that becomes a regime detector.

The same sample, reloaded

Each section runs standalone. Reload Apple 2005–2022, studentise, refit the \(t\).

The bootstrap idea

You want the sampling distribution of some statistic but cannot draw new samples. Classic statistics needs a formula for the standard error. The bootstrap (Efron, 1979) needs none — and you will see why by reinventing it.

What can you resample from?

You have a sample \(X_1,\dots,X_n\) and want to know how much \(\hat\theta\) would wobble across new samples — but you cannot draw new samples. Predict the bootstrap’s move.

With no access to the population, how do we mimic drawing a fresh sample?

  • Draw new points from a Normal fitted to the data
  • Resample without replacement (just a reshuffle)
  • Resample with replacement from the data itself
  • Throw away half the data and refit

The ECDF \(\hat F_n\) stands in for the unknown \(F\). Resampling with replacement is sampling from \(\hat F_n\). That single substitution is the whole idea — and Glivenko–Cantelli is why it works.

Reinvent the bootstrap SE

The recipe in code: build a \(B \times n\) matrix of resample indices, compute the statistic on each row, take the spread. A small skewed sample — 120 lognormal draws — and a statistic with no textbook SE: the median.

Bootstrap SE of the median, 120 lognormal draws (seed 11), \(B = 4000\). Order of magnitude first: 0.02, 0.2, or 2?

"around 0.2"

0.198

The trick you found: the B x n index matrix replaces every loop and every SE formula. There is no closed-form SE for a median — yet you just measured it: 0.198.

Your turn: sanity-check against a case you know

For the mean there is a formula: \(\mathrm{SE} = s/\sqrt n\). If the bootstrap is legitimate, its SE must match. Confirm it on Apple’s 251 returns of 2017.

Bootstrap the mean of r17 (\(B = 4000\), seed 5) into se_boot, and compute the textbook se_formula. They should agree within 10 %.

Both come out at about 0.0007 — so the bootstrap is not magic; it reproduces the answer you already trusted, then extends to medians, ratios and kurtosis, where no formula exists.

Percentile interval for Apple’s 2017 mean return

The simplest CI: take the 2.5 % and 97.5 % quantiles of \(\hat\theta^*\) directly. Predict whether the interval for the 2017 mean daily return contains zero.

The 95 % percentile CI for the 2017 mean daily return…

  • Contains 0 comfortably
  • Excludes 0, but only just
  • Is centred exactly on 0
  • Cannot be computed for a mean

\([0.00014,\ 0.00294]\): zero is outside, barely. Clean and correct when the bootstrap distribution is symmetric and unbiased. Predict the next slide: when is that assumption dangerous?

When does percentile go wrong?

For which statistic is the plain percentile CI least trustworthy?

  • The sample mean of a near-Normal sample
  • The kurtosis of a fat-tailed sample at small \(n\)
  • A proportion from a large balanced sample
  • A sum of two independent means

BCa (bias-corrected and accelerated) fixes two blind spots: a bias correction \(z_0\) (how often \(\hat\theta^* < \hat\theta\) — should be 50 %) and an acceleration \(a\) from the jackknife (handles skew). Same cost as percentile.

Watch BCa shift the endpoints: the kurtosis of 2017

Excess kurtosis is the statistic §3.1 warned you about — dominated by a handful of days. Bootstrap it (B = 2000) and compare the two intervals.

Resampling with replacement drops some of the extreme days, so most bootstrap kurtoses fall below the sample value. Is \(z_0 = \Phi^{-1}(\text{share below})\) positive or negative?

print(stats.norm.ppf(0.594) > 0)

True

Kurtosis 4.58 with a bootstrap SE of 1.55 — a third of its own size. 59.4 % of resamples fall below it, so \(z_0 = +0.24\), and BCa pushes the interval from \([1.47, 7.31]\) up to \([2.23, 9.15]\). Rule discovered: for medians, quantiles, ratios and kurtosis the bias and skew are real, and BCa moves the endpoints to where they belong — for free.

Hypothesis tests

A confidence interval says how much a number wobbles; a test asks whether a specific claim survives — the null / statistic / p-value logic of §3.1, now applied to a mean. Three claims: 2017’s mean return was zero, 2017 and 2018 share a mean, MSFT and AAPL share a distribution — then one test recomputed every day.

One-sample t-test: was Apple’s 2017 mean return zero?

\(H_0: \mu = 0\). Predict the statistic \(\hat t = \bar x / (s/\sqrt n)\) for 2017.

With \(\bar x \approx 0.00151\), \(s \approx 0.0111\) and \(n = 251\), what is \(\hat t\) to two decimals?

round(xbar / (s / n**0.5), 2)

2.16

One-sided or two-sided?

The cell printed a one-sided \(p\) (Normal approximation) and ttest_1samp’s two-sided \(p\). The business question — “did Apple earn a positive return?” — is directional.

For a positive \(\hat t\), the one-sided \(p\) compared with the two-sided \(p\) is…

  • About half
  • About double
  • Identical
  • Always exactly 0.05

Both reject at 5 % — the bootstrap CI excluded zero for the same reason. The interval, the \(t\)-statistic and the \(p\)-value are three views of one fact: \(\bar x\) is 2.16 standard errors from zero.

Why is shuffling labels valid?

The \(t\)-test assumed a Normal sampling distribution for \(\bar x\). For a sharp null — “the two groups have identical distributions” — you need no such assumption: build the null distribution by shuffling labels.

Under \(H_0: F_A = F_B\), why can we randomly relabel the pooled data?

  • The labels are exchangeable — under \(H_0\) every relabelling is equally likely
  • Because the sample is large
  • Because both groups are Normal
  • Because the variances are equal

No Normality, no equal-variance assumption, no large-\(n\) asymptotics. The price is \(B\) shuffles — trivial on a laptop.

Permutation test: was 2017 a different year from 2018?

Apple’s mean daily return was +0.151 % in 2017 and −0.028 % in 2018. Label the 502 returns by year, shuffle the labels 5 000 times, and ask how often a gap that large appears by chance.

The two-sided permutation \(p\) for the 2017 − 2018 gap will be roughly…

  • Essentially 0 (p < 0.001)
  • Borderline (around 0.05)
  • Large (around 0.2) — no evidence of a different mean
  • Exactly 0.50

\(p = 0.18\) from shuffles, 0.18 from Welch’s \(t\) — the classical test was fine here because \(n\) is large. The permutation test’s advantage is that it would still be right for 20 days of fat-tailed returns, where the \(t\) is not. Its Monte-Carlo error shrinks like \(1/\sqrt B\), independent of \(n\).

Two samples: do MSFT and AAPL returns share a distribution?

The permutation test compared two means. ks_2samp compares two whole empirical CDFs and needs no reference distribution at all. The notebook’s MSFT-vs-AAPL check, on the catalog files’ common dates.

Null: the two samples come from the same distribution. On 778 common days \(D = 0.039\), \(p = 0.61\) — no evidence that MSFT and AAPL daily returns are distributed differently. Keep the p-value in mind — the same two-sample idea, run on a rolling window, becomes a regime detector on the next slide.

A rolling two-sample t-test detects regime switches

Compare the last 10 days’ returns with the 10 days ending 20 days earlier: \(\hat t = \dfrac{\bar r_{\text{now}} - \bar r_{\text{lag}}}{\sqrt{(s^2_{\text{now}} + s^2_{\text{lag}})/10}}\). Days with \(|\hat t| > 2\) are flagged.

Twenty of 249 days are flagged; the red dots cluster at the September 2012 peak and the April 2013 trough — the points where the return distribution shifted. A test statistic, recomputed every day, is a trading signal.

Your turn: the KS version of the regime detector

For each 20-day window, store the p-value of stats.ks_2samp(w_now, w_lag) in ks_p (the test compares the current 20 returns with the 20 returns lagged by 20 days). Then count windows with p < 0.2.

What you discovered

  • The bootstrap resamples with replacement because \(\hat F_n\) stands in for \(F\). It matched the mean’s known SE (0.0007), then measured what no formula gives: SE 0.198 for a median, 1.55 for a kurtosis of 4.58.
  • Percentile CIs suit symmetric statistics; BCa corrects bias (\(z_0 = +0.24\)) and skew and moved the kurtosis interval from \([1.5, 7.3]\) to \([2.2, 9.1]\).
  • 2017’s mean return: \(\hat t = 2.16\), two-sided \(p = 0.03\), one-sided half that; the CI, the statistic and the \(p\) agree. A permutation test needs no Normality and gave \(p = 0.18\) for 2017 vs 2018.
  • Two samples need no reference distribution: ks_2samp cannot tell MSFT from AAPL (\(D = 0.039\), \(p = 0.61\)). A rolling two-sample \(t\) turns “same distribution?” into a regime signal: 20 flagged days around the 2012 peak and 2013 trough.

Next: §3.3 — tests you design: how many users, when to stop, and when not to test at all.

Experimental Design: A/B Tests and Bandits

So far the data arrived and you tested it. Now you design the experiment: how many units to catch a one-point lift, what peeking at the dashboard does to your false-positive rate, and when earning while learning (a bandit) beats a fixed test. These are simulations by necessity — you cannot rerun a market.

Classical A/B set-up

Two arms, fixed \(N\), a hypothesis fixed before the data. The design is a century old (Fisher’s agricultural plots) and still the gold standard when you can afford it. You will discover its single most-violated requirement.

Predict the test statistic

Control outcome \(\sim N(0.10, 1)\), treatment \(\sim N(0.18, 1)\), 800 each. The \(z\)-statistic is \(\hat\tau / \widehat{\mathrm{SE}}\), reject if \(|z| > 1.96\). Predict whether this experiment detects the true 0.08 lift.

There is a real 0.08 lift. Does \(|z| > 1.96\) (significant)?

  • Yes — a real effect always shows up as significant
  • No — \(z \approx 1.7\), just short of the bar (under-powered)
  • \(z\) will be exactly 1.96
  • \(z < 0\) (wrong sign)

Run it

A genuine effect, declared “not significant.” The discovery: the failure is not the effect — it is the sample size. Significance is a property of power, and power is something you size before you run.

How big must N be?

For a baseline rate \(p = 0.10\) and an absolute lift of \(0.01\) (10 % → 11 %), the formula \(n \approx 2\sigma^2(z_\alpha + z_\beta)^2/\tau^2\) at 80 % power. Predict the order of magnitude.

Required \(N\) per arm for \(p = 0.10\), lift \(= 0.01\), \(z_\alpha = 1.96\), \(z_\beta = 0.84\). Integer (use math.ceil).

import math
print(math.ceil(2 * 0.10*0.90 * (1.96 + 0.84)**2 / 0.01**2))

14112

About 14 000 per arm to catch a one-point lift. Most “failed” experiments never had the sample size to succeed. The previous slide’s \(n = 800\) was doomed before it ran.

Your turn: a two-point lift

The required \(N\) scales with \(1/\tau^2\): doubling the detectable lift should cut \(N\) by four.

Set n2 to the required \(N\) per arm (integer, math.ceil) for the same baseline \(p = 0.10\) but a lift of 0.02. Check that it is one quarter of 14 112.

Sequential tests

Real teams peek — they check the dashboard at lunch and stop when “it looks significant.” You will discover, by simulation, exactly how much damage that does to the false-positive rate.

Predict the cost of peeking

We simulate under a true null (no effect). At each new observation we recompute \(z\) and stop the first time \(|z| > 1.96\). Predict the resulting false-positive rate over 1 000 observations.

True null, peek-and-stop at every observation up to \(N = 1000\). False-positive rate?

  • Stays at the target 0.05
  • Rises slightly to ~0.10
  • Around 0.25
  • Above 0.5 (and climbing toward 1.0)

Run the peeking simulation

Over half the null experiments produce a “significant” result. The rule you discovered: every look spends Type-I error. Naive peeking turns a 5 % test into a coin flip.

The fix: spend your alpha

If repeated looks spend error, the cure is to budget it across looks. Predict which spending rule lets you peek hardest early.

Which sequential boundary is stringent early, relaxed later — the clinical-trial default?

  • Bonferroni (\(\alpha/K\) per look)
  • O’Brien–Fleming (1979)
  • Pocock constant boundary (1977)
  • No correction at all

Note

The cost of a properly-designed sequential test: ~5–15 % more samples than a fixed-\(N\) test of equal power. The gain: a principled option to stop early when the effect is large. Lan–DeMets \(\alpha\)-spending generalises this to any look schedule.

Multi-armed bandits

When the goal flips from “estimate the effect precisely” to “earn the most reward while learning,” A/B is the wrong tool. Bandits trade exploration against exploitation. You will race three policies head-to-head.

Predict the winner

Three arms with true conversion rates [0.08, 0.10, 0.12]; arm 2 is best. Over 4 000 pulls we race \(\varepsilon\)-greedy, UCB1, and Thompson sampling. Predict which earns the most.

Which policy collects the most total reward here?

  • \(\varepsilon\)-greedy (it’s simplest)
  • They tie exactly
  • Thompson sampling
  • A perfect oracle is the only winner

Run the race

A perfect oracle expects \(0.12 \times 4000 = 480\); the gap is regret. Thompson lands closest — its pull counts pile onto arm 2 fastest. \(\varepsilon\)-greedy keeps wasting 10 % of pulls on losers forever. That is the exploration–exploitation trade-off, made concrete.

When to use which

The design is decided before the data, and it depends on what the business actually pays for. Predict the right tool for each scenario.

Match the design to the goal

You are picking ad creatives; the revenue during the test counts. Best tool?

  • Fixed-\(N\) A/B test (clean estimate)
  • Group-sequential test
  • A multi-armed bandit
  • No experiment — just guess

Fixed-\(N\) A/B when you need a defensible point estimate (board, regulator) and the cost of a wrong call is one-time and large. Sequential when stakeholders will peek or units are expensive. Bandit when learning is earning.

What goes wrong (pre-mortem)

You now know peeking inflates Type-I error. Predict another silent killer of experiments.

You measure 12 KPIs and declare victory on whichever turns significant. The bug?

  • Nothing — more metrics is more information
  • Multiplicity — testing many metrics inflates Type-I, just like peeking
  • The sample is too large
  • Bandits would have the same issue and there is no fix

Note

Other traps: SUTVA violations (treatment leaks between units → cluster-randomise / switchbacks); novelty effects (a new UI shines for a week → pre-register a 30-day hold-out); CUPED variance reduction can cut required \(N\) by 30–60 % at zero cost. Decisions made after seeing the numbers are p-hacking by another name — the same lesson as §7.6’s 42 rules on trial.

What you discovered

  • A real effect can read “not significant” — significance is about power, sized before the run. About 14 000 per arm to catch a one-point lift; 3 528 for a two-point lift.
  • Peeking turns a 5 % test into a coin flip (you simulated > 0.5 false positives). \(\alpha\)-spending (Pocock, O’Brien–Fleming, Lan–DeMets) buys back the right to stop early.
  • Bandits (\(\varepsilon\)-greedy, UCB, Thompson) optimise cumulative reward; Thompson concentrated pulls on the best arm fastest in the race.
  • Pick the design before the data — and watch for multiplicity, SUTVA and novelty, all of which inflate error the same way peeking does.

Next: §3.4 — two variables at once: which correlation coefficient sees a parabola?

Association: Linear, Monotonic, and Nonlinear

Pearson’s \(r\) measures only the linear part of a relationship. You will build data that is strongly related yet fools Pearson completely, find which coefficient survives a monotone bend, which survives any bend at all — and then ask whether the association between two stocks is the same on the worst days as on ordinary ones.

Predict: can Pearson see a U-shape?

We make \(y = (x-5)^2 + \text{noise}\) — a clean, strong, non-linear relationship. Predict what Pearson’s \(r\) reports.

Pearson \(r\) between \(x \sim U(0,10)\) and \(y = (x-5)^2 + \varepsilon\) (seed 61, \(n = 300\)). Near 0, near 0.5, or near 1?

"near 0"

0.04

\(r \approx 0.04\) on a relationship you can see with your eyes. Pearson is blind to anything non-monotone. Its requirements — finite variance, linearity, outlier sensitivity — are assumptions, not guarantees.

Association: Pearson sees straight lines only

\(y = \sin x + \varepsilon\) is perfectly determined by \(x\) up to noise. Predict Pearson’s \(r\).

For y = sin(x) + noise over \(x \in [0, 100]\), Pearson’s \(r\) is…

  • Close to +1
  • Close to −1
  • Close to 0
  • Undefined

In the index panel spy is tomorrow’s SPY change and the other columns are today’s moves: every correlation in the spy row is near zero. Today’s Hang Seng is not a linear predictor of tomorrow’s SPY. “No association” here means no linear association.

Will rank measures rescue you?

Spearman (\(\rho_S\), Pearson on ranks) and Kendall (\(\tau\), concordant minus discordant pairs) catch any monotone relationship. Predict whether they catch a parabola.

For a symmetric parabola, Spearman and Kendall will report…

  • Strong positive values (~0.8)
  • Strong negative values
  • Near zero — they only see monotone trends
  • Exactly Pearson’s value by coincidence

Spearman: ranks instead of values

Spearman’s \(\rho\) is Pearson’s \(r\) on the ranks. Predict it for \(y = \log x\).

\(\log\) is strictly increasing, so the rank of \(y\) equals the rank of \(x\) for every point. What is Spearman’s \(\rho\)?

round(stats.spearmanr(xs, np.log(xs))[0], 3)

1.0

Spearman rescues monotone nonlinearity (log: Pearson 0.92 → Spearman 1.00) and nothing else: the parabola (0.08 / 0.06) and the cosine (0.04 / 0.03) are invisible to both coefficients.

Kendall’s tau: the same verdict from pairs

Kendall counts pairs: \(\tau = (\text{concordant} - \text{discordant}) / \binom{n}{2}\). It is more robust to ties and outliers than Spearman, and just as blind to a bend.

Log: \(\tau = 1.000\); parabola: \(0.039\); cosine: near zero. The rule discovered: rank measures fix outliers and curvature-but-monotone, not non-monotonicity. For “any dependence at all” you need distance correlation (Székely, 2007), which is zero if and only if \(X \perp Y\).

Distance correlation sees any dependence

Székely’s distance correlation is zero if and only if the variables are independent. The notebook uses the dcor package; we build it from pairwise distances.

In Colab

!pip install dcor --quiet
import dcor
dcor.distance_correlation(parabo["x"], parabo["y"])      # ≈ 0.49, 12 ms on 10 000 points

Which measure will be clearly positive for the parabola?

  • Pearson only
  • Spearman only
  • Pearson and Spearman
  • Distance correlation only

Distance correlation: log 0.97, parabola 0.50, cosine 0.37 — the notebook’s dcor gives 0.97 / 0.49 / 0.35 on its own draws. Only this column sees the two non-monotone shapes.

Your turn: does today’s return predict tomorrow’s?

feat holds next-day return (target), today’s return (lag1) and the 5-day sum (ret5) for 2020–2021. Set dc_lag1 to the distance correlation between lag1 and target, and compare with Pearson.

Does association survive on the worst days?

returns.csv holds daily S&P 500 and Tesla returns, 2015–2024. One Pearson number describes ten years. Predict what happens conditionally on the S&P’s worst 5 % of days.

On the S&P’s worst 5 % of days, what share are also among Tesla’s worst 5 %?

  • About 5 % — as under independence
  • About 12 %
  • About a third — some six times the independence value
  • 100 % — Tesla always crashes with the market

Pearson 0.465 over 2 515 days; on the 126 worst S&P days Tesla was in its own worst 5 % on 32.5 % of them — 6.5 times the independence rate. (Pearson within those days, 0.355, is lower — conditioning on a slice of \(x\) truncates its variance and mechanically shrinks \(r\).) The tail is where diversification is tested, and §3.5 is about the tail.

What you discovered

  • Pearson: linear only. The U-shape gave \(r = 0.04\); \(\sin x\) gave about 0. Zero correlation is not independence.
  • Spearman and Kendall see monotone bends (log: 1.00) and nothing else (parabola: 0.06 and 0.04).
  • Distance correlation is zero only under independence: parabola 0.50, cosine 0.37, built from pairwise distances with pdist.
  • In the index panel, no lagged index predicts tomorrow’s SPY linearly; in 2020–2021 the distance correlation of today’s Apple return with tomorrow’s is the practice you just did.
  • One correlation number hides the tail: Tesla joins the S&P’s worst 5 % of days a third of the time, 6.5 times the independence rate.

Next: §3.5 — the tails alone: what is the worst 20-day loss you should plan for?

Extreme Value Theory: GEV, Block Maxima, POT, Extreme VaR

Averages have the central limit theorem; maxima have Fisher–Tippett–Gnedenko. You will first see that matching mean and variance does not match the tail, then collect the worst day of every 20-trading-day block on the Dow (1985–1990, so Black Monday is in the sample), fit a GEV by maximum likelihood, check it with a Q-Q plot, score each block’s rarity, and read off the extreme VaR — the highest pain threshold.

Same mean, same sd — same tail?

Draw 4 309 Normal returns with Apple’s mean and standard deviation (seed 71). They match on the first two moments. Predict the 0.1 % quantile comparison.

With mean and sd matched, Apple’s 0.1 % quantile versus the Normal’s is…

  • Identical (same sd ⇒ same tail)
  • Substantially larger in magnitude (~1.8× here)
  • Smaller
  • Positive

Same mean, same sd — and a 0.1 % quantile of −10.5 % against −5.8 %, a 1 % CVaR of −7.8 % against −5.1 %, and 25 days beyond four standard deviations where the Normal has none. Variance does not measure tail risk. Two portfolios with identical Sharpe ratios can have wildly different blow-up probabilities.

The Fisher–Tippett–Gnedenko theorem

If the standardised maximum \(M_n\) of \(n\) i.i.d. draws converges at all, it converges to the GEV family:

\[F(s) = \begin{cases} \exp\!\big(-e^{-s}\big) & \xi = 0 \\[4pt] \exp\!\big(-(1 + \xi s)^{-1/\xi}\big) & \xi \ne 0 \end{cases} \qquad Q(p) = \begin{cases} -\ln(-\ln p) & \xi = 0 \\[4pt] \dfrac{1}{\xi}\big((-\ln p)^{-\xi} - 1\big) & \xi \ne 0 \end{cases}\]

with \(s = (x - \mu)/\sigma\). Three sub-families, indexed by the tail index \(\xi\).

Which sign of \(\xi\) means a heavy, unbounded tail?

  • \(\xi < 0\) (Weibull)
  • \(\xi = 0\) (Gumbel)
  • \(\xi > 0\) (Fréchet)
  • Any — the tail is set by \(\sigma\)

scipy’s sign convention

scipy.stats.genextreme uses shape c with c = −ξ. Every fit below reports ξ = −c.

The three GEV densities

With \(\mu = 0, \sigma = 1\) the support ends at \(\mu - \sigma/\xi\): \(= 3\) for \(\xi = -1/3\) (right end), \(= -3\) for \(\xi = 1/3\) (left end). The Fréchet curve is the one that keeps going.

The sample: Dow Jones log returns, 1985–1990

Which date is the worst single day in 1985–1990, and roughly what log return? (Think October 1987.)

old.Return.idxmin().date(), round(old.Return.min(), 4)

1987-10-19, −0.2563

Block minima: the worst day of every 20-day block

rolling(20).min() gives the running 20-day minimum; taking every 20th row (iloc[::20]) makes the blocks non-overlapping. Predict the number of blocks.

Roughly 1 500 trading days in 1985–1990. How many 20-day block minima?

  • 20
  • About 74
  • About 1 500
  • 6 — one per year

One red dot per block, whatever happened in it: a calm month contributes a minimum of −0.5 %, October 1987 contributes −25.6 %. Block maxima treat both as one observation each — the method is rate-limited by design.

Peaks over threshold: keep every day below −1.5 %

POT throws away the calendar and keeps every exceedance. Predict the order of magnitude: how many of the 1 496 days fell below −1.5 %?

How many days in 1985–1990 have a log return below −0.015?

  • About 9
  • About 90
  • About 900
  • All of them

The dots now cluster in October 1987 and October 1989 — POT captures clusters of stress that block minima flatten to one point. Its price is choosing \(u\): too high and the sample is tiny, too low and the GPD approximation fails.

Warm-up: GEV on the block maxima of a Normal

Before real data, check the theorem on a case where you know the answer (the notebook’s i.i.d. Normal experiment).

10 000 standard-Normal draws, 100 blocks of 100, take each block’s maximum, fit a GEV. Which \(\xi\) should you expect?

  • About +1/3
  • About 0
  • About −1
  • Undefined — Normal maxima have no limit law

Fit the GEV to the Dow’s block losses

Minima become maxima by a sign flip: fit to \(-\text{Min20}\), the block loss. Predict the support bound.

For \(\xi > 0\) the GEV support starts at \(\mu - \sigma/\xi\). With \(\mu \approx 0.0136\), \(\sigma \approx 0.0070\), \(\xi \approx 0.354\), what is the bound?

round(loc - scale / xi, 4)

−0.0063

\(\xi \approx 0.35 > 0\): Fréchet. The 20-day worst loss of the Dow has a polynomial tail; moments of order above \(1/\xi \approx 2.8\) do not exist.

Diagnostic: Q-Q plot against the fitted GEV

One point will sit far above the 45° line at the top right. Which block is it?

  • The block containing 19 October 1987
  • The first block of 1985
  • The block with the smallest loss
  • None — a fitted distribution always fits

Extreme scores: how rare was each block?

Score \(= 1 - F_{\text{GEV}}(\text{loss}) = P(\text{a block is worse than this one})\). Small score = rare block.

Which date has the smallest score, and how many blocks score below 0.10?

score.idxmin().date(), (score < 0.10).sum()

1987-10-19, 6

Black Monday’s score is 0.0007: the fitted GEV says a worse 20-day block arrives once in about 1 400 blocks — 110 years. Two others (January 1988, October 1989) are 1-in-50 events.

POT with the generalised Pareto distribution

Exceedances over a high threshold follow a GPD (Pickands–Balkema–de Haan): \(\;P(X - u > y \mid X > u) \approx \big(1 + \xi y/\beta\big)^{-1/\xi}\), with the same \(\xi\) as the GEV. The POT quantile is

\[\text{VaR}_q = u + \frac{\beta}{\xi}\Big[\Big(\tfrac{n}{N_u}(1-q)\Big)^{-\xi} - 1\Big].\]

91 exceedances give \(\xi = 0.44\) against the GEV’s 0.35 — the same heavy-tail verdict from a different sample; agreement between the two estimators is your sanity check. The GPD’s 99 % one-day VaR (3.3 %) sits just above the empirical 1 % quantile (3.1 %): with 1 496 days the empirical quantile is fine at 1 %. The GPD earns its keep at 0.1 %, where there is nothing left to count.

Hill’s estimator: the tail index a third way

A Fréchet tail is a power law, \(P(L > x) \sim x^{-\alpha}\) with \(\alpha = 1/\xi\). Hill (1975) reads \(\alpha\) off the top \(k\) order statistics: \(\hat\alpha_k = \big[\tfrac{1}{k}\sum_{i\le k}\ln(L_{(i)}/L_{(k+1)})\big]^{-1}\). Predict which \(k\) agrees with the GPD’s \(\xi = 0.44\).

Where will the Hill estimate of \(\xi\) form a plateau near 0.44?

  • Only at \(k = 25\)
  • For \(k\) between 25 and 100; it breaks down at \(k = 200\)
  • Only at \(k = 200\)
  • At no \(k\) — Hill and GPD never agree

\(\hat\xi\) = 0.47, 0.42, 0.44 for \(k\) = 25, 50, 100 — the plateau — then 0.63 at \(k = 200\), where the threshold (0.8 %) is an ordinary day. Picking \(k\) is the craft, exactly as picking \(u\) was for POT: too few points is noise, too many is the body of the distribution.

Return levels: the 1-in-T-day loss

The \(T\)-day return level solves \(P(L > x_T) = 1/T\). Two estimators: the empirical quantile, and the fitted GPD extrapolated: \(x_T = u + \frac{\beta}{\xi}\big[(\tfrac{k}{n}T)^{\xi} - 1\big]\). Predict the empirical method’s fatal limit.

Why can’t an empirical quantile give a 1-in-2 500-day loss from 1 496 days?

  • It can — empirical quantiles extrapolate freely
  • The formula divides by zero
  • There is no observation that far out — the quantile is undefined beyond the sample
  • Empirical quantiles are always biased high

At \(T = 250\) (one year) the two agree: 4.9 % against 4.7 %. At \(T = 1000\) the GPD says 8.9 % while the empirical 7.8 % rests on the two worst days in the sample; at \(T = 2500\) (ten years) only the GPD answers — 13.2 %, and Black Monday’s 25.6 % is the reminder that even that is one draw from the tail. Extrapolation with a fitted shape is what turns six years of data into a ten-year estimate.

Extreme VaR: the highest pain threshold

Invert the fitted GEV at probability \(\alpha\): the loss that only a fraction \(\alpha\) of 20-day blocks will exceed.

\[\text{EVaR}_\alpha = -\Big(\mu + \frac{\sigma}{\xi}\big[(-\ln(1-\alpha))^{-\xi} - 1\big]\Big)\]

With \(\xi = 0.354\), \(\mu = 0.0136\), \(\sigma = 0.0070\), what is EVaR at \(\alpha = 5\,\%\) (as a return)?

round(EVaR(xi, loc, scale, 0.05), 4)

−0.0506

A one-day 5 % VaR says “−1.6 %”. The extreme VaR says: in one 20-day block out of twenty, expect a day worse than −5.1 % — and 4.1 % of the 74 blocks did. That is the number a risk limit should be sized to.

Your turn: the 1 % extreme VaR

Compute the 1 % extreme VaR from the fitted GEV into evar01 (use EVaR), then count how many of the 74 block minima fell below it.

What you discovered

  • Matching mean and sd does not match the tail: Apple’s 0.1 % quantile is −10.5 % against −5.8 % for a Normal with the same moments, with 25 days beyond 4 sd where the Normal has none.
  • Maxima have their own limit law: the GEV with tail index \(\xi\) — Weibull (\(\xi<0\), bounded), Gumbel (0), Fréchet (\(\xi>0\), heavy). In scipy, c = −ξ.
  • Block minima (74 blocks of 20 days) give one observation per block; POT (91 days below −1.5 %) keeps every exceedance and sees clusters.
  • Three estimators, one verdict: GEV \(\xi \approx 0.35\), GPD \(0.44\), Hill plateau \(0.42\)\(0.47\). Normal block maxima, by contrast, gave \(\xi \approx 0\).
  • The Q-Q plot and the extreme score both single out 19 October 1987 (score 0.0007); a fitted GPD extrapolates to a 1-in-2 500-day loss of 13 % where the empirical quantile has nothing to count.
  • Extreme VaR at 5 % is about −5.1 % per 20-day block — three times the daily 5 % quantile of −1.6 %. That is the highest pain threshold.

Working with an AI Copilot

Three prompts that make an LLM useful for this chapter — and the pitfall each one guards against.

  1. “Before any test, print n, the standard deviation, and the excess kurtosis; if kurtosis is above 3, tell me which of the tests you propose assume Normality and give me a bootstrap or permutation alternative.” A copilot will run ttest_1samp on 20 fat-tailed returns and report a p-value to four decimals. The number is real; the assumption behind it is not.
  2. “When you run kstest against norm, confirm the data are standardised first (or pass args=(mean, sd)), and when you fit genextreme, state the sign convention and report ξ = −c.” Both are places where the code runs, returns a number, and the number is wrong — D = 0.47 on raw returns is a scale mismatch, not a shape test.
  3. “Fix the hypothesis, the metric, the sidedness and the sample size before you show me any result, and refuse to compute a p-value on a subset I chose after seeing the data.” Peeking, metric-shopping and post-hoc one-sided tests are the same error; an LLM that happily re-runs the analysis on request is the fastest p-hacking machine ever built.

Mistakes Library: “25-standard-deviation moves” (August 2007)

Warning

In the second week of August 2007, quantitative equity funds run by Goldman Sachs, Renaissance, AQR and others lost between 10 % and 30 % in a few days as crowded long-short factor positions unwound together. Goldman’s Global Equity Opportunities fund fell about 30 % in a week and received a US$3 billion injection. Explaining it, CFO David Viniar told the Financial Times (13 August 2007): “We were seeing things that were 25-standard-deviation moves, several days in a row.”

Under a Normal model a 25σ event has probability of order \(10^{-137}\) — it should not happen once in the life of the universe, let alone on consecutive days. The models were not unlucky; they were mis-specified. A Fréchet tail with \(\xi \approx 0.35\), the number you fitted to the Dow, assigns such moves probabilities measured in years, not eons.

Lesson for this chapter: counting sigmas presumes a Gaussian. The tail index, block maxima and the extreme VaR exist precisely because “how many σ” is the wrong question about the tail.

Decision Memo — Set the 20-day loss limit for the index book

To: Chief Risk Officer From: <Your name>, risk analytics Subject: Replace the daily-VaR-based stop with an extreme-VaR limit Date: 2026-09-15

Recommendation: Size the index book’s hard loss limit to the 5 % extreme VaR of the 20-day block minimum: −5.1 % of notional per block, reviewed quarterly.

Evidence: - GEV fitted by MLE to 74 block minima (DJI 1985–1990): ξ = 0.35, μ = 0.0136, σ = 0.0070; Q-Q diagnostic straight apart from 19 Oct 1987. - EVaR(5 %) = −5.1 % vs the daily 5 % quantile of −1.6 %: the current stop is sized to an ordinary bad day, not to an ordinary bad month. - Six blocks in six years scored below 0.10; the limit would have been breached 3 times (4.1 % of blocks), all in identified stress episodes.

Caveats: - 74 observations: the ξ confidence interval is wide (bootstrap it, §3.2, before sign-off). - Historical window ends 1990; re-estimate on 2008 and 2020 to test stability of ξ. - GPD on 91 exceedances gives ξ = 0.44 and the Hill plateau 0.42–0.47 vs the GEV’s 0.35; agreement in sign and size is the sanity check, not proof.

Next step: Re-fit on rolling 6-year windows to 2024; report EVaR(5 %) and EVaR(1 %) with bootstrap bands.

Chapter Summary

Concept Tool
Population vs sample sample, std(ddof=1)
Empirical distribution ECDF via np.sort and arange(1, n+1)/n, DKW bound, KernelDensity, Silverman’s \(h\)
Parametric fit and diagnostics stats.t.fit, probplot, bimodality coefficient
Bootstrap rng.integers(0, n, (B, n)), percentile CI, BCa (\(z_0\), \(a\) from the jackknife)
Tests shapiro, kstest (studentise first), ks_2samp, ttest_1samp(alternative=), permutation via rng.shuffle, rolling \(\hat t\)
Experimental design \(n = 2\sigma^2(z_\alpha+z_\beta)^2/\tau^2\), peeking simulation, \(\alpha\)-spending, ε-greedy / UCB1 / Thompson
Association pearsonr, spearmanr, kendalltau, distance correlation via pdist, tail co-exceedance
Extreme values genextreme.fit (ξ = −c), block minima, genpareto.fit, Hill estimator, return levels, extreme score, EVaR

Next: Chapter 4 — Statistical Predictive Models.

Discussion Questions

  1. The bootstrap SE of Apple’s 2017 kurtosis was 1.55 on an estimate of 4.58, and BCa moved the interval to \([2.2, 9.1]\). If a risk model needs the kurtosis as an input, what would you feed it — the point estimate, the lower bound, or the fitted \(t\)’s df — and why?
  2. The rolling \(t\)-test flagged 20 days out of 249 at \(|\hat t| > 2\). Under the null of no regime change, roughly how many flags would you expect from 249 dependent, overlapping tests — and what does §3.3’s peeking simulation say about reading those flags as signals?
  3. Tesla joined the S&P’s worst 5 % of days a third of the time, yet the Pearson correlation within those days was lower than overall. Which number belongs in a diversification argument, and which in a stress test?
  4. The GEV on block minima, the GPD on exceedances and the Hill estimator gave \(\xi\) = 0.35, 0.44 and 0.42–0.47. If they disagreed sharply, which would you trust for a risk limit — and what would you change first: the block length, the threshold, or \(k\)?