Problem
The classical Markowitz problem is as follows:
max
x
r
T
x
−
λ
x
T
Q
x
\max_x r^Tx-\lambda x^TQx
xmaxrTx−λxTQx
The quadprog
solver addresses this quadratic programming problem. Some cardinality constraints would be added.
x
x
x is the vector of asset allocation fractions, with
0
≤
x
(
i
)
≤
1
0\leq x(i)\leq 1
0≤x(i)≤1 for each
i
i
i. The indicator variables
v
v
v such that
v
(
i
)
=
0
v(i)=0
v(i)=0 when
x
(
i
)
=
0
x(i)=0
x(i)=0, and
v
(
i
)
=
1
v(i)=1
v(i)=1 when
x
(
i
)
>
0
x(i)>0
x(i)>0. To get variables that satisfy this restriction, set the
v
v
v vectror to be a binary variable, and impose the linear constraints.
v
(
i
)
f
min
≤
x
(
i
)
≤
v
(
i
)
f
max
v(i)f_{\min}\leq x(i)\leq v(i)f_{\max}
v(i)fmin≤x(i)≤v(i)fmax
and
m
≤
∑
v
(
i
)
≤
M
m\leq \sum v(i)\leq M
m≤∑v(i)≤M
Objective and Successive linear Approximations
The objective function is
min
x
λ
x
T
Q
x
−
r
T
x
\min_x\lambda x^TQx-r^Tx
xminλxTQx−rTx
However, the intlinprog
MILP solver requires a linear objective function. There is a standard technique to reformulate this problem into one with linear objective and nonlinear constraints. Introduce a slack variable
z
z
z to represent the quadratic term.
min
x
,
z
λ
z
−
r
T
x
{
x
T
Q
x
−
z
≤
0
z
≥
0
\min_{x, z} \lambda z-r^Tx\\ \begin{cases} x^TQx-z\leq 0\\ z\geq 0 \end{cases}
x,zminλz−rTx{xTQx−z≤0z≥0
To solve MILP
approximations iteratively, we could approximate the nonlinear constraint locally near the current point
x
T
Q
x
−
z
=
x
0
T
Q
x
0
+
2
x
0
T
Q
δ
−
z
+
O
(
∣
δ
∣
2
)
x^TQx-z=x_0^TQx_0+2x_0^TQ\delta-z+O(|\delta|^2)
xTQx−z=x0TQx0+2x0TQδ−z+O(∣δ∣2)
Replacing
δ
\delta
δ by
x
−
x
0
x-x_0
x−x0
x
T
Q
x
−
z
=
−
x
0
T
Q
x
0
+
2
x
0
T
Q
x
−
z
+
O
(
∣
x
−
x
0
∣
2
)
x^TQx-z = -x_0^TQx_0+2x_0^TQx-z+O(|x-x_0|^2)
xTQx−z=−x0TQx0+2x0TQx−z+O(∣x−x0∣2)
For each intermediate solution
x
k
x_k
xk you introduce a new linear constraint in
x
x
x and
z
z
z as the linear part of the expression above:
−
x
k
T
Q
x
k
+
2
x
k
T
Q
x
−
z
≤
0
-x_k^TQx_k+2x_k^TQx-z\leq 0
−xkTQxk+2xkTQx−z≤0
Code
%% integer programming
clc;
clear all;
close all;
%%
load port5;
r = mean_return;
Q = Correlation .* (stdDev_return * stdDev_return');
N = length(r);
% indexes
xvars = 1:N;
vvars = N+1:2*N;
zvar = 2*N+1;
lb = zeros(2*N+1, 1);
ub = ones(2*N+1, 1);
ub(zvar) = Inf;
% set the number of assets in the solution to between 100 and 150.
M = 150;
m = 100;
A = zeros(1, 2*N+1);
A(vvars) = 1; % A*x represents the sum of v(i)
A = [A; -A];
b = zeros(2, 1);
b(1) = M;
b(2) = -m;
% set the minimal nonzero fraction of assets to be 0.05 and the maximal to
% be 0.20
fmin = 0.001;
fmax = 0.05;
% inequalities fmin(i)*v(i)<=x(i)<= fmax(i)*v(i)
Atemp = eye(N);
Amax = horzcat(Atemp, -Atemp*fmax, zeros(N, 1));
A = [A; Amax];
b = [b; zeros(N, 1)];
Amin = horzcat(-Atemp, Atemp*fmin, zeros(N, 1));
A = [A; Amin];
b = [b; zeros(N, 1)];
% constraint sum(x_i)=1
Aeq = zeros(1, 2*N+1);
Aeq(xvars) = 1;
beq = 1;
% risk-aversion coefficient lambda
lambda = 100;
% objective function
f = [-r; zeros(N, 1); lambda];
%% solve
ops = optimoptions(@intlinprog, 'Display', 'off');
[xLinInt, fval, exitFlag, output] = intlinprog(f, vvars, A, b, Aeq, beq, lb, ub, ops);
%% stopping condition
thediff = 1e-4;
iter = 1;
assets = xLinInt(xvars);
trueVar = assets'*Q*assets;
zslack = xLinInt(zvar);
cvx code
%% MLIP
n = 10;
k_high = 8;
k_low = 2;
fmax = 0.30;
fmin = 0.05;
mu = abs(randn(n, 1));
Sigma = randn(n, n)/10;
Sigma = Sigma'*Sigma;
cvx_solver mosek
cvx_begin
variable w(n);
variable bi(n) binary;
ret = mu'*w;
risk = quad_form(w, Sigma);
maximize(ret-risk);
subject to
ones(1, n)*w==1;
w>=0;
w<=bi;
% k_high>=nnz(w)>=k_low;
% norm(w, Inf)<=fmax;
fmin*bi<=w;
fmax*bi>=w;
sum(bi)>=k_low;
sum(bi)<=k_high;
cvx_end
w=value(w);