Stock Protfolio Simulation
August 2023 (1190 Words, 7 Minutes)
This blog takes 8 stocks and simulates how well a portfolio would do if invested. It also tells what the best weighting for each stock would be the most optimal. It gives important information such as risk, volatility, profit, Sharpe Ratio, etc.
8 stocks have been chosen at random which are Intel (INTC), Ford Motor Company (F), Walt Disney Co (DIS), Tesla (TSLA), Amazon (AMZN), Bank of America (BAC), Sony (SONY), and Meta (META)
# Reading in the stocks of each stock and then creating a central data frame of each.
import numpy as np
import pandas as pd
import pandas_datareader.data as web
# Get stock data
all_data = {ticker: web.DataReader(ticker,'stooq')
for ticker in ['INTC', 'F', 'DIS', 'TSLA', 'AMZN', 'BAC', 'SONY', 'META']}
# Extract the 'Adjusted Closing Price'
price = pd.DataFrame({ticker: data['Close']
for ticker, data in all_data.items() })
price
INTC | F | DIS | TSLA | AMZN | BAC | SONY | META | |
---|---|---|---|---|---|---|---|---|
Date | ||||||||
2023-09-06 | 36.9800 | 12.07000 | 80.98 | 251.9200 | 135.3600 | 28.3900 | 85.5000 | 299.17 |
2023-09-05 | 36.7100 | 12.09000 | 81.19 | 256.4900 | 137.2700 | 28.6500 | 84.5400 | 300.15 |
2023-09-01 | 36.6100 | 12.14000 | 81.64 | 245.0100 | 138.1200 | 28.9800 | 85.2600 | 296.38 |
2023-08-31 | 35.1400 | 12.13000 | 83.68 | 258.0800 | 138.0100 | 28.6700 | 83.1900 | 295.89 |
2023-08-30 | 34.5300 | 12.03000 | 84.28 | 256.9000 | 135.0700 | 29.0400 | 82.3500 | 295.10 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
2018-09-13 | 40.4097 | 8.17819 | 108.49 | 19.2973 | 99.4935 | 27.3640 | 56.6914 | 161.36 |
2018-09-12 | 39.8432 | 8.15184 | 107.31 | 19.3693 | 99.5000 | 27.6253 | 56.4070 | 162.00 |
2018-09-11 | 39.8432 | 8.12587 | 107.45 | 18.6293 | 99.3575 | 28.0093 | 55.7992 | 165.94 |
2018-09-10 | 41.0554 | 8.18682 | 108.50 | 19.0333 | 96.9505 | 27.9808 | 55.6325 | 164.18 |
2018-09-07 | 41.1893 | 8.09099 | 108.79 | 17.5493 | 97.6035 | 28.0181 | 55.4838 | 163.04 |
1257 rows × 8 columns
The standard deviation is how investors measure volatility and risk. STD measures the average distance from the mean, so the higher the STD the less concise the data is. In the finance world, if the STD is higher, that means that the price of the stock can be more unpredictable. This equates to risk. The lower the STD the better, and vice versa.
# finding standard deviation
price.std()
INTC 9.269080
F 3.718751
DIS 29.812228
TSLA 112.305918
AMZN 32.601511
BAC 6.760205
SONY 21.699832
META 68.639090
dtype: float64
The correlation between each of the stocks shows how well the portfolio will do. The correlation in math shows the relationship and the proportion between two variables. In finance the correlation dictates how two stocks will react in relationship. In layman terms it shows if one stock will go up what will another stock do. Correlation ranges from -1 to 1, where -1 is the most optimal.
# finding correlation of stocks
price.corr()
INTC | F | DIS | TSLA | AMZN | BAC | SONY | META | |
---|---|---|---|---|---|---|---|---|
INTC | 1.000000 | -0.207316 | 0.707290 | -0.138098 | 0.374776 | 0.087599 | 0.102494 | 0.461628 |
F | -0.207316 | 1.000000 | 0.209967 | 0.827277 | 0.448191 | 0.879887 | 0.749091 | 0.377600 |
DIS | 0.707290 | 0.209967 | 1.000000 | 0.242519 | 0.615637 | 0.491690 | 0.473677 | 0.646180 |
TSLA | -0.138098 | 0.827277 | 0.242519 | 1.000000 | 0.723774 | 0.744394 | 0.887155 | 0.518510 |
AMZN | 0.374776 | 0.448191 | 0.615637 | 0.723774 | 1.000000 | 0.522691 | 0.806006 | 0.837762 |
BAC | 0.087599 | 0.879887 | 0.491690 | 0.744394 | 0.522691 | 1.000000 | 0.763278 | 0.459897 |
SONY | 0.102494 | 0.749091 | 0.473677 | 0.887155 | 0.806006 | 0.763278 | 1.000000 | 0.720367 |
META | 0.461628 | 0.377600 | 0.646180 | 0.518510 | 0.837762 | 0.459897 | 0.720367 | 1.000000 |
By finding the correlation of the entire portfolio, it will show how well it will do. The closer to -1 the better.
# Finding average correlation to show profability of portfolio
averageCorr = price.corr()
averageCorrMean = averageCorr.mean()
averageCorrMean
column_sum = 0
# For loop to find the mean of the entire data table
for i in range(len(averageCorrMean)):
column_sum += averageCorrMean[i]
column_sum = column_sum/len(averageCorrMean)
column_sum
0.5729350722124822
This is where the math and the real fun begins. This next code finds the optimal weights of each stock. It does this by running through 6000 differnt scenarios each with different weighting. It finds the weights by finding the retention factor and the volatility of each stock and then compare it to each of the other 7 stocks. Then once it has done that it’ll compare it to average yearly stock prices where it will then compare all 6000 scenarios and output the most optimal.
# finding weights, return, volitilty, and sharpe ratio.
stocks = pd.concat([price['INTC'], price['F'], price['DIS'], price['TSLA'], price['AMZN'], price['BAC'], price['SONY'], price['META']], axis = 1)
log_ret = np.log(stocks/stocks.shift(1))
# setting up variables
np.random.seed(42)
num_ports = 6000
num_stocks = 8
all_weights = np.zeros((num_ports, len(stocks.columns)))
ret_arr = np.zeros(num_ports)
vol_arr = np.zeros(num_ports)
sharpe_arr = np.zeros(num_ports)
# going through all possible weights
for x in range(num_ports):
# Weights
weights = np.array(np.random.random(num_stocks))
weights = weights/np.sum(weights)
# Save weights
all_weights[x,:] = weights
# Expected return
ret_arr[x] = np.sum( (log_ret.mean() * weights * 252))
# Expected volatility
vol_arr[x] = np.sqrt(np.dot(weights.T, np.dot(log_ret.cov()*252, weights)))
# Sharpe Ratio
sharpe_arr[x] = ret_arr[x]/vol_arr[x]
The Sharpe Ratio is how investors determine the profability of a portfolio in a single number that can be compared to other portfolios. In finance, the Sharpe ratio measures the performance of an investment such as a security or portfolio compared to a risk-free asset, after adjusting for its risk.
# printing the max sharpe ratio
print("Max Sharpe Ratio = ",sharpe_arr.max())
sharpe_arr.argmax()
max_sr_ret = ret_arr[sharpe_arr.argmax()]
max_sr_vol = vol_arr[sharpe_arr.argmax()]
Max Sharpe Ratio = -8.584010866738334e-05