Editor's Pick

Portfolio Optimization: Two Assets

[22]:
import pandas_datareader.data as web

_start = '05/01/2018'
_end = '05/01/2020'

df_vti = web.get_data_yahoo('VTI',_start, _end,interval='w')
df_tlt = web.get_data_yahoo('TLT',_start, _end,interval='w')
_images/moo-portfolio-02_3_0.svg
_images/moo-portfolio-02_4_0.svg
_images/moo-portfolio-02_5_0.svg

This analysis is nothing else what we have done in the previous blog post of this series. Now we should consider what it means holding shares of both assets at the same time. The analysis so far did not include what happens with both assets together at the same time. For instance, the percentage change distribution does not show whether the stocks follow usually the same trends.

One way of measure how strong two assets are coupled is correlation. Let us consider three different cases of correlation in this article.

[ ]:

[26]:
import numpy as np

pct_vti = pct_change(vti)
pct_tlt = pct_change(tlt)

cov = np.cov(pct_vti, pct_tlt)

corr = np.corrcoef(pct_vti, pct_tlt)
corr
[26]:
array([[ 1.        , -0.18396702],
       [-0.18396702,  1.        ]])
_images/moo-portfolio-02_9_0.svg
[28]:
def return_in_perc(v):
    return v[-1] / v[0]


asset_returns = np.array([return_in_perc(pct_vti), return_in_perc(tlt)])
print(asset_returns)

def portfolio_return(w):
    return (w * asset_returns).sum()

def portfolio_risk(w):
    return w.T @ cov @ w
[0.05066095 1.41146315]
[29]:
X = []
F = []

for k in np.linspace(0, 1, 1000):
    w = np.array([k, 1-k])

    est_return = portfolio_return(w)
    est_risk = portfolio_risk(w)

    X.append(w)
    F.append((est_risk, est_return))

X, F = np.array(X), np.array(F)
[30]:
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting

nds = NonDominatedSorting().do(F * [1, -1], only_non_dominated_front=True)
others = [k for k in range(len(F)) if k not in nds]


plt.plot(F[others, 0], F[others, 1], color="black")
plt.plot(F[nds, 0], F[nds, 1], color="red", label="Pareto Frontier")

min_var = np.argmin(F[:, 0])
min_var_x = np.round(X[min_var], 3)
plt.scatter(F[min_var, 0], F[min_var, 1], marker="*", color="blue", s=200,
            label=f"Minimum Variance Portfolio \n{min_var_x[0], min_var_x[1]}")

plt.legend()
plt.show()
_images/moo-portfolio-02_12_0.svg
[31]:
I = np.ones(2)
cov_inv = np.linalg.inv(cov)

min_var = (I @ cov_inv) / (I.T @ cov_inv @ I)

min_var
[31]:
array([0.32489106, 0.67510894])

math proof