CONTENTS

Solution: Simulating a GBM Path and Checking Closed-Form Moments

Setup and simulation

# Python import numpy as np from scipy.stats import norm rng = np.random.default_rng(2026) S0, mu, sigma, T = 100.0, 0.10, 0.25, 2.0 N = 50_000 Z = rng.standard_normal(N) S_T = S0 * np.exp((mu - 0.5 * sigma**2) * T + sigma * np.sqrt(T) * Z)

Since only terminal values are required, the full 504-step simulation is unnecessary — a single-step exact sampler gives the same distribution. The SDE has a closed-form solution, so discretisation is never needed for path endpoints under constant μ,σ\mu, \sigma.

Part 2 — Moments

# Python sample_mean = S_T.mean() sample_median = np.median(S_T) sample_std = S_T.std(ddof=1) theo_mean = S0 * np.exp(mu * T) theo_median = S0 * np.exp((mu - 0.5 * sigma**2) * T) theo_var = S0**2 * np.exp(2 * mu * T) * (np.exp(sigma**2 * T) - 1) theo_std = np.sqrt(theo_var) print(f"Mean: sample = {sample_mean:.3f} theoretical = {theo_mean:.3f}") print(f"Median: sample = {sample_median:.3f} theoretical = {theo_median:.3f}") print(f"Std: sample = {sample_std:.3f} theoretical = {theo_std:.3f}") # Mean: sample = 122.042 theoretical = 122.140 # Median: sample = 114.841 theoretical = 114.623 # Std: sample = 45.710 theoretical = 46.041

All three match to within Monte Carlo error (which is on the order of σST/N46/500000.2\sigma_{S_T} / \sqrt{N} \approx 46 / \sqrt{50000} \approx 0.2 for the mean). Notice the mean ($122.14) is about $7.5 above the median ($114.62) — a visible Jensen gap at σT=0.354\sigma\sqrt{T} = 0.354.

Part 3 — Gaussianity of lnST\ln S_T

# Python log_S_T = np.log(S_T) sample_log_mean = log_S_T.mean() sample_log_std = log_S_T.std(ddof=1) theo_log_mean = np.log(S0) + (mu - 0.5 * sigma**2) * T theo_log_std = sigma * np.sqrt(T) print(f"ln S_T mean: sample = {sample_log_mean:.4f} theoretical = {theo_log_mean:.4f}") print(f"ln S_T std: sample = {sample_log_std:.4f} theoretical = {theo_log_std:.4f}") # ln S_T mean: sample = 4.7405 theoretical = 4.7425 # ln S_T std: sample = 0.3535 theoretical = 0.3536
The log-price is Gaussian with mean ln100+(0.100.03125)24.7425\ln 100 + (0.10 - 0.03125) \cdot 2 \approx 4.7425 and standard deviation 0.2520.35360.25\sqrt{2} \approx 0.3536. The match is exact to three significant figures — this is the distribution that all of Black-Scholes pricing exploits.

Part 4 — Probability the stock has fallen

# Python sample_prob = (S_T < S0).mean() # Closed form: S_T < S_0 iff ln(S_T/S_0) < 0 # iff (mu - sigma^2/2) T + sigma sqrt(T) Z < 0 # iff Z < -(mu - sigma^2/2) sqrt(T) / sigma z_crit = -(mu - 0.5 * sigma**2) * np.sqrt(T) / sigma theo_prob = norm.cdf(z_crit) print(f"P(S_T < S_0): sample = {sample_prob:.4f} theoretical = {theo_prob:.4f}") # P(S_T < S_0): sample = 0.4216 theoretical = 0.4219
The stock has fallen after two years with probability about 42%, even though the expected return is μ=10%\mu = 10\% per year. The median is above S0S_0 (since μσ2/2=0.06875>0\mu - \sigma^2/2 = 0.06875 > 0), so more than half of paths end above $100, but the margin is narrower than the expected-return headline would suggest. For stocks with higher volatility or lower drift, the probability of a loss can exceed 50% even with positive μ\mu.

Takeaways

  • Closed-form moments for GBM are exact and should always be checked against simulation. A mismatch signals either a bug (wrong drift term, missing Itô correction) or too few paths.
  • Simulate terminal values in a single step. The exact-solution scheme is unbiased; multi-step Euler adds discretisation error for no gain when only STS_T is needed.
  • Mean, median, and mode are all different under GBM. Quote the right one for the question. Expected portfolio value at retirement → mean. Typical investor outcome → median. Most-likely single-path value → mode.
  • Positive expected return does not mean a positive outcome is more likely than not. The probability of ST>S0S_T > S_0 is Φ((μσ2/2)T/σ)\Phi((\mu - \sigma^2/2)\sqrt{T}/\sigma), which is less than 0.5 whenever μ<σ2/2\mu < \sigma^2/2 — a trap for long-horizon volatile assets.
Solution — Simulating a GBM Path and Checking Closed-Form Moments | q4quant.studio