Investment portfolio optimization by the Markowitz method





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.



All Articles