minimize
$$x_1^2 + x_2^2 - 2x_1 - x_2$$subject to
$$x_1 \ge 0$$$$x_2 \ge 0$$ $$x_1+x_2=1$$
minimize $$\frac{1}{2} x'Px + q'x$$ subject to $$Gx \le h$$ and $$Ax=b$$
P = np.array(
[
[2., 0.],
[0., 2.]
]
)
q = np.array([-2., -1.]).reshape(2, 1)
G = np.array(
[
[-1., 0.],
[0., -1.]
]
)
h = np.array([0., 0.]).reshape(2, 1)
A = np.array([1., 1.]).reshape(1, 2)
b = np.array([1.]).reshape(1, 1)
from cvxopt import matrix
from cvxopt.solvers import qp
sol = qp(
P=matrix(P),
q=matrix(q),
G=matrix(G),
h=matrix(h),
A=matrix(A),
b=matrix(b)
)
np.array(sol["x"])
pcost dcost gap pres dres 0: -1.1111e+00 -2.2222e+00 1e+00 1e-16 1e+00 1: -1.1231e+00 -1.1680e+00 4e-02 1e-16 4e-02 2: -1.1250e+00 -1.1261e+00 1e-03 2e-16 3e-04 3: -1.1250e+00 -1.1250e+00 1e-05 6e-17 3e-06 4: -1.1250e+00 -1.1250e+00 1e-07 3e-16 3e-08 Optimal solution found.
array([[0.7499999], [0.2500001]])
import yfinance as yf
tickers = ["SPY", "IEF", "GLD"]
prices = yf.download(tickers, start="1970-01-01")["Adj Close"]
prices = prices.resample("M").last()
rets = prices.pct_change().dropna()
rets.head(3)
[*********************100%%**********************] 3 of 3 completed
GLD | IEF | SPY | |
---|---|---|---|
Date | |||
2004-12-31 | -0.029255 | 0.011674 | 0.030121 |
2005-01-31 | -0.036073 | 0.008710 | -0.022421 |
2005-02-28 | 0.031028 | -0.013683 | 0.020904 |
12 * rets.mean()
GLD 0.087096 IEF 0.031683 SPY 0.100341 dtype: float64
np.sqrt(12) * rets.std()
GLD 0.169435 IEF 0.064872 SPY 0.150749 dtype: float64
rets.corr()
GLD | IEF | SPY | |
---|---|---|---|
GLD | 1.000000 | 0.317975 | 0.084318 |
IEF | 0.317975 | 1.000000 | -0.121379 |
SPY | 0.084318 | -0.121379 | 1.000000 |
mu = rets.mean().to_numpy()
Sigma = rets.cov().to_numpy()
minimize
$$\frac{1}{2} w'\Sigma w$$subject to
$$\sum w_i = 1 \quad \Leftrightarrow \quad \iota'w = 1$$where $\iota$ is a column vector of ones.
P = Sigma
q = np.zeros((3, 1))
A = np.ones((1, 3))
b = np.ones((1, 1))
sol = qp(
P=matrix(P),
q=matrix(q),
A=matrix(A),
b=matrix(b)
)
import pandas as pd
gmv = pd.Series(sol["x"], index=rets.columns)
gmv
GLD -0.001301 IEF 0.817025 SPY 0.184276 dtype: float64
w = gmv.to_numpy()
print(f"\nGMV annualized std dev is {np.sqrt(12*w@Sigma@w):.2%}")
print(f"GMV annualized mean is {12*mu@w: .2%}")
print(f"\nIEF annualized std dev is {np.sqrt(12)*rets.IEF.std():.2%}")
print(f"IEF annualized mean is {12*rets.IEF.mean():.2%}")
GMV annualized std dev is 5.67% GMV annualized mean is 4.43% IEF annualized std dev is 6.49% IEF annualized mean is 3.17%
minimize
$$\frac{1}{2} w'\Sigma w$$subject to
$$\mu'w = r$$$$\iota'w = 1$$where $r=$ target expected return and $\iota$ is a column vector of ones.
# example target monthly expected return
r = 0.06/12
P = Sigma
q = np.zeros((3, 1))
A = np.array(
[
mu,
[1., 1., 1.]
]
)
b = np.array([r, 1]).reshape(2, 1)
sol = qp(
P=matrix(P),
q=matrix(q),
A=matrix(A),
b=matrix(b)
)
efficient = pd.Series(sol["x"], index=rets.columns)
efficient
GLD 0.115466 IEF 0.565289 SPY 0.319245 dtype: float64
# example monthly interest rate
rf = 0.03/12
# example target expected return
r = 0.06/12
P = Sigma
q = np.zeros((3, 1))
A = (mu - rf*np.ones(3)).reshape(1, 3)
b = np.array([r-rf]).reshape(1, 1)
sol = qp(
P=matrix(P),
q=matrix(q),
A=matrix(A),
b=matrix(b)
)
efficient_with_cash = pd.Series(sol["x"], index=rets.columns)
efficient_with_cash
GLD 0.176189 IEF -0.027139 SPY 0.284129 dtype: float64
sharpes = np.sqrt(12)*(rets.mean() - rf) / rets.std()
sharpe_efficient = np.sqrt(12)*(r - rf) / np.sqrt(w@Sigma@w)
print(f"SPY = {sharpes.SPY:.2%}")
print(f"IEF = {sharpes.IEF:.2%}")
print(f"GLD = {sharpes.GLD:.2%}")
print(f"Efficient portfolio with cash = {sharpe_efficient:.2%}")
SPY = 46.66% IEF = 2.59% GLD = 33.70% Efficient portfolio with cash = 55.43%
tang = w / np.sum(w)
pd.Series(tang, index=rets.columns).round(3)
GLD 0.407 IEF -0.063 SPY 0.656 dtype: float64