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.

In [1]:
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]$.

In [2]:
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.

In [3]:
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.

In [4]:
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
No description has been provided for this image

Validation checks¶

Every laboratory report must reproduce these; a report whose checks do not pass is returned ungraded.

In [5]:
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.