An example of a standard implementation in Python for optimization of an investment portfolio using the Markowitz method. There are many implementations of this method. Including Python. Implemented again (see link on GitHub ).
Sources
Let's take some theory from these sources:
Best Investment Portfolio Through Monte Carlo Simulation in Python
Markowitz Portfolio Theory (Wikipedia)
Downloading data on quotes
We use data from the Yahoo.Finance service
! pip install yfinance
import yfinance as yf
We take several shares of the American market over the past 3 months.
data = yf.download(['AAPL','GE','BAC','AMD','PLUG','F'],period='3mo')
Closing rates
We will use daily closing rates in our calculations
closeData = data.Close
closeData
Course charts
import matplotlib.pyplot as plt
for name in closeData.columns:
closeData[name].plot()
plt.grid()
plt.title(name)
plt.show()
Changing courses
Next, you need relative changes to the previous day.
dCloseData = closeData.pct_change()
dCloseData
Graphs of relative rate changes
for name in dCloseData.columns:
dCloseData[name].plot()
plt.title(name)
plt.grid()
plt.show()
Average yield
Average daily return for each share to calculate portfolio return.
dohMean = dCloseData.mean()
dohMean
Covariance
To calculate the portfolio risk, a covariance matrix is โโrequired.
cov = dCloseData.cov()
cov
Random portfolio
We will generate random portfolios. In them, the sum of the shares is equal to 1 (one).
import numpy as np
cnt = len(dCloseData.columns)
def randPortf():
res = np.exp(np.random.randn(cnt))
res = res / res.sum()
return res
r = randPortf()
print(r)
print(r.sum())
[0.07519908 0.07594622 0.20932539 0.40973202 0.1234458 0.10635148]
1.0
Portfolio return
The portfolio return is calculated as the sum of the shares of the returns for each stock in the portfolio.
def dohPortf(r):
return np.matmul(dohMean.values,r)
r = randPortf()
print(r)
d = dohPortf(r)
print(d)
[0.0789135 0.13031559 0.25977124 0.21157419 0.13506695 0.18435853]
0.006588795350151513
Portfolio riskยถ
We calculate the portfolio risk through matrix products of portfolio shares and covariance matrices.
def riskPortf(r):
return np.sqrt(np.matmul(np.matmul(r,cov.values),r))
r = randPortf()
print(r)
rs = riskPortf(r)
print(rs)
[0.10999361 0.13739338 0.20412889 0.13648828 0.24021123 0.17178461]
0.02483674110724784
Portfolio cloud
Let's generate a set of portfolios and display the result on a risk-return chart. Let's find the parameters of the optimal portfolio for the minimum risk and the maximum Sharpe ratio. Let's compare with the data of the averaged portfolio.
risk = np.zeros(N)
doh = np.zeros(N)
portf = np.zeros((N,cnt))
for n in range(N):
r = randPortf()
portf[n,:] = r
risk[n] = riskPortf(r)
doh[n] = dohPortf(r)
plt.figure(figsize=(10,8))
plt.scatter(risk*100,doh*100,c='y',marker='.')
plt.xlabel(', %')
plt.ylabel(', %')
plt.title(" ")
min_risk = np.argmin(risk)
plt.scatter([(risk[min_risk])*100],[(doh[min_risk])*100],c='r',marker='*',label=' ')
maxSharpKoef = np.argmax(doh/risk)
plt.scatter([risk[maxSharpKoef]*100],[doh[maxSharpKoef]*100],c='g',marker='o',label=' - ')
r_mean = np.ones(cnt)/cnt
risk_mean = riskPortf(r_mean)
doh_mean = dohPortf(r_mean)
plt.scatter([risk_mean*100],[doh_mean*100],c='b',marker='x',label=' ')
plt.legend()
plt.show()
Let's display the data of the found portfolios.
import pandas as pd
print('---------- ----------')
print()
print(" = %1.2f%%" % (float(risk[min_risk])*100.))
print(" = %1.2f%%" % (float(doh[min_risk])*100.))
print()
print(pd.DataFrame([portf[min_risk]*100],columns=dCloseData.columns,index=[', %']).T)
print()
print('---------- ----------')
print()
print(" = %1.2f%%" % (float(risk[maxSharpKoef])*100.))
print(" = %1.2f%%" % (float(doh[maxSharpKoef])*100.))
print()
print(pd.DataFrame([portf[maxSharpKoef]*100],columns=dCloseData.columns,index=[', %']).T)
print()
print('---------- ----------')
print()
print(" = %1.2f%%" % (float(risk_mean)*100.))
print(" = %1.2f%%" % (float(doh_mean)*100.))
print()
print(pd.DataFrame([r_mean*100],columns=dCloseData.columns,index=[', %']).T)
print()
---------- ----------
= 1.80%
= 0.59%
, %
AAPL 53.890706
AMD 12.793389
BAC 4.117541
F 16.547201
GE 10.945462
PLUG 1.705701
---------- ----------
= 2.17%
= 0.88%
, %
AAPL 59.257114
AMD 8.317192
BAC 2.049882
F 8.689935
GE 4.772159
PLUG 16.913719
---------- ----------
= 2.33%
= 0.68%
, %
AAPL 16.666667
AMD 16.666667
BAC 16.666667
F 16.666667
GE 16.666667
PLUG 16.666667
conclusions
We repeated the classical method of calculating the shares of an investment portfolio. We got quite concrete results.
Portfolio optimization using the Markowitz method presupposes the preservation of parameters in the future (correlations between individual instruments and their level of profitability). But this is not guaranteed. This is to be verified in the following works.
It is clear that one should not expect a positive result from the above test. But then you can look for how to modify the Markowitz method to obtain a more guaranteed income in the future. Here's a topic for another study.