Scaling GBM Drift and Volatility Across Time Steps
Summary
This exchange explains how to simulate geometric Brownian motion at different resolutions when drift and volatility are specified annually. The central correction is to express both the simulation horizon and each time increment as fractions of a year, then pass the annualized drift and volatility unchanged into the GBM formula. Brownian increments scale with the square root of elapsed time internally, while the drift accumulates in proportion to elapsed time.
For daily inputs, annual volatility converts by dividing by the square root of the number of days in a year, whereas annual drift converts by dividing by that number directly. The questioner's approach incorrectly scales drift by a square root and mixes the time step and volatility units. The accepted answer applies year fractions for a continuously traded asset and gives daily equivalents. The discussion is a unit-consistency explanation rather than a validation of GBM as a realistic model for highly volatile assets; real returns may depart from its assumptions.
Key ideas
- Express the total horizon and each step in consistent year fractions when using annualized parameters.
- In a GBM, volatility scales with the square root of elapsed time.
- Drift scales linearly with elapsed time, rather than with its square root.
- For daily simulation inputs, convert annual volatility and drift using different scaling rules.
- Correct units resolve the simulation mismatch but do not establish that GBM captures real market behavior.
Tags
Full text
# Geometric brownian motion small timesteps high volatility
# Geometric brownian motion small timesteps high volatility
I'm trying to generate some sample geometric brownian motion paths for an asset which is traded 24/7 without interruption and is highly volatile (upwards to 150% implied volatility on options markets).
I'm currently using this script: https://stackoverflow.com/a/13203189/5433929
I would like to generate sample paths with arbitrary resolution like a minute, an hour, a day, or anything between these. My understanding is that if I assume an annualized volatility of 150% for example, and I want to generate an hourly GBM using the script I linked, I will need to convert this 150% in to an hourly volatility, which is done by taking `1.5/sqrt(number of hours in a year)`. However when I do this, the sample paths generated over for example a 30 days time horizon are not realistic at all. Depending on the drift parameter, it eithers goes almost in a straight line up, varies with the typical GBM features within an extremely tight range of +/- 0.1% not at all representative of an asset with such high volatility.
What am I doing wrong here?
Thanks in advance.
EDIT:
Due to demand from @Kermittfrog, I'm pasting in my specific script.
```
def generateGBM(T, mu, sigma, S0, dt):
'''
Generate a geometric brownian motion time series. Shamelessly copy pasted from here: https://stackoverflow.com/a/13203189
Params:
T: time horizon
mu: drift
sigma: percentage volatility
S0: initial price
dt: size of time steps
Returns:
t: time array
S: time series
'''
N = round(T/dt)
t = np.linspace(0, T, N)
W = np.random.standard_normal(size = N)
W = np.cumsum(W)*np.sqrt(dt) ### standard brownian motion ###
X = (mu-0.5*sigma**2)*t + sigma*W
S = S0*np.exp(X) ### geometric brownian motion ###
return t, S
```
I'm doing this with the following parameters:
```
#Initial reference market price
INITIAL_PRICE = 1100
#The desired annualized volatility
ANNUALIZED_VOL = 1.5
#The annual drift of the geometric brownian motion
DRIFT = 0.04
#The time horizon in days
TIME_HORIZON = 30
#The size of the time steps in days (20 minutes here)
TIME_STEPS_SIZE = 0.0138889
```
and scaling down annualized volatility and drift by dividing by the square root of the number of timesteps there would be in a year
```
N_timesteps = 365/dt
sigma_timestep = sigma/np.sqrt(N_timesteps)
mu = DRIFT/np.sqrt(N_timesteps)
```
The result is plots that don't really look anything like what one would expect from a 150% volatility asset over a period of a month, unless my expectations are really wrong
## Answer by Kermittfrog (score 1, accepted)
https://quant.stackexchange.com/a/66213
Given your code, the following will yield what you are after:
```
t,S = generateGBM(TIME_HORIZON/365, DRIFT, ANNUALIZED_VOL, INITIAL_PRICE, 1/365/24/3)
```
As all inputs are annualized, you must also think in units of year fractions: The time horizon is 30 days over 365 days, and the time step size, being 20 minutes, is one year over `365 * 24 * 3` (there are three 20-minutes intervals in an hour).
If, on the other hand, you want to work with daily inputs, you can run:
```
t,S = generateGBM(TIME_HORIZON, DAILY_DRIFT, DAILY_VOL, INITIAL_PRICE, 1/24/3)
```
and you can transform annual vol and annual drift into daily counterparts as
```
DAILY_VOL = np.sqrt(1/365) * ANNUALIZED_VOL
DAILY_DRIFT = 1/365 * DRIFT
```
HTH?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.