Monte Carlo Part 1
Monte carlo simulations are extremely useful in finance. In this part of the course we will build out a monte carlo dashboard. The first step is to define a way to create random paths. The following will achieve that like we did in the options course.
import numpy as np
import pandas as pd
rf = .02
price0 = 100
sigma = .08
runs = 10000
periods = 252
#Build a function to wrap everything in a function
def simulate_returns(price0, rf, periods, sigma, runs, time_step = 1/252):
returns = pd.DataFrame(np.random.normal(0,1, periods * runs).reshape(periods, runs))
returns = returns * sigma * time_step ** .5 + (rf - .5 * sigma ** 2) * time_step
returns = np.exp(returns).cumprod()
returns.index = returns.index + 1
returns.loc[0] = 1
returns = returns.sort_index()
paths = returns * price0
return paths
paths = simulate_returns(price0, rf, periods, sigma, runs)
print(paths)
Now for a dashboard part. There are a few things to note. First of all, we are going to use a data dictionary to store the data so that we can use it outside of the widget. So first we define a blank one and then every time this is run we set the data component to be equal to the paths we made. We also are going to opt to use manual interaction because running these takes time and we do not want excessive runs. Finally, we will simply fix the time increment as 1/252 meaning trading days by setting the variable as fixed().
Now this code will implement the widgets, make sure you run it at least once so that the future pieces work. Since it is manual it will not automatically run.
from ipywidgets import interact_manual
from ipywidgets import widgets
#Create widgets
rf = widgets.FloatSlider(value=.02,
min=.01,
max=.10,
step=.001,
description='Risk-free Rate:',
readout_format='.1%'
)
price0 = widgets.IntSlider(value=100,
min=1,
max=1000,
step = 10,
description='Price:'
)
sigma = widgets.FloatSlider(value=.08,
min=.01,
max=.40,
step=.001,
description='Sigma:',
readout_format='.1%'
)
runs = widgets.IntSlider(value=10000,
min=1,
max=1000000,
step = 10,
description='Runs:'
)
periods = widgets.IntSlider(value=252,
min=10,
max=504,
step = 10,
description='Periods:'
)
#Create data dictionary
data_dictionary = {}
#Modify the formula so that the data dictionary is modified
def simulate_returns(price0, rf, periods, sigma, runs, time_step = 1/252):
returns = pd.DataFrame(np.random.normal(0,1, periods * runs).reshape(periods, runs))
returns = returns * sigma * time_step ** .5 + (rf - .5 * sigma ** 2) * time_step
returns = np.exp(returns).cumprod()
returns.index = returns.index + 1
returns.loc[0] = 1
returns = returns.sort_index()
paths = returns * price0
data_dictionary['Data'] = paths
print("Data Updated")
#To set the data you need to make sure you click the Run Interact button
interact_manual(simulate_returns, price0=price0, periods=periods, rf=rf, sigma=sigma, runs=runs,
time_step = widgets.fixed(1/252));
The below function can be used to draw the path of the stock on average with a 95% confidence interval around the prices over each day.
import matplotlib.pyplot as plt
#Create a function which draws paths with confidence intervals
def draw_paths(paths):
df = pd.concat([paths.mean(axis=1) - paths.std(axis=1) * 1.645,
paths.mean(axis=1),
paths.mean(axis=1) + paths.std(axis=1) * 1.645], axis=1)
df.columns = ['Lower Bound', 'Mean', 'Upper Bound']
plt.plot(df.index, df['Lower Bound'].values, color='red', linestyle='--')
plt.plot(df.index, df['Mean'].values, color='black')
plt.plot(df.index, df['Upper Bound'].values, color='red', linestyle='--')
plt.xlabel("T")
plt.ylabel("Stock Price")
plt.legend(['Lower Bound', 'Mean','Upper Bound'])
plt.title("Stock Price Confidence Interval")
plt.show()
draw_paths(data_dictionary['Data'])
Add this to the widget so that when we actually run the paths we get a nice visualization.
#Update the formula to graph the path after data is run
def simulate_returns(price0, rf, periods, sigma, runs, time_step = 1/252):
returns = pd.DataFrame(np.random.normal(0,1, periods * runs).reshape(periods, runs))
returns = returns * sigma * time_step ** .5 + (rf - .5 * sigma ** 2) * time_step
returns = np.exp(returns).cumprod()
returns.index = returns.index + 1
returns.loc[0] = 1
returns = returns.sort_index()
paths = returns * price0
data_dictionary['Data'] = paths
draw_paths(paths)
#Once again, make sure the click the button
interact_manual(simulate_returns, price0=price0, periods=periods, rf=rf, sigma=sigma, runs=runs,
time_step = widgets.fixed(1/252));
Challenge