Chapter 6 · Time Series Models for Trading and Risk
Section 6.5 · Chapter 6 · Learning Statistics with Python
Time Series Models for Trading and Risk
Prof. Xuhu Wan
ISOM, HKUST Business School · 2026 Edition
Returns look like white noise — until you square them. You will predict which ACF is flat and which is loud, turn the picture into a \(p\)-value, write a GARCH(1,1) estimator in 15 lines, forecast tomorrow’s variance and back-test a Value-at-Risk through March 2020.
For daily Dow Jones returns, which is more predictable from yesterday?
The daily standard deviation was 0.417 % in 2017 and 6.381 % in March 2020 — a 15× swing in scale, a 234× swing in variance. The worst day, −13.84 % on 2020-03-16, sits inside a cluster of other huge days. Quiet begets quiet; storms beget storms.
The white-noise band is \(\pm 2/\sqrt{1479} = \pm 0.052\).
Which pattern will acf(r) and acf(r**2) show at lags 1–5?
Squared returns: ACF 0.442, 0.551, 0.325, 0.324, 0.297 — every lag far outside ±0.052, and \(Q(10) = 1929\). Raw returns: −0.195, 0.157, −0.015, −0.080, 0.079 — small, though \(Q(10)=321\) is not zero either (the March 2020 reversals).
\[\varepsilon_t = \sigma_t z_t,\ z_t \sim N(0,1),\qquad \text{ARCH}(q):\ \sigma_t^2 = \omega + \sum_{i=1}^{q}\alpha_i \varepsilon_{t-i}^2,\qquad \text{GARCH}(1,1):\ \sigma_t^2 = \omega + \alpha\,\varepsilon_{t-1}^2 + \beta\,\sigma_{t-1}^2 .\]
\(\varepsilon_t\) stays white noise (\(\mathbb E[\varepsilon_t\mid\text{past}] = 0\)) while \(\varepsilon_t^2\) follows an AR(\(q\)). Unconditional variance \(\omega/(1-\alpha-\beta)\); persistence \(\alpha+\beta\).
Compared with ARCH(\(q\)), the \(\beta\) term makes the ACF of \(\varepsilon_t^2\)…
Engle’s ARCH-LM test for order \(q\) regresses:
\(R^2 = 0.362\) on a regression that “should” have \(R^2 = 0\): \(LM = 533.2\) against a \(\chi^2_5\) whose 1 % critical value is 15.1. het_arch reports the same 533.2 — you just re-implemented it. (On 1 479 i.i.d. normals the same code gives \(LM \approx 5\), \(p = 0.42\).)
Load appl.csv, build percent log returns for 2018–2020 into ra, and run arch_lm(ra.values, q=5). The starter leaves LM_a as a placeholder; replace the marked line so that LM_a is Apple’s ARCH-LM statistic.
Expected once fixed: \(LM = 132.6\), \(p < 10^{-4}\) on 756 days, with squared-return ACF 0.319, 0.302, 0.159. Apple clusters like the index.
In Colab
\(\hat\alpha = 0.1000\) and \(\hat\beta = 0.8800\) to four decimals. What is the most likely explanation?
arch rounds to two decimals\(r_t = \mu + \varepsilon_t\). Gaussian log-likelihood of one observation: \(-\tfrac12\left[\ln(2\pi\sigma_t^2) + \varepsilon_t^2/\sigma_t^2\right]\). Sum, negate, minimise — in percent, so \(\omega\) is of order 0.01, not \(10^{-6}\).
Equity indices typically show \(\alpha \approx 0.05\)–\(0.2\) and \(\alpha+\beta \approx 0.95\)–\(0.99\). Will alpha + beta land above or below 0.95?
0.975 — above
\(\hat\mu=0.082\), \(\hat\omega=0.037\), \(\hat\alpha=0.214\), \(\hat\beta=0.761\). Persistence 0.975 — a shock to variance halves in 27 days. Long-run sd 1.217 % vs sample 1.240 %. Well under a second.
From 0.43 % on 2017-11-09 to 8.88 % on 2020-03-17 — 141 % annualised. Volatility is not a number; it is a time series, and the model tracks it one day at a time.
Tomorrow is exact: \(\hat\sigma^2_{T+1} = \omega + \alpha\varepsilon_T^2 + \beta\sigma_T^2\). Beyond that, \(\mathbb{E}[\varepsilon^2_{T+h}] = \sigma^2_{T+h}\), so \(\hat\sigma^2_{T+h} = \omega + (\alpha+\beta)\,\hat\sigma^2_{T+h-1}\).
On 2020-11-13, \(\varepsilon_T = 1.283\) and \(\sigma_T^2 = 1.913\). What is \(\hat\sigma^2_{T+1}\) to 3 decimals?
1.845
1.845 → 1.810 → 1.704 → 1.560, sliding toward the long-run 1.48. A GARCH forecast is a decay path, not a level: the further out, the less today matters.
\(\text{VaR}_p = -\left(\hat\mu + \hat\sigma_{T+1}\, q_p\right)\), a positive loss. With \(q_{0.01} = -2.326\), \(\hat\mu = 0.082\), \(\hat\sigma_{T+1} = \sqrt{1.845} = 1.358\):
Standardised residuals still have excess kurtosis 2.43; the fitted \(\nu = 12.7\). Tomorrow’s 1 % VaR: 3.08 % (normal) vs 3.23 % (\(t\)). In-sample the normal 1 % VaR is breached 2.5 % of days, the \(t\) 2.2 % — both too often. At 5 % both are close (5.8 %, 6.1 %). Fat tails survive GARCH; they are just thinner.
Fit on 2015–2018 (1 006 days), freeze the parameters, run the recursion forward through 2019–2020 (473 days), count breaches of the 1 % VaR. Compare with an unconditional VaR from the training mean and sd.
Over 2019–2020 the 1 % VaR should be breached about 5 times (473 × 0.01). Which is closer to 5?
1 % VaR: GARCH 18 breaches (3.8 %, Kupiec \(p<0.001\)), unconditional 31 (6.6 %). 5 % VaR: GARCH 32 (6.8 %, \(p = 0.09\) — not rejected), unconditional 47 (9.9 %). GARCH-normal is still too thin at 1 %; the fixed-sd model is simply wrong. The unconditional line is crossed ten times in March 2020 alone; GARCH’s is crossed once — it was already at \(\sigma \approx 6\) %.
minimize with bounds. Dow 2015–2020: \(\alpha=0.214\), \(\beta=0.761\), half-life 27 days. Work in percent or the optimiser stalls at its starting values.Next: §6.6 — does bad news raise volatility more than good news? And what happens when you size positions by \(\hat\sigma_t\)?
Prof. Xuhu Wan · HKUST ISOM · Learning Statistics with Python