DEV Community

Amit Kumar Jha
Amit Kumar Jha

Posted on • Originally published at desk2quant.vercel.app

Monte Carlo Simulation in Finance: From Random Walks to Option Pricing

Why Monte Carlo?

Monte Carlo simulation is the Swiss Army knife of quantitative finance. When analytical solutions don't exist, we simulate: generate thousands of possible future price paths, price the derivative on each path, then average and discount.

The method is named after the Monaco casino — a nod to randomness. But there's nothing gambling about it when done correctly.

Simulating Asset Prices with GBM

The geometric Brownian motion (GBM) model is the starting point:

St+Δt=Stexp((μσ22)Δt+σΔtZ) S_{t+\Delta t} = S_t \exp\left(\left(\mu - \frac{\sigma^2}{2}\right)\Delta t + \sigma \sqrt{\Delta t} \, Z\right)

Where:

  • StS_t
    is the current stock price
  • μ\mu
    is the drift (expected return)
  • σ\sigma
    is the volatility
  • ZN(0,1)Z \sim N(0,1)
    is a standard normal random variable

This comes from discretizing the GBM stochastic differential equation:

dS=μSdt+σSdWt dS = \mu S \, dt + \sigma S \, dW_t

Python: Building a Monte Carlo Pricer

import numpy as np

def monte_carlo_call(S0, K, T, r, sigma, n_sims=100000, n_steps=252):
    """Price a European call option via Monte Carlo."""
    dt = T / n_steps
    Z = np.random.standard_normal((n_sims, n_steps))

    drift = (r - 0.5 * sigma**2) * dt
    diffusion = sigma * np.sqrt(dt) * Z

    terminal_prices = S0 * np.exp(np.sum(drift + diffusion, axis=1))
    payoffs = np.maximum(terminal_prices - K, 0)

    call_price = np.exp(-r * T) * np.mean(payoffs)
    std_error = np.exp(-r * T) * np.std(payoffs) / np.sqrt(n_sims)

    return call_price, std_error

# Example: 1-year ATM call
price, se = monte_carlo_call(S0=100, K=100, T=1.0, r=0.05, sigma=0.20)
print(f"Price: {price:.4f}, 95% CI: [{price-1.96*se:.4f}, {price+1.96*se:.4f}]")
Enter fullscreen mode Exit fullscreen mode

Variance Reduction

Raw Monte Carlo converges at

O(1/N)O(1/\sqrt{N})

— to halve the error, you need 4x simulations. Variance reduction helps:

Antithetic Variates

For every path

ZZ
, also simulate
Z-Z
. This halves variance at zero extra cost:
Z_anti = -Z  # Flip the sign
Z_all = np.vstack([Z, Z_anti])
Enter fullscreen mode Exit fullscreen mode

Control Variates

Use Black-Scholes (which has a closed form) as a control to reduce variance of your Monte Carlo estimate.

Multi-Asset Simulations

For basket options or portfolio derivatives, use Cholesky decomposition to generate correlated paths:

L = np.linalg.cholesky(corr_matrix)
Z = np.random.standard_normal((n_sims, n_steps, n_assets))
corr_Z = Z @ L.T  # Correlated random draws
Enter fullscreen mode Exit fullscreen mode

Interview Essentials

Q: Why not just use Black-Scholes?
Black-Scholes only works for vanilla European options. Most real derivatives — path-dependent, American, basket — don't have analytic solutions. Monte Carlo handles all of them.

Q: How do you price American options with Monte Carlo?
Use the Longstaff-Schwartz least-squares method. At each exercise date, regress future payoffs on current state variables to estimate continuation value.

Q: What are the convergence properties?
Standard error decreases as

1/N1/\sqrt{N}

. To get one more decimal digit, you need 100x more simulations. This is why variance reduction matters.

Summary

Technique Best For Complexity
Raw MC Vanilla European Low
Antithetic Variates Any path Low
Control Variates Known analytic benchmark Medium
Importance Sampling Deep OTM options High
Quasi-MC (Sobol) Low-dimensional Medium

Monte Carlo is not the fastest, but it's the most flexible. When in doubt, simulate.


Ready to go deeper? Check out Desk2Quant for hands-on practice with derivatives pricing, portfolio optimization, and quant interview prep.

Top comments (0)