Chapter 12 Laboratory — Module 12: The Learning Machine¶
Mathematical Foundations of Modern Finance · Part III · Week 12
Four panels: the Gaussian update, the Kalman filter, the de-smoothing panel, and the drift panel. Reproduces the $151.5M ± $3.3M nowcast and recovers θ = 0.8 from a smoothed series.
Seeds: 20261201–20261204.
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams.update({"figure.figsize": (7, 4.2), "axes.grid": True,
"grid.alpha": 0.3, "font.size": 11})
BRASS, INK = "#B4884A", "#0F1E3D"
import mfmf_engine_ch12 as eng
E1 — The Gaussian update (supports LOS 12.2)¶
A prior $N(\$153M, \$4M^2)$ and an independent noisier signal $N(\$148M, \$6M^2)$ fuse into a posterior nowcast of $151.5M with a ±$3.3M band — weight 0.69 on the prior, and the weights are not opinions but precisions.
r1 = eng.E1_nowcast()
print(f"Nowcast mean : ${r1['nowcast_mean']:.2f}M (book: $151.5M)")
print(f"Nowcast sd (1-sd): ${r1['nowcast_sd']:.2f}M (book: $3.3M)")
print(f"Weight on prior : {r1['weight_on_prior']:.3f} (book: 0.69)")
print(f"Weight on signal : {r1['weight_on_signal']:.3f}")
Nowcast mean : $151.46M (book: $151.5M) Nowcast sd (1-sd): $3.33M (book: $3.3M) Weight on prior : 0.692 (book: 0.69) Weight on signal : 0.308
E2 — The Kalman filter and innovation whiteness (supports LOS 12.3)¶
Run the filter on a simulated state. Correctly specified, the innovations are white (no autocorrelation). Mis-set the observation noise by a factor of four and the autocorrelogram betrays it.
r2 = eng.E2_kalman()
print(f"Filter RMSE : {r2['rmse']:.4f}")
print(f"Innovation autocorr (correct) : {r2['innov_autocorr_correct']:+.4f}")
print(f"Innovation autocorr (misspec) : {r2['innov_autocorr_misspec']:+.4f}")
print(f"Whiteness OK : {r2['whiteness_ok']}")
Filter RMSE : 0.1340 Innovation autocorr (correct) : -0.0436 Innovation autocorr (misspec) : +0.4016 Whiteness OK : True
E3 — De-smoothing (supports LOS 12.4–12.5)¶
Reported marks smooth the true returns: $r_t = (1-\theta)r^\*_t + \theta r_{t-1}$. An AR(1) regression recovers $\theta \approx 0.8$; unsmoothing restores the true volatility the reported series understated.
r3 = eng.E3_desmooth()
print(f"theta (true / recovered): {r3['theta_true']} / {r3['theta_hat']:.3f}")
print(f"Reported vol : {r3['reported_vol']:.3f}")
print(f"Unsmoothed vol : {r3['unsmoothed_vol']:.3f}")
print(f"True vol : {r3['true_vol']}")
theta (true / recovered): 0.8 / 0.815 Reported vol : 0.062 Unsmoothed vol : 0.195 True vol : 0.18
E4 — Learning the drift (supports LOS 12.6)¶
With a prior standard deviation on the drift, the weight the posterior places on the data grows with the horizon. Even twenty years leaves substantial prior weight — the drift is hard to learn.
r4 = eng.E4_drift()
print(f"Prior precision tau0 : {r4['tau0']:.1f}")
print(f"Data weight (20 years) : {r4['data_weight_20y']:.3f}")
print(f"Prior weight (20 years) : {r4['prior_weight_20y']:.3f}")
Prior precision tau0 : 2500.0 Data weight (20 years) : 0.207 Prior weight (20 years) : 0.793
Validation checks¶
Every laboratory report must reproduce these; a report whose checks do not pass is returned ungraded.
checks = eng.validation_checks()
for k, v in checks.items():
if k.startswith("_"): continue
print(f"[{'PASS' if v else 'FAIL'}] {k}")
assert checks["ALL_PASS"], "Validation failed — do not submit."
print("\nAll Module validation checks pass.")
[PASS] V1_nowcast_151.5 [PASS] V2_nowcast_sd_3.3 [PASS] V3_weight_on_prior_0.69 [PASS] V4_kalman_innovation_white [PASS] V5_desmooth_recovers_theta [PASS] ALL_PASS All Module validation checks pass.