Finding the most efficient portfolio¶
Now with the help of our computer, we will consider ALL the different sets of weights possible our stocks to reach a portfolio that has the highest Sharpe ratio. That is, among these random portfolios, I would like to find the most efficient ones (According to Sharpe). This notebook is a sequel to previous notes where I am using a dataset of 14 securities. Thus I suggest you read the previous notes to fully understand what is going on here.
To do so, we need to give a series of instructions to the computer to:
- Choose a random set of weights each time
- Create a portfolio with them
- Measure the risk
- Calculate the Sharpe ratio
- Finally, save it in a list so it can be compared with all other portfolios made through the same process
# This is the package for downloading US stock data
import yfinance as yf, pandas as pd, numpy as np, datetime as dt
import matplotlib as mpl, matplotlib.pyplot as plt
Importing an Excel File¶
To import an Excel file in Python, I will first create a variable "main_data" and assign to it (with the help of Pandas
) a transformed version of the Excel file we created in the last Notebook "8_stock_returns.xlsx".
Note: the Excel file and this Jupyter notebook should be located in the same folder. Otherwise, the process of importing the excel file may require extra steps of code.
main_data = pd.read_excel('returns_cleaned.xlsx', index_col='Date')
# also drop the the columns (Port_1 & Risk Fee) since I do not need them in my task here
main_data = main_data.drop(columns=['Port_1', 'Risk Free'])
main_data.head()
AMZN | BA | CVX | DIS | GOLD | HSY | INTC | K | KO | M | MMM | MSFT | NVDA | ^W5000 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||||||
2000-02 | 0.066796 | -0.169944 | -0.106876 | -0.063683 | -0.003817 | 0.033823 | 0.142136 | 0.043815 | -0.153428 | -0.118619 | -0.058078 | -0.086845 | 0.726813 | 0.021192 |
2000-03 | -0.027223 | 0.027196 | 0.248125 | 0.213236 | -0.038314 | 0.115744 | 0.167938 | 0.028577 | -0.034704 | 0.151618 | 0.010757 | 0.188811 | 0.320068 | 0.058114 |
2000-04 | -0.176306 | 0.049587 | -0.079108 | 0.057576 | 0.071714 | -0.066667 | -0.038844 | -0.058252 | 0.010433 | -0.195266 | -0.021877 | -0.343529 | 0.054929 | -0.052775 |
2000-05 | -0.124575 | -0.015748 | 0.085904 | -0.032951 | 0.078067 | 0.140109 | -0.016757 | 0.252577 | 0.129630 | 0.132353 | -0.010101 | -0.103047 | 0.280505 | -0.036091 |
2000-06 | -0.248383 | 0.074325 | -0.076026 | -0.080000 | 0.008687 | -0.060264 | 0.072445 | -0.012356 | 0.076112 | -0.123377 | -0.025796 | 0.278721 | 0.113911 | 0.043327 |
main_data.describe()
AMZN | BA | CVX | DIS | GOLD | HSY | INTC | K | KO | M | MMM | MSFT | NVDA | ^W5000 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 287.000000 | 287.000000 | 287.000000 | 287.000000 | 287.000000 | 287.000000 | 287.000000 | 287.000000 | 287.000000 | 287.000000 | 287.000000 | 287.000000 | 287.000000 | 287.000000 |
mean | 0.021791 | 0.012425 | 0.009857 | 0.006993 | 0.008019 | 0.011105 | 0.006905 | 0.006957 | 0.006123 | 0.010586 | 0.007103 | 0.012066 | 0.037451 | 0.005589 |
std | 0.129926 | 0.095598 | 0.069003 | 0.076331 | 0.114357 | 0.057571 | 0.097305 | 0.049571 | 0.050752 | 0.129830 | 0.060703 | 0.081098 | 0.174042 | 0.045973 |
min | -0.411552 | -0.454664 | -0.214625 | -0.267794 | -0.381056 | -0.177236 | -0.444732 | -0.138636 | -0.172743 | -0.628874 | -0.157037 | -0.343529 | -0.486551 | -0.177406 |
25% | -0.048718 | -0.042805 | -0.029320 | -0.034121 | -0.065293 | -0.025150 | -0.047196 | -0.021066 | -0.024001 | -0.061762 | -0.028618 | -0.037006 | -0.054362 | -0.021149 |
50% | 0.021677 | 0.015899 | 0.008623 | 0.005748 | 0.007592 | 0.008660 | 0.007672 | 0.009468 | 0.008546 | 0.011221 | 0.010560 | 0.017244 | 0.030835 | 0.011240 |
75% | 0.087617 | 0.070993 | 0.046279 | 0.050941 | 0.072783 | 0.044363 | 0.062263 | 0.035843 | 0.038312 | 0.077280 | 0.039402 | 0.055028 | 0.122156 | 0.034368 |
max | 0.621776 | 0.459312 | 0.269666 | 0.248734 | 0.426362 | 0.275952 | 0.337429 | 0.252577 | 0.141928 | 0.644122 | 0.213949 | 0.407781 | 0.826237 | 0.133806 |
stock_names = [x for x in main_data.columns]
stock_means = np.array([main_data[x].mean() for x in stock_names]).T
stock_cov = np.array(main_data[stock_names].cov())
dataset_columns = stock_names + ['Port_Return', 'Port_Risk', 'Port_Sharpe']
r_f = 0.005
no_stocks = len(stock_names)
main_list = []
for i in range(10000000):
weight = np.array([np.random.random_sample() for x in stock_names])
weight /= np.sum(weight)
entry = [weight.dot(stock_means)]
entry.append(weight.dot(stock_cov).dot(weight.T)**0.5)
sharpe = (entry[-2] - r_f ) / entry[-1]
entry.append(sharpe)
main_list.append(np.append(weight, entry))
print('done!!!')
done!!!
main_matrix = np.vstack(main_list)
main_matrix[:7]
array([[0.08952249, 0.1237281 , 0.09806072, 0.08110084, 0.0997132 , 0.08158651, 0.08989499, 0.00660169, 0.0151624 , 0.10605358, 0.05307233, 0.06608257, 0.0041403 , 0.08528027, 0.0104156 , 0.05132228, 0.10552137], [0.05307114, 0.09823329, 0.02633822, 0.13302875, 0.06579368, 0.10195316, 0.13875912, 0.07471172, 0.03135454, 0.00455951, 0.02169596, 0.05511066, 0.07023474, 0.1251555 , 0.01109369, 0.04957423, 0.12292059], [0.04419427, 0.12961178, 0.01164222, 0.13435415, 0.13914584, 0.10364673, 0.09756549, 0.03148035, 0.06038125, 0.14337519, 0.04890231, 0.02915401, 0.01648726, 0.01005915, 0.0100475 , 0.05137652, 0.09824526], [0.08085281, 0.00766668, 0.00561845, 0.03279166, 0.00033228, 0.11513434, 0.01633928, 0.15791356, 0.13349173, 0.0208246 , 0.03281222, 0.13272392, 0.11264784, 0.15085063, 0.01256861, 0.04753374, 0.15922604], [0.02992966, 0.01160897, 0.0241734 , 0.14091895, 0.0794554 , 0.01960431, 0.11477198, 0.07319995, 0.18034245, 0.11808549, 0.01886189, 0.13651088, 0.0375537 , 0.01498296, 0.00980218, 0.04965786, 0.09670536], [0.06936717, 0.07466782, 0.06707473, 0.11496441, 0.07953203, 0.11240064, 0.01256041, 0.02881488, 0.03830221, 0.10925284, 0.07255171, 0.04336411, 0.09058646, 0.08656057, 0.01238356, 0.05171059, 0.14278626], [0.02401006, 0.04123624, 0.09985102, 0.0852718 , 0.03947427, 0.08406158, 0.14704075, 0.01334256, 0.12612358, 0.00170426, 0.11442047, 0.12646769, 0.02193327, 0.07506246, 0.0093441 , 0.04506295, 0.09640072]])
efficient_port = pd.DataFrame(main_matrix[1:], columns = dataset_columns)
efficient_port.describe().apply(lambda s: s.apply('{0:.5f}'.format))
AMZN | BA | CVX | DIS | GOLD | HSY | INTC | K | KO | M | MMM | MSFT | NVDA | ^W5000 | Port_Return | Port_Risk | Port_Sharpe | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 | 9999999.00000 |
mean | 0.07141 | 0.07143 | 0.07146 | 0.07139 | 0.07143 | 0.07145 | 0.07143 | 0.07142 | 0.07143 | 0.07143 | 0.07143 | 0.07141 | 0.07145 | 0.07142 | 0.01164 | 0.05068 | 0.13037 |
std | 0.04116 | 0.04117 | 0.04117 | 0.04116 | 0.04118 | 0.04115 | 0.04118 | 0.04117 | 0.04118 | 0.04115 | 0.04118 | 0.04117 | 0.04117 | 0.04116 | 0.00131 | 0.00426 | 0.01932 |
min | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 0.00728 | 0.03525 | 0.05057 |
25% | 0.03704 | 0.03705 | 0.03706 | 0.03701 | 0.03702 | 0.03710 | 0.03703 | 0.03704 | 0.03703 | 0.03706 | 0.03703 | 0.03701 | 0.03706 | 0.03706 | 0.01067 | 0.04770 | 0.11688 |
50% | 0.07141 | 0.07142 | 0.07148 | 0.07138 | 0.07141 | 0.07145 | 0.07143 | 0.07141 | 0.07143 | 0.07142 | 0.07143 | 0.07139 | 0.07145 | 0.07141 | 0.01164 | 0.05047 | 0.13175 |
75% | 0.10352 | 0.10355 | 0.10358 | 0.10348 | 0.10357 | 0.10354 | 0.10356 | 0.10354 | 0.10355 | 0.10353 | 0.10355 | 0.10355 | 0.10357 | 0.10352 | 0.01258 | 0.05341 | 0.14450 |
max | 0.30460 | 0.42600 | 0.35769 | 0.33866 | 0.36738 | 0.32222 | 0.34830 | 0.35433 | 0.32917 | 0.31468 | 0.39217 | 0.33261 | 0.32792 | 0.38799 | 0.02018 | 0.08534 | 0.20671 |
efficient_port.head()
AMZN | BA | CVX | DIS | GOLD | HSY | INTC | K | KO | M | MMM | MSFT | NVDA | ^W5000 | Port_Return | Port_Risk | Port_Sharpe | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.053071 | 0.098233 | 0.026338 | 0.133029 | 0.065794 | 0.101953 | 0.138759 | 0.074712 | 0.031355 | 0.004560 | 0.021696 | 0.055111 | 0.070235 | 0.125156 | 0.011094 | 0.049574 | 0.122921 |
1 | 0.044194 | 0.129612 | 0.011642 | 0.134354 | 0.139146 | 0.103647 | 0.097565 | 0.031480 | 0.060381 | 0.143375 | 0.048902 | 0.029154 | 0.016487 | 0.010059 | 0.010047 | 0.051377 | 0.098245 |
2 | 0.080853 | 0.007667 | 0.005618 | 0.032792 | 0.000332 | 0.115134 | 0.016339 | 0.157914 | 0.133492 | 0.020825 | 0.032812 | 0.132724 | 0.112648 | 0.150851 | 0.012569 | 0.047534 | 0.159226 |
3 | 0.029930 | 0.011609 | 0.024173 | 0.140919 | 0.079455 | 0.019604 | 0.114772 | 0.073200 | 0.180342 | 0.118085 | 0.018862 | 0.136511 | 0.037554 | 0.014983 | 0.009802 | 0.049658 | 0.096705 |
4 | 0.069367 | 0.074668 | 0.067075 | 0.114964 | 0.079532 | 0.112401 | 0.012560 | 0.028815 | 0.038302 | 0.109253 | 0.072552 | 0.043364 | 0.090586 | 0.086561 | 0.012384 | 0.051711 | 0.142786 |
with plt.style.context('ggplot'):
efficient_port.plot.scatter(x='Port_Risk',
y = 'Port_Return',
c= 'Port_Sharpe',
s=0.75,
colormap='Reds',
title= 'Alternative Portfolios & Their Sharpe Ratios',
xlabel= "Portfolio Risk (Standard Deviation)",
ylabel= 'Portfolio Return (%)',
figsize=(14,7))
plt.show()
# What is the highest Sharpe ratio in this table?
efficient_port['Port_Sharpe'].max()
0.2067120020682262
efficient_port['Port_Sharpe'].idxmax()
8835253
efficient_port.iloc[8835253]
AMZN 0.184954 BA 0.038137 CVX 0.005322 DIS 0.000620 GOLD 0.078891 HSY 0.234023 INTC 0.005338 K 0.014769 KO 0.041534 M 0.018769 MMM 0.077062 MSFT 0.057378 NVDA 0.215487 ^W5000 0.027718 Port_Return 0.017850 Port_Risk 0.062163 Port_Sharpe 0.206712 Name: 8835253, dtype: float64
# What are stock the weights for this highest Sharpe ratio?
efficient_port.iloc[efficient_port['Port_Sharpe'].idxmax()]
AMZN 0.184954 BA 0.038137 CVX 0.005322 DIS 0.000620 GOLD 0.078891 HSY 0.234023 INTC 0.005338 K 0.014769 KO 0.041534 M 0.018769 MMM 0.077062 MSFT 0.057378 NVDA 0.215487 ^W5000 0.027718 Port_Return 0.017850 Port_Risk 0.062163 Port_Sharpe 0.206712 Name: 8835253, dtype: float64