Skip to content

Mean-MaxLoss efficient frontier

Course exercise 136 · Part II

Problem

On the same EURO STOXX 50 data, measure risk as the worst weekly return observed in the sample, and build the frontier that minimises that worst loss for each target return.

Method

Maximising the worst portfolio return is linear with one auxiliary variable \(d\), a lower bound on the portfolio return in every scenario:

\[ \max_{x,\,d}\ d \quad \text{s.t.} \quad d \le r_t^\top x \ \ \forall t,\quad \mu^\top x = \eta,\quad \mathbf{1}^\top x = 1,\quad x \ge 0 \]

At the optimum \(d\) is the worst return, and \(-d\) is the maximum loss. linprog minimises, so the objective is \(-d\).

Code

clear all, clc

%% 1

A = readtable('weekly_EUROSTOXX50_price_time.txt');
times = A{:, 1};
P = A{:, 2:end};

%% 2

RR = diff(P) ./ P(1:end-1, :);
[T, n] = size(RR);

%% 3

mu = mean(RR);

%% 4

f  = [zeros(1, n), -1];        % minimise -d  ->  maximise d (= -MaxLoss)
A_ = [-RR, ones(T, 1)];        % d - sum(r_t x) <= 0  for every t
b  = zeros(T, 1);
lb = [zeros(n, 1); -inf];      % x >= 0 (no short selling); d free
ub = [];

% minimum risk (budget only)
Aeq = [ones(1, n), 0];
beq = 1;

[X0, fo] = linprog(f, A_, b, Aeq, beq, lb, ub);
x_min   = X0(1:n);
eta_min = mu * x_min;
eta_max = max(mu);

%% 5

N   = 100;
eta = linspace(eta_min, eta_max, N);

%% 6

Risk_MaxLoss = NaN(1, N);

Aeq_ = [mu,         0;          % return constraint
        ones(1, n), 0];         % budget constraint

for k = 1:N
    beq_ = [eta(k); 1];
    [X_star, fo_star] = linprog(f, A_, b, Aeq_, beq_, lb, []);
    Risk_MaxLoss(k) = fo_star;
end

%% 7

figure(1)
plot(Risk_MaxLoss, eta, LineWidth=2)
xlabel('Risk-MaxLoss')
ylabel('Expected return')
title('Mean-MaxLoss Efficient Frontier')
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import linprog

# %% 1
A = np.loadtxt('weekly_EUROSTOXX50_price_time.txt')
times = A[:, 0]
P = A[:, 1:]

# %% 2
RR = np.diff(P, axis=0) / P[:-1, :]
T, n = RR.shape

# %% 3
mu = np.mean(RR, axis=0)

# %% 4
f = np.concatenate([np.zeros(n), [-1]])          # minimise -d  ->  maximise d (= -MaxLoss)
A_ = np.hstack([-RR, np.ones((T, 1))])           # d - sum(r_t x) <= 0  for every t
b = np.zeros(T)
lb = [(0, None)] * n + [(None, None)]            # x >= 0 (no short selling); d free

# minimum risk (budget only)
Aeq = np.concatenate([np.ones(n), [0]]).reshape(1, -1)
beq = [1]

res = linprog(f, A_ub=A_, b_ub=b, A_eq=Aeq, b_eq=beq, bounds=lb)
X0 = res.x
fo = res.fun
x_min = X0[:n]
eta_min = mu @ x_min
eta_max = np.max(mu)

# %% 5
N = 100
eta = np.linspace(eta_min, eta_max, N)

# %% 6
Risk_MaxLoss = np.zeros(N)

Aeq_ = np.vstack([np.concatenate([mu, [0]]),           # return constraint
                  np.concatenate([np.ones(n), [0]])])  # budget constraint

for k in range(N):
    beq_ = [eta[k], 1]
    res = linprog(f, A_ub=A_, b_ub=b, A_eq=Aeq_, b_eq=beq_, bounds=lb)
    Risk_MaxLoss[k] = res.fun

print("fo =", fo, " eta_min =", eta_min, " MaxLoss at the top =", Risk_MaxLoss[-1])

# %% 7
plt.figure(1)
plt.plot(Risk_MaxLoss, eta, linewidth=2)
plt.xlabel('Risk-MaxLoss')
plt.ylabel('Expected return')
plt.title('Mean-MaxLoss Efficient Frontier')
plt.show()

Results

The minimum-MaxLoss portfolio loses at most 2.85% in its worst week, with an expected return of 0.297%, holding 10 stocks. At the top of the frontier the worst week costs 6.33%.

Mean-MaxLoss efficient frontier

Takeaways

  • The worst case of the sample becomes an explicit constraint: only one variable is added, and one constraint per week.
  • It is the most conservative measure of the set: it looks at a single observation, the worst, and ignores how often bad weeks happen.
  • That same feature makes it unstable: one extreme week in or out of the sample can move the whole frontier.