How correlation shapes the frontier¶
Problem¶
Take two stocks from the EURO STOXX 50 data and keep their own expected returns and volatilities, but impose three correlations between them: −1, 0 and +1. Plot the three resulting frontiers.
Method¶
With two assets, the portfolio variance for weights \(x_1 + x_2 = 1\) is
\[
\sigma_p^2 = x_1^2 \sigma_1^2 + x_2^2 \sigma_2^2 + 2\,x_1 x_2\,\rho\,\sigma_1 \sigma_2
\]
The covariance matrix is rebuilt for each \(\rho\) and the frontier is traced with quadprog
over target returns between the two stocks' returns; the risk axis is the volatility
\(\sigma_p\).
Code¶
clear all, clc
%% 1
A = readtable("weekly_EUROSTOXX50_price_time.txt");
times = A{:, 1};
P = A{:, [6 7]};
%% 2
Ret = diff(P) ./ P(1:end-1, :);
[n_valori, n_asset] = size(Ret);
%% 3
mu = mean(Ret);
vol = std(Ret); % sigma = standard deviation = volatility (sigma^2 = variance)
eta_min = min(mu);
eta_max = max(mu);
eta = linspace(eta_min, eta_max, 100);
rho = [-1 0 1];
Risk_vol = NaN(length(rho), 100);
Aeq_ = [mu; ones(1, n_asset)];
lb = zeros(n_asset, 1);
%% 4
for j = 1:length(rho)
Sigma = [vol(1)^2, rho(j)*vol(1)*vol(2);
rho(j)*vol(1)*vol(2), vol(2)^2]; % Sigma = covariance matrix
H = 2 * Sigma;
for k = 1:length(eta)
beq_ = [eta(k); 1];
[x, Risk_var] = quadprog(H, [], [], [], Aeq_, beq_, lb, []); % Risk_var = portfolio variance (sigma^2)
Risk_vol(j, k) = sqrt(Risk_var);
end
end
%% 5
figure(1)
hold on
for k = 1:length(rho)
plot(Risk_vol(k, :), eta, LineWidth=2)
end
xlabel('Risk')
ylabel('Returns')
title('Efficient frontier')
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize
# %% 1
A = np.loadtxt('weekly_EUROSTOXX50_price_time.txt')
times = A[:, 0]
P = A[:, [5, 6]] # MATLAB: A{:, [6 7]} (columns 6 and 7 are 5 and 6 in Python)
# %% 2
Ret = np.diff(P, axis=0) / P[:-1, :]
n_valori, n_asset = Ret.shape
# %% 3
mu = np.mean(Ret, axis=0)
vol = np.std(Ret, axis=0, ddof=1) # MATLAB: std(Ret) (ddof=1 divides by N-1, as MATLAB does)
eta_min = np.min(mu)
eta_max = np.max(mu)
eta = np.linspace(eta_min, eta_max, 100)
rho = [-1, 0, 1]
Risk_vol = np.zeros((len(rho), 100))
lb = [(0, None)] * n_asset
budget = {'type': 'eq', 'fun': lambda x: np.sum(x) - 1}
# %% 4
for j in range(len(rho)):
Sigma = np.array([[vol[0]**2, rho[j] * vol[0] * vol[1]],
[rho[j] * vol[0] * vol[1], vol[1]**2]]) # covariance matrix
H = 2 * Sigma
for k in range(len(eta)):
target = {'type': 'eq', 'fun': lambda x: mu @ x - eta[k]}
res = minimize(lambda x: 0.5 * x @ H @ x, np.array([0.5, 0.5]), method='SLSQP',
bounds=lb, constraints=[budget, target], options={'ftol': 1e-14})
Risk_var = res.fun # portfolio variance
Risk_vol[j, k] = np.sqrt(max(Risk_var, 0)) # volatility (max(...,0) avoids a tiny negative rounding)
# %% 5
plt.figure(1)
for k in range(len(rho)):
plt.plot(Risk_vol[k, :], eta, linewidth=2)
plt.xlabel('Risk')
plt.ylabel('Returns')
plt.title('Efficient frontier')
plt.show()
Results¶
The two stocks have weekly expected returns of 0.116% and 0.394% and volatilities of 3.06% and 4.09%.
| Correlation | Lowest volatility reachable |
|---|---|
| \(\rho = -1\) | 0.03% (practically zero) |
| \(\rho = 0\) | 2.45% |
| \(\rho = +1\) | 3.06% (the less volatile stock alone) |

Takeaways¶
- With \(\rho = +1\) there is no diversification: the frontier is a straight line between the two stocks.
- With \(\rho = -1\) the two stocks hedge each other perfectly, and a riskless combination exists.
- Every real case lies in between: the lower the correlation, the more the curve bends to the left, and the more risk diversification removes.