qmeanvag.gms : Portfolio Modeling with Parallel Solutions

Description

This model is a parallel implementation of the sequential QMEANVAR
model. Minimum and maximum trade constraints are added to the standard
mean-variance model. If it is not profitable to trade within these
ranges, no trade should take place. A turnover constraint is added to
improve stability of the solution to small changes in data. The
resulting model is now expressed as a MIQCP and a frontier is
computed.

Dahl, H, Meeraus, A, and Zenios, S A, Some Financial Optimization
Models: Risk Management. In Zenios, S A, Ed, Financial Optimization.
Cambridge University Press, New York, NY, 1993.


Small Model of Type : MIQCP


Category : GAMS Model library


Main file : qmeanvag.gms

$title Portfolio Modeling with Parallel Solutions (QMEANVAG,SEQ=327)

$onText
This model is a parallel implementation of the sequential QMEANVAR
model. Minimum and maximum trade constraints are added to the standard
mean-variance model. If it is not profitable to trade within these
ranges, no trade should take place. A turnover constraint is added to
improve stability of the solution to small changes in data. The
resulting model is now expressed as a MIQCP and a frontier is
computed.

Dahl, H, Meeraus, A, and Zenios, S A, Some Financial Optimization
Models: Risk Management. In Zenios, S A, Ed, Financial Optimization.
Cambridge University Press, New York, NY, 1993.


Two double dash option are used to conveniently change the number of
points and set a time limit.

Keywords: mixed integer quadratic constraint programming, portfolio optimization,
          finance, risk management
$offText

$eolCom //
$ife %system.gamsversion%<231 $abort Version too old! Need 231 or higher

$setddlist points maxtime
$if not set points  $set points    4
$if not set maxtime $set maxtime 120
$if not errorfree $abort wrong double dash parameters: --points=n --maxtime=secs

Scalar maxtime / %maxtime% /;

Set i 'securities' / cn, fr, gr, jp, sw, uk, us /;

Alias (i,j);

Parameter mu(i) 'expected return of security'
                / cn 0.1287, fr 0.1096, gr 0.0501, jp 0.1524, sw 0.0763, uk 0.1854, us 0.0620 /;

Table q(i,j) 'covariance matrix'
           cn     fr     gr     jp     sw     uk     us
   cn   42.18
   fr   20.18  70.89
   gr   10.88  21.58  25.51
   jp    5.30  15.41   9.60  22.33
   sw   12.32  23.24  22.63  10.32  30.01
   uk   23.84  23.80  13.22  10.46  16.36  42.23
   us   17.41  12.62   4.70   1.00   7.20   9.90  16.42;

* we will continue to use only the lower triangle of the q-matrix
* and adjust the off diagonal entries to give the correct results.

q(i,j) = 2*q(j,i);
q(i,i) = q(i,i)/2;

Scalar tau 'bounding parameter on turnover of current holdings' / 0.3 /;

Set pd 'portfolio data labels'
       / old  'current holdings fraction of the portfolio'
         umin 'minimum increase of holdings fraction of security i'
         umax 'maximum increase of holdings fraction of security i'
         lmin 'minimum decrease of holdings fraction of security i'
         lmax 'maximum decrease of holdings fraction of security i' /;

Table  bdata(i,pd) 'portfolio data and trading restrictions'
*              - increase -   - decrease -
        old    umin    umax   lmin    lmax
   cn   0.2    0.03    0.11   0.02    0.30
   fr   0.2    0.04    0.10   0.02    0.15
   gr   0.0    0.04    0.07   0.04    0.10
   jp   0.0    0.03    0.11   0.04    0.10
   sw   0.2    0.03    0.20   0.04    0.10
   uk   0.2    0.03    0.10   0.04    0.15
   us   0.2    0.03    0.10   0.04    0.30;

bdata(i,'lmax') = min(bdata(i,'old'),bdata(i,'lmax')); // tighten bounds

abort$(abs(sum(i, bdata(i,'old')) - 1) >= 1e5) 'error in bdata', bdata;

Variable
   var   'variance of portfolio'
   ret   'return of portfolio'
   x(i)  'fraction of portfolio of current holdings of i'
   xi(i) 'fraction of portfolio increase'
   xd(i) 'fraction of portfolio decrease'
   y(i)  'binary switch for increasing current holdings of i'
   z(i)  'binary switch for decreasing current holdings of i';

Binary   Variable y, z;
Positive Variable x, xi, xd;

Equation
   budget    'budget constraint'
   turnover  'restrict maximum turnover of portfolio'
   maxinc(i) 'bound of maximum lot increase of fraction of i'
   mininc(i) 'bound of minimum lot increase of fraction of i'
   maxdec(i) 'bound of maximum lot decrease of fraction of i'
   mindec(i) 'bound of minimum lot decrease of fraction of i'
   binsum(i) 'restrict use of binary variables'
   xdef(i)   'final portfolio definition'
   retdef    'return definition'
   vardef    'variance definition';

budget..    sum(i, x(i)) =e= 1;

xdef(i)..   x(i)  =e= bdata(i,'old') - xd(i) + xi(i);

maxinc(i).. xi(i) =l= bdata(i,'umax')*y(i);

mininc(i).. xi(i) =g= bdata(i,'umin')*y(i);

maxdec(i).. xd(i) =l= bdata(i,'lmax')*z(i);

mindec(i).. xd(i) =g= bdata(i,'lmin')*z(i);

binsum(i).. y(i) + z(i) =l= 1;

turnover..  sum(i, xi(i)) =l= tau;

retdef..    ret =e= sum(i, mu(i)*x(i));

vardef..    var =e= sum((i,j), x(i)*q(i,j)*x(j));

Model
   maxret / budget, xdef, turnover, maxinc, mininc, maxdec, mindec, binsum, retdef         /
   minvar / budget, xdef, turnover, maxinc, mininc, maxdec, mindec, binsum, retdef, vardef /;

Set
   p     'efficient frontier points' / minvar, p1*p%points%, maxret /
   pp(p) 'efficient frontier points' /         p1*p%points%         /;

Scalar
   rmin 'minimum variance'
   rmax 'maximum variance';

Parameter
   Report(p,i,*) 'portfolio details at different points'
   xres(*,p)     'summary report at different points';

option limCol = 0, limRow = 0, solPrint = off, optCr = 0, solveLink = %solveLink.CallModule%, reslim = %maxtime%;

loop(p('maxret'),
   solve maxret max ret using miqcp;
   rmax              = ret.l;
   xres(i,p)         = x.l(i);
   report(p,i,'inc') = xi.l(i);
   report(p,i,'dec') = xd.l(i);
);

loop(p('minvar'),
   solve minvar min var using miqcp;
   rmin              = ret.l;
   xres(i,p)         = x.l(i);
   report(p,i,'inc') = xi.l(i);
   report(p,i,'dec') = xd.l(i);
);

Parameter h(p) 'solution handle';

minvar.solPrint  = %solPrint.quiet%;
minvar.solveLink = %solveLink.asyncGrid%;

abort$(timeelapsed > maxtime - 5) "too much time spend for initial solves";
minvar.resLim = maxtime - timeelapsed - 4;

loop(pp,
   ret.fx = rmin + (rmax - rmin)/(card(pp) + 1)*ord(pp);
   solve minvar min var using miqcp;
   h(pp) = minvar.handle;
);

repeat
   loop(pp$handlecollect(h(pp)),
      if(minvar.solveStat = %solveStat.normalCompletion%,
         xres(i,pp)         = x.l(i);
         report(pp,i,'inc') = xi.l(i);
         report(pp,i,'dec') = xd.l(i);
      );
      display$handledelete(h(pp)) 'trouble deleting handles';
      h(pp) = 0;
   );
   display$sleep(card(h)*0.1) 'sleep some time';
until card(h) = 0 or timeelapsed > maxtime;

xres(i,pp)$h(pp) = na;

xres('mean',p) = sum(i, mu(i)*xres(i,p));
xres('var' ,p) = sum((i,j), xres(i,p)*q(i,j)*xres(j,p));

display$(card(p) < 10) xres, report;

* prepare gdx data for IDE charting
execute_unload "qmeanvarg.gdx";