Chapter 3 Laboratory — Module 3: Information and Conditional Expectation Simulator¶
Mathematical Foundations of Modern Finance · Part I · Week 3
Three panels: a conditional-expectation tree, a clairvoyance detector for trading rules, and a two-observer panel (public vs insider filtration; smoothing and the law of total variance). Reproduces Example 3.9 (one-step growth mean 1.08).
Seeds: 20260301–20260303.
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_ch03 as eng
E1 — The tower property, verified numerically (supports LOS 3.2–3.3)¶
Build the three-period tree with $p=0.6$ per branch. Averaging date-2 forecasts down to date-1 reproduces the date-1 conditional expectation exactly: $E[\,E[X|F_2]\,|F_1] = E[X|F_1]$.
r1 = eng.E1_tower()
print(f"One-step growth mean: {r1['growth_mean']} (book: 1.08)")
print("E[S_T | F_1] direct :", r1['E_at_1'])
print("E[S_T | F_1] via tower :", r1['tower_reconstructed'])
print(f"Max tower error : {r1['max_tower_error']:.2e}")
One-step growth mean: 1.08 (book: 1.08)
E[S_T | F_1] direct : {'(1,)': np.float64(139.968), '(0,)': np.float64(104.976)}
E[S_T | F_1] via tower : {'(1,)': np.float64(139.968), '(0,)': np.float64(104.976)}
Max tower error : 0.00e+00
E2 — The clairvoyance detector (supports LOS 3.4)¶
Submit the rule hold the stock only in periods it rises. Deciding at $t-1$ to hold through $t$ requires the move in $(t-1,t]$ — future information. The detector rejects it and names the leak.
r2 = eng.E2_clairvoyance()
print("Rule :", r2['rule'])
print("Verdict :", r2['verdict'])
print("Leak periods:", r2['leak_periods'])
Rule : hold the stock only in periods it rises Verdict : REJECTED — consumes the contemporaneous move (look-ahead). Leak periods: [1, 2, 3, 4]
E3 — Smoothing and the law of total variance (supports LOS 3.5–3.6)¶
Proposition 3.12: $\mathrm{Var}(X) = E[\mathrm{Var}(X|G)] + \mathrm{Var}(E[X|G])$. A coarser observer (who sees only the first two steps) measures a lower conditional variance — the mechanism behind smoothed, sparsely observed marks.
r3 = eng.E3_total_variance()
print(f"Var(X) : {r3['Var_X']:.5f}")
print(f"E[Var(X|G)] (within): {r3['E_Var_given_G']:.5f}")
print(f"Var(E[X|G]) (between): {r3['Var_E_given_G']:.5f}")
print(f"sum : {r3['sum']:.5f} identity holds: {r3['identity_holds']}")
fig, ax = plt.subplots()
ax.bar(["E[Var(X|G)]", "Var(E[X|G])"],
[r3['E_Var_given_G'], r3['Var_E_given_G']],
color=[INK, BRASS])
ax.axhline(r3['Var_X'], color="grey", ls="--", lw=1.5, label="Var(X)")
ax.set_title("E3: total variance splits into within + between")
ax.legend(); plt.tight_layout(); plt.show()
Var(X) : 0.07940 E[Var(X|G)] (within): 0.03974 Var(E[X|G]) (between): 0.03966 sum : 0.07940 identity holds: True
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_growth_mean_1.08 [PASS] V2_tower_holds [PASS] V3_clairvoyance_rejected [PASS] V4_total_variance_identity [PASS] ALL_PASS All Module validation checks pass.