Simple portfolio optimization#
We consider a portfolio optimization problem as described on the Convex Optimization Applications slides.
(a)#
Find minimum-risk portfolios with the same expected return as the uniform portfolio (\(w = (1/n)1\)), with risk measured by portfolio return variance, and the following portfolio constraints (in addition to \(1^Tw = 1\)):
No (additional) constraints.
Long-only: \(w \geq 0\).
Limit on total short position: \(1^T(w_-) \leq 0.5\), where \((w_-)_i = \max\{−w_i, 0\}\).
Compare the optimal risk in these portfolios with each other and the uniform portfolio.
# Construct problem data.
import numpy as np
np.random.seed(1)
n = 20
mu = np.ones(n) * 0.03 + np.random.rand(n) * 0.12
mu[0] = 0
S = np.random.randn(n, n)
S = S.T @ S
Sigma = S / max(np.abs(np.diag(S))) * 0.2
Sigma[:, 0] = np.zeros(n)
Sigma[0, :] = np.zeros(n)
w_unif = np.ones(n) / n
import cvxpy as cp
w = cp.Variable(n)
# Uniform portfolio
print(f"Risk for uniform: {np.sqrt(w_unif.T @ Sigma @ w_unif):.1%}")
# No additional constraints
# TODO: your code here. You define risk.
print(f"Risk for unconstrained: {np.sqrt(risk.value):.1%}")
# Long only
# TODO: your code here. You define risk.
print(f"Risk for long only: {np.sqrt(risk.value):.1%}")
# Limit on total short position
# TODO: your code here. You define risk.
print(f"Risk for limit on short: {np.sqrt(risk.value):.1%}")
(b)#
Plot the optimal risk-return trade-off curves for the long-only portfolio, and for total short position limited to 0.5, in the same figure.
Comment on the relationship between the two trade-off curves.
import matplotlib.pyplot as plt
w = cp.Variable(n)
gamma = cp.Parameter(nonneg=True)
N = 128
# Long only
# TODO: your code here: define prob, expec_return, risk
gamma_vals = np.logspace(-1, 5, num=N)
return_vec1 = np.zeros(N)
risk_vec1 = np.zeros(N)
for i in range(N):
gamma.value = gamma_vals[i]
# you define prob, expec_return, and risk.
prob.solve()
return_vec1[i] = expec_return.value
risk_vec1[i] = risk.value
plt.figure()
plt.plot(np.sqrt(risk_vec1) * 100, return_vec1 * 100, label="Long only")
# Limit on short
# TODO: your code here: define prob, expec_return, risk
return_vec2 = np.zeros(N)
risk_vec2 = np.zeros(N)
for i in range(N):
gamma.value = gamma_vals[i]
# you define prob, expec_return, and risk.
prob.solve()
return_vec2[i] = expec_return.value
risk_vec2[i] = risk.value
plt.plot(np.sqrt(risk_vec2) * 100, return_vec2 * 100, label="Limit on short")
plt.legend()
plt.xlabel("Risk in %")
plt.ylabel("Return in %")
plt.show()