Skip to content
All library documents

Matching Euler and Exact Geometric Brownian Motion Simulations

Article Quant Q&A · Author: KKK

Summary

The document explains why two simulated geometric Brownian motion paths differ when one is generated with the exact exponential solution and the other with a discretised stochastic differential equation. For a fair path-by-path comparison, both methods must use the same normally distributed random draws. Setting a random seed alone does not ensure that condition if each method generates its own draws.

The Euler-style update must also scale the Brownian shock by the square root of the time step. The accepted answer identifies both corrections and points out that the example’s volatility input is far larger than a typical intended decimal volatility. These changes align the random inputs and time scaling, making the discretised path comparable with the exact update. The discussion is a focused debugging example; it does not compare discretisation error across time-step sizes, assess statistical convergence, or provide broader guidance on choosing model parameters.

Key ideas

  • A common random seed does not make separately generated random arrays identical.
  • Use the same normal draws in both the exact and discretised simulations for a direct comparison.
  • The Brownian shock in the discretised update must be scaled by the square root of the time step.
  • Check whether volatility is entered as a decimal or as a percentage before interpreting paths.
  • The example addresses implementation mismatches but does not evaluate discretisation error or model fit.

Tags

Full text
# Why do I get this difference when simulating geometric Brownian motion?


# Why do I get this difference when simulating geometric Brownian motion?












I tried simulating GBM using both the SDE definition and the closed form solution. The paths I get through these methods are very different. Can someone help me figure my mistake?

```
import numpy as np
import matplotlib.pyplot as mp
import statsmodels.api as sm

time_step = 1e-6
N = 30000

np.random.seed(987654321)
A = np.zeros((N,1))
A[0] = 2
B = np.zeros((N,1))
B[0] = 2
volvol = 40.0

s = np.sqrt(time_step)

y = np.exp(-volvol**2.0/2 * time_step + volvol * s * np.random.normal(0, 1, N))
y1 = np.random.normal(0, s, N)

for i in np.arange(1, N, 1):
   A[i] = A[i-1] * y[i]

for i in np.arange(1, N, 1):
    print(B)
    dB = volvol * B[i - 1] * y1[i]
    B[i] = B[i-1]+ dB

mp.plot(A, label = 'exp')
mp.plot(B, label = 'SDE')

mp.legend(loc='lower right', ncol=1, fancybox=True, shadow=True, prop={'size': 6})
mp.grid()
mp.show()
```

## Answer by David Duarte (score 4, accepted)

https://quant.stackexchange.com/a/50781

Here are the things you need to correct in your code:

Although you are setting a seed, you are generating the random numbers twice, and therefore they are not identical. Try this:

```
rand = np.random.randn(N)
y = np.exp(-volvol**2.0/2 * time_step + volvol * s * rand)
y1 = rand
```

You also need to multiply the sigma by the square root of the timestep in the SDE version :

```
dB = volvol * B[i-1] * y1[i] * s
```

That should be enough to get the same results.

Apart from that, your volatility is 4000%. The sigma should be `0.4` and not `40.0`.

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.