Monte Carlo Simulations on Stock Portfolios in Python
While learning about different forecasting methods available in Python, I came across the Monte Carlo Simulation. To practise this I will pull stock data from the Yahoo Finance API and use it to create a stock portfolio to run Monte Carlo simulations on.
Importing relevant modules
I am using pretty common Python modules such as Pandas, NumPy and Matplotlib.
Yahoo Finance (yfinance) is used here to pull data on stocks.
#importing relevant modules
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import datetime as dt
import yfinance as yf
Creating a function to pull data
This function is used to pull the necessary stock data to create our Monte Carlo simulation, the Mean Returns and Covariance.
It first looks up which stocks are being searched for, along with start and end dates.
Next it pulls the close prices for those dates, calculates the mean returns, and lastly calculates the covariance.
#function for importing data
def get_data(stocks, start, end):
stockData = yf.download(stocks, start, end)
stockData = stockData['Close']
returns = stockData.pct_change()
meanReturns = returns.mean()
covMatrix = returns.cov()
return meanReturns, covMatrix
Creating our portfolio
This section is where we define which stocks will be in our portfolio, and which date ranges to use.
For this portfolio I have chosen the largest Blue Chip Tech Stocks: Apple, Microsoft, Alphabet/Google, Amazon, Nvidia and Meta/Facebook. We can add or reduce stocks by adjusting the tickers selected in the code, we can even include indexes like the S&P 500 or FTSE 100.
For simplicity, the date range we are using is the past 300 days from the day the model is run.
For the portfolio, each stock is weighed evenly.
#selecting stocks and date ranges
stockList = ['AAPL','MSFT','GOOG','AMZN','NVDA','META']
stocks = [stock for stock in stockList]
endDate = dt.datetime.now()
startDate = endDate - dt.timedelta(days=300)
#running function to pull data on selected stocks
meanReturns, covMatrix = get_data(stocks, startDate, endDate)
#evenly weighing each stock in portfolio
weights = [1/(len(stockList))] * len(stockList)
Creating the Monte Carlo Simulation
This model will predict 100 days into the future, and will run 400 simulations.
We create a blank table for the model to fill with it’s predicted values for each simulation.
The model uses a normal distribution and the cholesky decomposition on the covariance matrix. It sums the daily returns to return a cumulative product, showing how the portfolio will grow/fall over our time period.
# monte carlo simulation
mc_sims = 400 # number of simulations
T = 100 #timeframe in days
meanM = np.full(shape=(T, len(weights)), fill_value=meanReturns)
meanM = meanM.T
portfolio_sims = np.full(shape=(T, mc_sims), fill_value=0.0)
initialPortfolio = 10000
for m in range(0, mc_sims):
Z = np.random.normal(size=(T, len(weights)))#uncorrelated RV's
L = np.linalg.cholesky(covMatrix) #cholesky decomposition
dailyReturns = meanM + np.inner(L, Z) #correlated daily returns for individual stocks
portfolio_sims[:,m] = np.cumprod(np.inner(weights, dailyReturns.T)+1)*initialPortfolio
Results of the simulation
Here are how the plots of the Monte Carlo look with different numbers of simulations. I find it helpful to show the simulation in stages as a single image with 400 lines can be difficult to comprehend.
#plotting the simulation
plt.plot(portfolio_sims)
plt.ylabel('Predicted Portfolio Value ($)')
plt.xlabel('Days')
plt.title('Monte Carlo Simulation of Stock Portfolio')
plt.show()
This is how the model looks with 1 simulation:
This is how the model looks with 10 simulations:
This is how the model looks with 50 simulations:
This is how the model looks with 400 simulations:
As we add more simulations we see a wider and denser scope of possibilities. On average we should expect a moderate upward trend where portfolio value steadily increases as days pass. There are some more fringe cases which show a sharper upward trend in portfolio value, and cases where the portfolio decreases over time.
This was an interesting exercise for me, I can see how this would be useful to predict and show the range of possibilities of an expected outcome. It can be applied to areas beyond stock portfolios such as sales forecasting, energy usage etc.
Thank you for reading
This project was a great way for me to explore what I’m capable of doing in Python. I hope to carry on learning and being able to do even more as a data analyst as I continue to develop my knowledge of tools like Python, SQL and PowerBI.