Execute the following steps to find the Efficient Frontier using convex optimization.
- Import the library:
import cvxpy as cp
- Convert the annualized average returns and the covariance matrix to numpy arrays:
avg_returns = avg_returns.values
cov_mat = cov_mat.values
- Set up the optimization problem:
weights = cp.Variable(n_assets)
gamma = cp.Parameter(nonneg=True)
portf_rtn_cvx = avg_returns * weights
portf_vol_cvx = cp.quad_form(weights, cov_mat)
objective_function = cp.Maximize(portf_rtn_cvx - gamma * portf_vol_cvx)
problem = cp.Problem(objective_function,
[cp.sum(weights) == 1, weights >= 0])
- Calculate the Efficient Frontier:
N_POINTS = 25
portf_rtn_cvx_ef = np.zeros(N_POINTS)
portf_vol_cvx_ef = np.zeros(N_POINTS)
weights_ef = []
gamma_range = np.logspace(-3, 3, num=N_POINTS)
for i in range(N_POINTS):
gamma.value = gamma_range[i]
problem.solve()
portf_vol_cvx_ef[i] = cp.sqrt(portf_vol_cvx).value
portf_rtn_cvx_ef[i] = portf_rtn_cvx.value
weights_ef.append(weights.value)
- Plot the allocation for different values of the risk-aversion parameter:
weights_df = pd.DataFrame(weights_ef,
columns=RISKY_ASSETS,
index=np.round(gamma_range, 3))
ax = weights_df.plot(kind='bar', stacked=True)
ax.set(title='Weights allocation per risk-aversion level',
xlabel=r'$gamma$',
ylabel='weight')
ax.legend(bbox_to_anchor=(1,1))
In the following plot, we see the asset allocation for the considered range of risk-aversion parameters:
- Plot the Efficient Frontier, together with the individual assets:
fig, ax = plt.subplots()
ax.plot(portf_vol_cvx_ef, portf_rtn_cvx_ef, 'g-')
for asset_index in range(n_assets):
plt.scatter(x=np.sqrt(cov_mat[asset_index, asset_index]),
y=avg_returns[asset_index],
marker=MARKS[asset_index],
label=RISKY_ASSETS[asset_index],
s=150)
ax.set(title='Efficient Frontier',
xlabel='Volatility',
ylabel='Expected Returns', )
ax.legend()
The following image shows a plot of the Efficient Frontier, generated by solving the convex optimization problem:
In the There's more... section, we also compare the frontier to the one obtained in the previous recipe, Finding the Efficient Frontier using optimization with scipy.