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()