I have written a simple script for modelling stock prices using Geometric Brownian Motion. The time series I am downloading are daily adjusted closing prices. My aim is to be able to change the prediction period and all other variables.

However, I am trying to understand the price prediction differences between calculating the mu (average returns) and sigma (variance) using a linear or standard methods versus using a log approach. The log approach consistently generates a higher predicted stock price. My code is below.

I have scavenged the internet and read whatever I could find. There are several helpful articles on this forum as well, such as here and here. But nothing really covers my question.

My question is, which approach is the most appropriate?

(I am using Python 3.)

from math import log, e
import matplotlib.pyplot as plt
from pandas_datareader import data
from datetime import date, timedelta
import datetime

stock = 'AAPL' # Enter the name of the stock
start = '2015/1/1'
apple = data.DataReader(stock, 'yahoo', start)

# This is the 'normal' way of calculating mu and sigma
close = apple[:]['Adj Close']
mu = (close[-1]/close[1])** (252.0/len(close)) - 1
sigma = (close/close.shift(1)-1)[1:].std()*np.sqrt(252)

# This is the 'log' way of calculating mu and sigma
apple['log_price'] = np.log(apple['Adj Close'])
apple['log_return'] = apple['log_price'].diff()
mu = apple.log_return.sum() -1
sigma = np.std(apple.log_price)

s0 = close[-1]
T = 18/12
delta_t = 0.001
num_reps = 1000
steps = T/delta_t
closing_prices = []
for j in range(num_reps):
    price_path = [s0]
    st = s0
    for i in range(int(steps)):
        drift = (mu - 0.5 * sigma**2) * delta_t
        diffusion = sigma * np.sqrt(delta_t) * np.random.normal(0, 1)
        st = st*e**(drift + diffusion)
plt.ylabel('stock price',fontsize=15)
plt.axhline(y = s0, color = 'r', linestyle = '-') # print latest price TW

mean_end_price = round(np.mean(closing_prices),2)
print("Expected price in 12 months: $", str(mean_end_price))

  • $\begingroup$ "The log approach consistently generates a higher predicted stock price" because your log drift is higher than your normal drift due to the way you calculate mu for log vs normal which is pointed out in the answer below. $\endgroup$
    – amdopt
    Jun 25, 2020 at 18:56

1 Answer 1


The drift in your code is:

drift = (mu - 0.5 * sigma**2) * delta_t

So I assume you are using the Geometric Brownian Motion to simulate your stock price, not just plain Brownian motion. Therefore your model is Lognormal, not Normal. Also, I assume that the time series that you're downloading is daily closing prices.

The solution to the GBM model is the following (below $\delta t:=(t_i)-(t_{i-1})$ & $Z\sim~N(0,1)$):

$$ ln(S_{t_i}) - ln(S_{t_{i-1}}) = (\mu - 0.5\sigma^2)\delta t+\sigma\sqrt{\delta t}Z $$

Note that the log-returns above are distributed normally:

$$ln(S_{t_i}) - ln(S_{t_{i-1}}) \sim N\left(\tilde{\mu} := (\mu - 0.5\sigma^2)\delta t;\tilde{\sigma}:=\sqrt{\delta t}\sigma\right)$$

If we're using daily time series, then $\delta t = \frac{1}{260}$.

If you wanna calibrate the above model based on historical daily data, your task is to calibrate $\tilde{\mu}$ and $\tilde{\sigma}$:

$$(i): \tilde{\mu}=\frac{1}{n}\sum_{i=1}^{n} ln\left( \frac{S_{t_i}}{S_{t_{i-1}}}\right)$$

$$ (ii): \tilde{\sigma}^2 = \frac{1}{n-1} \sum_{i=1}^{n} \left( \left[ ln\left( \frac{S_{t_i}}{S_{t_{i-1}}}\right) - \tilde{\mu} \right]^2\right) $$

Note that in (i) above, you computed $\tilde{\mu}$ and NOT $\mu$. In your simulation, you need $\mu$, so first do the following:

$$ \sigma = \tilde{\sigma}*\frac{1}{\sqrt{\delta t}}=\tilde{\sigma}*\sqrt{260} $$

$$\mu = \frac{1}{\delta t}*\tilde{\mu}+0.5{\sigma}^2=260*\tilde{\mu}+0.5{\sigma}^2$$

That is the $\mu$ you should use in your simulation.

The next point is: why do you have the following line in your code?

mu = apple.log_return.sum() -1

Why subtract the 1? If your log-returns are daily, to get $\mu$, your code should read:

mu = apple.log_return.sum()/apple.log_return.count()
mu = mu*260 + 0.5*apple.log_return.var()*sqrt(260)

To get the $\mu$ in line with the formulas I described above.

If you are using GBM to simulate your stock price, you should not be using regular returns to calibrate your model: so what you call "normal" way is the wrong way to calibrate your model.

Ps: if you have daily time series to start with, you can choose one day to be your unit of time if you want to simulate with daily granularity. That will make your task significantly easier, because you can work with $\tilde{\sigma}$ and $\tilde{\mu}$ directly, rather than having to annualize them: i.e. to convert them into $\sigma$ and $\mu$.

  • $\begingroup$ Thank you very much for this elaborate comment. Yes, I use Geometric Brownian Motion and daily closing prices (have updated the question, thanks). If I understand your answer correctly: (1) delta_t = 0.001 becomes delta_t = 1/260 (2) use your code for mu. Is that right? (P.S. I think sqrt in your code should be np.sqrt). $\endgroup$
    – twhale
    Jun 25, 2020 at 19:17
  • $\begingroup$ (1) Yes, (2) Yes (correct, should be np.sqrt()). Ps: why do you have T=18/12? Is your time series length 18 months? $\endgroup$ Jun 25, 2020 at 19:32
  • $\begingroup$ Thanks! T=18/12 because I want to predict the price 1.5 years from now. Are you using 1/260 because there are around 260 trading days in a year? $\endgroup$
    – twhale
    Jun 25, 2020 at 19:35
  • $\begingroup$ Perhaps I should use delta_t = 1 / T (with T expressed in days). $\endgroup$
    – twhale
    Jun 25, 2020 at 19:47
  • $\begingroup$ Yes I assume 260 trading days in a given year. Your $\delta t$ should be 1\260, and if you want to simulate for 1.5 years, you should have 390 time steps. Alternatively, your $\delta t$ can be equal to 1, and then your simulation parameters can simply be $\tilde{\mu}$ instead of $\mu$ and $\tilde{\sigma}$ instead of $\sigma$. You then still simulate over 390 time steps. $\endgroup$ Jun 25, 2020 at 20:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.