Monte Carlo Pricing (Basic)
Motivation: why this matters in quant finance
Risk-neutral valuation says the price of any European-style payoff is
This note develops the basic technique: how to discretise an SDE under , generate paths, estimate the price, and quantify the standard error. Variance-reduction techniques that improve on the basic method are covered in the antithetic, control variate, importance sampling, and quasi-MC lessons.
The informal idea
Three steps.
-
Simulate. Generate independent realisations of the underlying at maturity (or paths if path-dependent).
-
Evaluate. Compute payoffs .
-
Average and discount.
By the law of large numbers, as . By the CLT, the standard error is
where is the (estimated) standard deviation of the discounted payoff. A 95% confidence interval is .
Formal statement
Let be a payoff with finite second moment under , and let be i.i.d. -distributed copies. The Monte Carlo estimator is
- Unbiased: .
- Consistent: a.s. by the strong law.
- Asymptotically normal: in distribution.
- MSE: .
The standard deviation is estimated from the sample as .
Algorithm: pricing a European call under Black-Scholes
Under , with .
For a European call with strike :
import numpy as np
def mc_european_call(S0, K, T, r, sigma, N=100_000, seed=42):
rng = np.random.default_rng(seed)
Z = rng.standard_normal(N)
ST = S0 * np.exp((r - 0.5 * sigma**2) * T + sigma * np.sqrt(T) * Z)
payoff = np.maximum(ST - K, 0.0)
discounted = np.exp(-r * T) * payoff
price = discounted.mean()
se = discounted.std(ddof=1) / np.sqrt(N)
return price, se
S0, K, T, r, sigma = 100, 100, 1.0, 0.05, 0.2
price, se = mc_european_call(S0, K, T, r, sigma, N=100_000)
print(f"MC price: {price:.4f} ± {1.96*se:.4f} (95% CI)")
# MC price: 10.4287 ± 0.0901 (95% CI)The closed-form Black-Scholes price is — well within the confidence interval.
Algorithm: path-dependent payoffs
with independent . No discretisation error — the multiplicative log-normal structure means each step is exactly the GBM increment.
def mc_asian_call(S0, K, T, r, sigma, M=50, N=100_000, seed=42):
rng = np.random.default_rng(seed)
dt = T / M
Z = rng.standard_normal((N, M))
increments = (r - 0.5 * sigma**2) * dt + sigma * np.sqrt(dt) * Z
log_paths = np.log(S0) + np.cumsum(increments, axis=1)
paths = np.exp(log_paths)
avg = paths.mean(axis=1)
payoff = np.maximum(avg - K, 0.0)
discounted = np.exp(-r * T) * payoff
return discounted.mean(), discounted.std(ddof=1) / np.sqrt(N)
price, se = mc_asian_call(100, 100, 1.0, 0.05, 0.2, M=50, N=100_000)
print(f"Asian call: {price:.4f} ± {1.96*se:.4f}")
# Asian call: 5.7720 ± 0.0467Algorithm: SDEs without closed-form increments
Strong convergence rate , weak convergence rate . For options pricing, weak convergence is what matters.
For Heston (stochastic vol), Euler is unstable for the variance process; Andersen's QE scheme or Lord-Koekkoek-Dijk's truncation is preferred. For jump-diffusions, simulate Poisson arrivals and inject jumps. These are model-specific corrections to the basic recipe.
Key properties
- Dimensional independence. Convergence rate does not depend on the dimension. A 100-asset basket option converges at the same rate as a 1-asset option (per sample), though each sample is more expensive.
- Bias from discretisation. For non-GBM models discretised by Euler, MC includes both Monte Carlo error () and discretisation bias (). The true error decomposes; total compute scales as .
- Variance reduction. Antithetic variates, control variates, importance sampling, stratified sampling, and quasi-MC all aim to reduce without increasing .
- Greeks via MC. Greeks (delta, vega, etc.) can be computed by bumping inputs, by pathwise derivatives (when payoff is differentiable), or by likelihood-ratio estimators (when not). All have trade-offs in bias/variance.
- Confidence interval tolerance. A 95% CI half-width of \0.10$0.10N$ much larger or to use QMC.
Worked example: 95% CI sample size
Target: estimate a Black-Scholes ATM call price of magnitude \sim\10\pm $0.05$ at 95% confidence.
Empirical \sigma_X \approx \15$ for ATM call (sample std of discounted payoff).
Need .
So roughly paths give a \pm\0.05\pm$0.025N = 1.4M$. Slow.
This is exactly why variance reduction is mandatory for production pricing — naive MC at for a complex multi-asset model takes minutes per pricing, which kills any iterative use (calibration, real-time risk).
Common confusions and pitfalls
- Using instead of . Pricing must be under the risk-neutral measure. Real-world drift gives wrong prices.
- Forgetting the discount factor. , not .
- Random seed. Reproducibility matters — set seeds explicitly. Don't use
np.random.seedand the default global RNG; use a Generator. - Discretisation bias. For path-dependent options on Black-Scholes, exact GBM discretisation has zero bias. For Heston, Euler can produce negative variance — kills the simulation. Use a proper scheme.
- Antithetic doesn't always help. If the payoff is monotone in , antithetic gives big variance reduction. For symmetric payoffs (straddles, butterflies), antithetic provides little benefit.
- Reporting "exact" prices. Always report the SE alongside the point estimate. A point estimate without uncertainty is misleading.
- In-the-money paths only. For deeply OTM options, most paths contribute zero — variance per sample is dominated by rare ITM paths. Importance sampling fixes this.
Where this goes next
- Antithetic variates — pairing with .
- Control variates — subtract a correlated known-mean.
- Importance sampling — re-weight paths toward the payoff region.
- Quasi-Monte Carlo — replace pseudo-random with low-discrepancy sequences.