Lesson 6.3: The Brute-Force Solution: Monte Carlo Simulation

We now introduce one of the most powerful and versatile tools in all of quantitative finance: Monte Carlo simulation. When a problem is too complex for an elegant analytical formula, we can solve it with computational brute force. This lesson explains how to use the random paths from our GBM model to price complex derivatives and estimate financial risk.

Part 1: The Core Idea - Solving Problems with Randomness

The name "Monte Carlo" comes from the famous casino in Monaco, and the core idea is just as simple as a game of chance. The principle of Monte Carlo simulation is to approximate the solution to a complex deterministic problem by generating a large number of random samples and observing the outcome.

It's a method of converting a difficult calculus problem (like integration) into a simple statistical problem (calculating an average).

The Core Analogy: Estimating π with a Dartboard

Imagine you have a square dartboard with a side length of 2, and inscribed within it is a circle with a radius of 1.

  • The area of the square is Asquare=22=4A_{\text{square}} = 2^2 = 4.
  • The area of the circle is Acircle=πr2=πA_{\text{circle}} = \pi r^2 = \pi.
  • The ratio of their areas is Acircle/Asquare=π/4A_{\text{circle}} / A_{\text{square}} = \pi / 4.

Now, imagine you are a terrible dart player and you throw 1,000,000 darts that land completely randomly within the square. You can't calculate π\pi directly, but you can count:

Number of darts that hit the circleπ4×Total darts thrown\text{Number of darts that hit the circle} \approx \frac{\pi}{4} \times \text{Total darts thrown}

By simply rearranging this, you can get a surprisingly accurate estimate of π\pi:

π4×Number of darts in circleTotal darts thrown\pi \approx 4 \times \frac{\text{Number of darts in circle}}{\text{Total darts thrown}}

This is the essence of Monte Carlo. We didn't need any geometry or calculus, just a random number generator and the ability to count. We traded an analytical problem for a statistical one.

Part 2: The Application to Derivatives Pricing

How does this apply to finance? The fundamental theorem of asset pricing tells us that the price of a derivative today is the **expected value of its future discounted payoffs**, calculated under the risk-neutral measure.

The Risk-Neutral Pricing Formula

Price0=EQ[erT×Payoff(ST)]\text{Price}_0 = E_Q[e^{-rT} \times \text{Payoff}(S_T)]
  • EQ[]E_Q[\cdot] denotes the expectation taken under the risk-neutral probability measure.
  • erTe^{-rT} is the continuous discount factor, where rr is the risk-free rate and TT is the time to expiry.
  • Payoff(ST)\text{Payoff}(S_T) is the function that defines the derivative's value at expiry, which depends on the final stock price STS_T.

The key insight is that an "expected value" is just a fancy term for an average. The Law of Large Numbers tells us that we can approximate an expected value by taking the average of a large number of random samples.

This gives us the **Monte Carlo Algorithm for Option Pricing**.

The Monte Carlo Pricing Algorithm
  1. Step 1: Simulate Price Paths.Simulate a large number (NN) of possible future price paths for the underlying asset from today (t=0t=0) until the option's expiry (t=Tt=T). We do this using the **risk-neutral** version of the GBM we learned in the last lesson:
    ST=S0exp((r12σ2)T+σW(T))S_T = S_0 \exp\left( \left(r - \frac{1}{2}\sigma^2\right)T + \sigma W(T) \right)
    This will give us NN different possible values for the final stock price, ST(1),ST(2),,ST(N)S_T^{(1)}, S_T^{(2)}, \dots, S_T^{(N)}.
  2. Step 2: Calculate the Payoff for Each Path.For each simulated final price ST(i)S_T^{(i)}, calculate the payoff of the derivative. For a simple European call option with strike price KK, the payoff is:
    Payoff(i)=max(ST(i)K,0)\text{Payoff}^{(i)} = \max(S_T^{(i)} - K, 0)
  3. Step 3: Average the Discounted Payoffs.Calculate the average of all the payoffs, and then discount that average back to today's value using the risk-free rate.
    Estimated Price=erT×1Ni=1NPayoff(i)\text{Estimated Price} = e^{-rT} \times \frac{1}{N} \sum_{i=1}^N \text{Payoff}^{(i)}

Part 3: The Power of Monte Carlo - Pricing the 'Unpriceable'

For a simple European call option, the Monte Carlo method is overkill; the Black-Scholes formula gives an exact analytical solution. The true power of Monte Carlo is its ability to price **path-dependent** or "exotic" options, for which no simple formula exists.

Example: Pricing an Asian Option

An **Asian option** is a path-dependent option whose payoff depends on the *average* price of the underlying asset over a period of time.

The payoff for an Asian call option is max(SˉK,0)\max(\bar{S} - K, 0), where Sˉ\bar{S} is the average price of the stock between t=0t=0 and t=Tt=T.

There is no closed-form solution for this. But with Monte Carlo, the algorithm is almost unchanged:

  1. Simulate the full price path S(t)S(t) from 00 to TT for each of the NN simulations.
  2. For each path, calculate the average price along that path, Sˉ(i)\bar{S}^{(i)}.
  3. Calculate the payoff for each path: Payoff(i)=max(Sˉ(i)K,0)\text{Payoff}^{(i)} = \max(\bar{S}^{(i)} - K, 0).
  4. Average the discounted payoffs, as before.

Monte Carlo's flexibility is its greatest strength. Any payoff function you can write in code, you can price.

Part 4: Application to Risk Management - Value-at-Risk (VaR)

Monte Carlo is not just for pricing; it's a cornerstone of modern risk management. It can be used to estimate the distribution of a portfolio's future returns, allowing us to calculate key risk metrics like **Value-at-Risk (VaR)**.

VaR answers the question: "What is the maximum loss I can expect to incur over a given time horizon, at a certain level of confidence?"

The Monte Carlo Algorithm for VaR
  1. Step 1: Simulate Future Asset Prices. Use a model like GBM (this time with the *real-world* drift μ\mu, not the risk-free rate) to simulate thousands of possible future prices for every asset in your portfolio at the end of your risk horizon (e.g., 10 days from now).
  2. Step 2: Re-price the Portfolio. For each of the thousands of simulated scenarios, calculate the new total value of your portfolio. This gives you a distribution of possible future portfolio values.
  3. Step 3: Calculate Portfolio Returns. Convert the distribution of future values into a distribution of portfolio profit and loss (P&L) or returns.
  4. Step 4: Find the Percentile. Find the desired percentile of this P&L distribution. For a 99% VaR, you find the 1st percentile of the distribution. This value is your VaR.

Example: If your 10-day 99% VaR is $5 million, it means that under your model's assumptions, you have a 1% chance of losing $5 million or more over the next 10 days.

Part 5: Python Implementation - Pricing a European Call Option

Monte Carlo Option Pricing in Python

import numpy as np

def monte_carlo_call_price(s0, K, T, r, sigma, n_sims):
    """
    Prices a European call option using Monte Carlo simulation.
    
    s0: Initial stock price
    K: Strike price
    T: Time to expiry in years
    r: Risk-free rate
    sigma: Annual volatility
    n_sims: Number of simulation paths
    """
    # Generate N random shocks from a standard normal distribution
    z = np.random.randn(n_sims)
    
    # Calculate the final stock prices using the risk-neutral GBM formula
    drift_term = (r - 0.5 * sigma**2) * T
    random_term = sigma * np.sqrt(T) * z
    st = s0 * np.exp(drift_term + random_term)
    
    # Calculate the payoff for each simulated price
    # Payoff is max(S_T - K, 0)
    payoffs = np.maximum(st - K, 0)
    
    # Calculate the average payoff
    average_payoff = np.mean(payoffs)
    
    # Discount the average payoff back to today
    option_price = np.exp(-r * T) * average_payoff
    
    return option_price

# --- Option Parameters ---
S0 = 100       # Initial price
K = 105        # Strike price
T = 1.0        # 1 year to expiry
R = 0.05       # 5% risk-free rate
SIGMA = 0.20   # 20% volatility
N_SIMS = 1000000 # 1 million simulations for accuracy

# --- Run Simulation ---
call_price = monte_carlo_call_price(S0, K, T, R, SIGMA, N_SIMS)

print(f"Monte Carlo European Call Price: {call_price:.4f}")

# For comparison, let's calculate the Black-Scholes price
from scipy.stats import norm
d1 = (np.log(S0 / K) + (R + 0.5 * SIGMA**2) * T) / (SIGMA * np.sqrt(T))
d2 = d1 - SIGMA * np.sqrt(T)
bs_price = (S0 * norm.cdf(d1) - K * np.exp(-R * T) * norm.cdf(d2))
print(f"Black-Scholes Analytical Price: {bs_price:.4f}")

# The Monte Carlo price should be very close to the Black-Scholes price.

What's Next? Quantifying Uncertainty in Our Estimates

Monte Carlo simulation is a powerful tool for understanding the distribution of *outcomes* given a *model*. But what about the uncertainty in the model itself? We fit a linear regression and get an estimate β^=0.7\hat{\beta}=0.7. How confident are we in that number? Our standard errors rely on strong assumptions (like normality of errors).

What if we could create "alternative realities" for our *data* itself, not just for the future price path? This is the idea behind resampling methods.

In the next lesson, we will introduce **Bootstrapping**, a revolutionary computational technique that allows us to estimate the uncertainty of any statistic by resampling our own data.

Up Next: Resampling Reality: Bootstrapping