← All writing
Note · Quantitative finance

Why the typical price path loses

Geometric Brownian motion, the model behind the Monte Carlo scene on the home page. A price can grow on average and still end below its start more often than not.

Nov 20263 min readMonte CarloStochastic calculus

The Monte Carlo panel on the home page draws 64 price paths a year long and stacks where they end into a histogram. Watch it for a few runs and something looks off: the model is set to grow 6% a year, yet about half of the paths end below where they started. Nothing is broken. That is what compounding does to a noisy price.

The model

Geometric Brownian motion says the relative change of a price SS over a short time dtdt is a constant drift plus noise:

dS=μS dt+σS dWdS = \mu S\,dt + \sigma S\,dW

Here μ\mu is the drift, σ\sigma the volatility and dWdW the increment of a Brownian motion, a normal draw with variance dtdt. Applying Itô’s lemma to ln⁡S\ln S gives a closed-form solution,

ST=S0exp⁡ ⁣((μ−12σ2) T+σWT),S_T = S_0 \exp\!\Big( \big(\mu - \tfrac{1}{2}\sigma^2\big)\,T + \sigma W_T \Big),

so ln⁡ST\ln S_T is normal and STS_T is lognormal. The extra −12σ2-\tfrac{1}{2}\sigma^2 is the whole story.

Mean and median part ways

Take the expectation and the noise term contributes eσ2T/2e^{\sigma^2 T/2}, which exactly cancels the correction:

E[ST]=S0 eμT,median⁡(ST)=S0 e(μ−σ2/2) T.\mathbb{E}[S_T] = S_0\, e^{\mu T}, \qquad \operatorname{median}(S_T) = S_0\, e^{(\mu - \sigma^2/2)\,T}.

The mean grows at μ\mu; the typical path grows at μ−σ2/2\mu - \sigma^2/2. The gap, σ2/2\sigma^2/2 per year, is often called volatility drag. A few lucky paths that end far above the rest pull the mean up, while most paths sit below it. The chance of ending below the starting price is

P(ST<S0)=Φ ⁣(−(μ−σ2/2)Tσ),\mathbb{P}(S_T < S_0) = \Phi\!\left( -\frac{(\mu - \sigma^2/2)\sqrt{T}}{\sigma} \right),

with Φ\Phi the standard normal distribution function.

Try it: 400 price paths over one yearends above startends below
Expected price–
Median price–
Drag σ²/2–
Paths below start–
Start price 100, 252 trading days. Same random shocks while you drag; Re-run draws new ones.

The figure starts with the home page’s settings, μ=6%\mu = 6\% and σ=38%\sigma = 38\%. The drag is 7.2%7.2\%, larger than the drift, so the expected price after a year is about 106106 while the median is about 98.898.8, and roughly 51%51\% of paths end below 100100. Push σ\sigma to 80%80\% with μ=10%\mu = 10\% and the median falls to about 8080 while the mean rises to 110.5110.5; about 61%61\% of paths now lose.

Simulating it

Because the solution is exact, there is no need for a small-step integrator: draw normal increments, add the drift in log space and exponentiate.

gbm.py
import numpy as np
def gbm_paths(s0, mu, sigma, years=1.0, steps=252, n=400, seed=7):
rng = np.random.default_rng(seed)
dt = years / steps
shocks = rng.standard_normal((n, steps))
log_steps = (mu - 0.5 * sigma**2) * dt + sigma * np.sqrt(dt) * shocks
log_paths = np.cumsum(log_steps, axis=1)
return s0 * np.exp(np.hstack([np.zeros((n, 1)), log_paths]))
paths = gbm_paths(100, mu=0.06, sigma=0.38, n=100_000)
final = paths[:, -1]
print(f"mean {final.mean():.1f} median {np.median(final):.1f} "
f"below start {(final < 100).mean():.1%}")

With a large n the printout lands on the theory: mean near 106.2106.2, median near 98.898.8, a little over half the paths below the start.

Why it matters

Volatility drag is why a portfolio’s average return and its compounded return differ, why leveraged products decay in sideways markets, and why “expected value” can describe almost nobody’s actual outcome. The model is deliberately simple; real prices have fat tails and changing volatility. The gap between mean and median survives those refinements, and it only gets wider as volatility rises.