Go to Portfolio
Blog II

Monte Carlo Simulation and the Stock Market


The stock market is uncertain.

We cannot know exactly where a stock will trade next month or next year, but we can ask a more useful question:

What are some possible outcomes if the stock continues behaving roughly like it has in the past?

This is where Monte Carlo simulation becomes useful.

Instead of predicting one future price, Monte Carlo simulation generates thousands of possible futures.


What Is Monte Carlo Simulation?

Monte Carlo simulation is a method that uses repeated random sampling to model uncertain outcomes.

Imagine a stock trading at:

$100

Instead of predicting:

In one year → $125

we simulate many possibilities:

Simulation 1 → $84
Simulation 2 → $116
Simulation 3 → $103
Simulation 4 → $142
Simulation 5 → $71
...
Simulation 10,000 → $128

The result is not a single prediction.

It is a distribution of possible outcomes.

Price

  │                         ╱
  │                   ╱────╱
  │          ╱───────╱
  │     ╱───╱     ╲
  │────╱            ╲────
  │       ╲────╲
  │            ╲────────

  └────────────────────────── Time

        Thousands of possible paths

Why Use It for Stocks?

Stock prices are affected by many unpredictable events:

earnings

interest rates

economic conditions

investor sentiment

news

competition

market volatility

Trying to predict all of them individually is unrealistic.

Monte Carlo simulation instead models the uncertainty itself.

A simplified process is:

Historical prices

Calculate returns

Estimate average return

Estimate volatility

Generate random movements

Simulate thousands of paths

Analyse possible outcomes

Returns and Volatility

Two important inputs are:

Return
Volatility

Return

A simple percentage return is:

Return = (New Price - Old Price) / Old Price

For example:

Old price = $100
New price = $105

Then:

Return = (105 - 100) / 100

       = 0.05

       = 5%

Volatility

Volatility describes how much returns tend to vary.

Compare two stocks:

Stock A:

+1%
-1%
+2%
-1%

and:

Stock B:

+10%
-8%
+15%
-12%

Stock B has much higher volatility.

Higher volatility generally produces a wider range of possible simulated outcomes.

Low volatility

      ╱────
─────╱─────
    ╱──────


High volatility

        ╱────────
   ╱───╱
──╱
   ╲─────
         ╲──────

Geometric Brownian Motion

A common simplified model for stock-price simulations is Geometric Brownian Motion, or GBM.

The idea is that the next stock price depends on:

current price
+
expected return
+
random movement based on volatility

One common form is:

S(t + Δt) =
S(t) × exp[(μ - σ²/2)Δt + σ√Δt Z]

Where:

S = stock price

μ = expected return

σ = volatility

Δt = time step

Z = random value from a normal distribution

You do not need to memorize the equation to understand the idea.

Think of it as:

Next Price
    =
Current Price
    ×
Expected Movement
    ×
Random Market Movement

A Simple Python Simulation

Suppose a stock currently trades at:

$100

and we assume:

Expected annual return = 8%

Annual volatility = 25%

We can simulate one year of daily prices.

import numpy as np
import matplotlib.pyplot as plt

initial_price = 100

expected_return = 0.08
volatility = 0.25

days = 252

dt = 1 / days

prices = [initial_price]

for _ in range(days):

    random_shock = np.random.normal()

    next_price = prices[-1] * np.exp(
        (expected_return - 0.5 * volatility**2) * dt
        + volatility * np.sqrt(dt) * random_shock
    )

    prices.append(next_price)

plt.plot(prices)

plt.xlabel("Trading Days")
plt.ylabel("Stock Price")

plt.show()

This creates one possible future.

Run it again and you will get a different path.


Simulating Thousands of Futures

One simulation is not particularly useful.

Monte Carlo becomes interesting when we repeat it many times.

import numpy as np
import matplotlib.pyplot as plt

initial_price = 100

expected_return = 0.08
volatility = 0.25

days = 252
simulations = 1000

dt = 1 / days

final_prices = []

for _ in range(simulations):

    price = initial_price

    for _ in range(days):

        random_shock = np.random.normal()

        price *= np.exp(
            (expected_return - 0.5 * volatility**2) * dt
            + volatility * np.sqrt(dt) * random_shock
        )

    final_prices.append(price)

plt.hist(
    final_prices,
    bins=50
)

plt.xlabel("Final Stock Price")
plt.ylabel("Frequency")

plt.show()

Now instead of asking:

What will the stock price be?

we can inspect:

What range of outcomes occurred?

What was the median outcome?

How often did the stock lose money?

What did the worst simulated outcomes look like?

Understanding the Distribution

Imagine 10,000 simulations produced:

Lowest outcomes        → around $55

Most common outcomes   → around $105–$120

Higher outcomes        → around $160+

Extreme outcomes       → above $200

A histogram might look roughly like:

Frequency


   │                ███
   │             ███████
   │           ███████████
   │        ███████████████
   │     ███████████████████
   │   ██████████████████████

   └───────────────────────────
      60  80 100 120 140 160

            Final Price

The important part is that we now see a range, rather than one confident-looking prediction.


Estimating Probability of a Loss

Once we have our simulated final prices, we can ask:

In what percentage of simulations did the stock finish below today’s price?

final_prices = np.array(final_prices)

probability_of_loss = np.mean(
    final_prices < initial_price
)

print(
    f"Probability of loss: "
    f"{probability_of_loss:.2%}"
)

If the result were:

Probability of loss: 35%

that means:

35% of our simulated paths
finished below the starting price.

It does not mean there is objectively a 35% chance the real stock will fall.

The result is only valid relative to the assumptions in our model.


Percentiles

Percentiles are often more useful than looking only at the average.

percentile_5 = np.percentile(
    final_prices,
    5
)

median = np.percentile(
    final_prices,
    50
)

percentile_95 = np.percentile(
    final_prices,
    95
)

print(percentile_5)
print(median)
print(percentile_95)

Suppose we get:

5th percentile  → $67

Median          → $106

95th percentile → $169

A useful interpretation is:

       simulated outcomes

Worst-ish                     Best-ish
   │                             │
   ▼                             ▼

$67────────────$106────────────$169
5%             50%              95%

Again, these are model outputs, not guaranteed future price ranges.


Monte Carlo Is Not a Crystal Ball

This is the most important part.

A simulation may look sophisticated:

10,000 simulations

advanced mathematics

beautiful charts

probabilities

percentiles

but its output is only as realistic as its assumptions.

A simple model usually assumes that historical return and volatility provide useful information about the future.

Markets do not always behave that way.

For example:

financial crisis

new regulation

unexpected earnings

company bankruptcy

new technology

war

interest-rate changes

can completely change a stock’s behaviour.

Monte Carlo cannot predict an event that your model does not represent.


Historical Data Does Not Equal the Future

Suppose a stock historically returned:

12% per year

with:

20% volatility

Using those numbers does not mean the future will have:

12% return
20% volatility

The simulation is better interpreted as:

What might outcomes look like under these assumptions?

rather than:

What will happen?

That distinction matters.


Where Monte Carlo Is Useful

Monte Carlo simulation is useful for exploring:

possible investment outcomes

portfolio risk

retirement scenarios

different volatility assumptions

different return assumptions

downside scenarios

long-term compounding

For example, rather than assuming:

Portfolio returns exactly 8%
every year

you could simulate:

Year 1   +14%

Year 2   -8%

Year 3   +21%

Year 4   +3%

Year 5   -11%

which is much closer to how real markets behave.


Common Mistake: Treating the Average as a Prediction

Suppose your simulations produce an average final price of:

$125

It is tempting to say:

The stock should reach $125.

That is not what the simulation tells you.

The more useful information is the distribution:

How wide are the outcomes?

How large is the downside?

How often do losses occur?

How extreme are the tails?

Monte Carlo is primarily a tool for understanding uncertainty and risk, not producing a price target.


Common Mistake: Trusting Historical Returns Too Much

If you estimate:

expected_return = historical_returns.mean()

you are assuming historical average returns are a reasonable estimate of future expected returns.

That assumption may be very weak.

A model can be mathematically correct while still being based on poor assumptions.


Common Mistake: Ignoring Extreme Events

Basic Monte Carlo models often assume normally distributed random movements.

Real financial markets can experience extreme events more frequently than simple normal-distribution models suggest.

This means a basic simulation can underestimate:

crashes

large price jumps

tail risk

More advanced financial models attempt to account for these limitations.


Final Mental Model

Think of Monte Carlo simulation like this:

We don't know
what will happen.


Define assumptions


Add randomness


Simulate many futures


Observe the distribution


Understand uncertainty

For stock-market simulations:

Current Price
      +
Expected Return
      +
Volatility
      +
Randomness

Thousands of Price Paths

Possible Outcomes

The key idea is not:

Predict the stock market.

It is:

Model many plausible outcomes and understand how uncertain the future really is.

That is what makes Monte Carlo simulation useful in finance.