1

I wanted to calculate the efficient frontier with equality and inequality constraints for given covariance matrix and expected returns. While doing so, the solver calculates weights that unfortunately seem to break the constraints. The overall risks and returns seem to be fine tho.

Primitive64Store tmpCOV = Primitive64Store.FACTORY.rows(new double[][]
{{0.04161599999999999,0.029532687342839998,0.027204649602,0.0077310803303999994,-1.1671155792E-4,-5.6919104880000004E-5,0.011540367260999999,-0.0011583596544,0.0016547567508,0.011887951467599998,-0.0021016779250799997,0.01217717900784,0.0354566417292},
{0.029532687342839998,0.034969,0.02882914496033,0.00673688572095,-1.5753286164E-4,-7.799020878E-5,0.008590460804089999,-0.00120903466574,0.0014109339056999999,0.0104683749489,-0.00296095338671,0.0075381371219,0.03405788034132},
{0.027204649602,0.02882914496033,0.028561000000000003,0.0095923333779,-1.4991438384E-4,-8.476063180000002E-5,0.009686536403049999,-0.0011563569438200001,9.994519899E-4,0.0082659675852,-3.1685615143000007E-4,0.00860292214782,0.03129653885267},
{0.0077310803303999994,0.00673688572095,0.0095923333779,0.011024999999999998,-5.7071784E-6,-1.6705374000000003E-5,0.0062936927109,-2.7820294949999996E-4,8.405088299999999E-5,6.61814874E-4,0.0037795989147,0.0055210432431,0.00810029447955},
{-1.1671155792E-4,-1.5753286164E-4,-1.4991438384E-4,-5.7071784E-6,1.6E-5,5.40282216E-6,-3.1099949760000004E-5,1.671549776E-5,-3.051312E-7,-7.517952359999999E-5,3.06077038E-5,1.3556312E-7,-2.345551502E-4},
{-5.6919104880000004E-5,-7.799020878E-5,-8.476063180000002E-5,-1.6705374000000003E-5,5.40282216E-6,4.0E-6,-1.7530565920000003E-5,3.13729976E-6,-4.841874E-6,-4.2240274199999995E-5,5.2011296E-6,3.44228808E-6,-1.3104418078000002E-4},
{0.011540367260999999,0.008590460804089999,0.009686536403049999,0.0062936927109,-3.1099949760000004E-5,-1.7530565920000003E-5,0.011881,4.4589363133999997E-4,9.499062894E-4,0.0036563291469,0.00251147898767,0.004586606807859999,0.0108024647181},
{-0.0011583596544,-0.00120903466574,-0.0011563569438200001,-2.7820294949999996E-4,1.671549776E-5,3.13729976E-6,4.4589363133999997E-4,0.001444,6.672796998E-4,-9.37965438E-5,0.00112122563298,-0.0011028714312,-0.0013360677697000002},
{0.0016547567508,0.0014109339056999999,9.994519899E-4,8.405088299999999E-5,-3.051312E-7,-4.841874E-6,9.499062894E-4,6.672796998E-4,9.0E-4,0.0013451552549999999,2.136913209E-4,4.8565227479999997E-4,0.0022035050261999998},
{0.011887951467599998,0.0104683749489,0.0082659675852,6.61814874E-4,-7.517952359999999E-5,-4.2240274199999995E-5,0.0036563291469,-9.37965438E-5,0.0013451552549999999,0.0081,-0.0018377539344,0.0026756272619999997,0.0138893569515},
{-0.0021016779250799997,-0.00296095338671,-3.1685615143000007E-4,0.0037795989147,3.06077038E-5,5.2011296E-6,0.00251147898767,0.00112122563298,2.136913209E-4,-0.0018377539344,0.004489000000000001,1.718834348E-4,-0.0025610649546900003},
{0.01217717900784,0.0075381371219,0.00860292214782,0.0055210432431,1.3556312E-7,3.44228808E-6,0.004586606807859999,-0.0011028714312,4.8565227479999997E-4,0.0026756272619999997,1.718834348E-4,0.023716,0.010939810005740002},
{0.0354566417292,0.03405788034132,0.03129653885267,0.00810029447955,-2.345551502E-4,-1.3104418078000002E-4,0.0108024647181,-0.0013360677697000002,0.0022035050261999998,0.0138893569515,-0.0025610649546900003,0.010939810005740002,0.051529000000000005}});


Primitive64Store tmpR =  Primitive64Store.FACTORY.rows(new double[][] {{0.067},{0.045},{0.049},{0.01},{0.02},{1.0E-4},{0.034},{0.001},{0.006},{0.027},{0.005},{0.02},{0.04}});


Primitive64Store tmpEqualityMatrix = Primitive64Store.FACTORY.rows(new double[][]{{1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}});

Primitive64Store tmpEqualityVector = Primitive64Store.FACTORY.rows(new double[][] {{1}});

Primitive64Store tmpInequalityMatrix = Primitive64Store.FACTORY.rows(new double[][] {{1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0},
{-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0},
{1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0},
{-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0}});

Primitive64Store tmpInequalityVector = Primitive64Store.FACTORY.rows({{0.1},{0.2},{0.2},{0.1},{0.2},{0.1},{0.1},{1.0},{1.0},{1.0},{1.0},{0.1},{1.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0}{0.0}{0.0},{0.0},{0.0},{1.0},{0.0}});

//calculating the Portfolio with the maximum return, this always seems to work fine
ConvexSolver tmpSolverMinDiversification = new ConvexSolver.Builder(tmpCOV.multiply(0), tmpR)
                .equalities(tmpEqualityMatrix, tmpEqualityVector).inequalities(tmpInequalityMatrix, tmpInequalityVector).build();
Optimisation.Result tmpResultMinDiversification = tmpSolverMinDiversification.solve();

//calculating the portfolio with minimal risk, this is where the error starts for me
ConvexSolver tmpSolverMaxDiversification = new ConvexSolver.Builder(tmpCOV, tmpR.multiply(0))
                .equalities(tmpEqualityMatrix, tmpEqualityVector).inequalities(tmpInequalityMatrix, tmpInequalityVector).build();
Optimisation.Result tmpResultMaxDiversification = tmpSolverMaxDiversification.solve();

The Results im getting are as follows:

Weights of the portfolio with maximum return

OPTIMAL -0.0455 @ { 0.1, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.4999999999999999 }

Weights of the portfolio with minimal risk

OPTIMAL 1.9065360317211953E-4 @ { -1.3215574127953212E-12, -3.0462153636587428E-12, 4.302357005267538E-12, 0.03119627351114846, 0.19999999989250858, 0.10000000045688691, -9.548402360338467E-13, 0.1370395162618518, 0.48451020053501254, 2.5326118583080594E-12, 0.03544670257704311, 0.011807307116290258, 6.086745301734428E-13 }

   

If i add an additional inequality (1 new row on both) constraint and run the calculation again

Primitive64Store tmpInequalityMatrix = Primitive64Store.FACTORY.rows(new double[][] {{1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0},
{-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0},
{1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0},
{-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0},
{-0.067,-0.045,-0.049,-0.01,-0.02,-1.0E-4,-0.034,-0.001,-0.006,-0.027,-0.005,-0.02,-0.04}});

Primitive64Store tmpInequalityVector = Primitive64Store.FACTORY.rows({{0.1},{0.2},{0.2},{0.1},{0.2},{0.1},{0.1},{1.0},{1.0},{1.0},{1.0},{0.1},{1.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0}{0.0}{0.0},{0.0},{0.0},{1.0},{0.0},{ -0.037349326902676616 }});

ConvexSolver tmpSolverMaxDiversification = new ConvexSolver.Builder(tmpCOV, tmpR.multiply(0))
                .equalities(tmpEqualityMatrix, tmpEqualityVector).inequalities(tmpInequalityMatrix, tmpInequalityVector).build();
Optimisation.Result tmpResultEfficientPortfolio = tmpSolverMaxDiversification.solve();

i get the following result

OPTIMAL 0.004591387674786172 @ { 0.10000000000095524, 0.0, 0.20000000000667748, -3.148187023285784E-12, 0.19999999988537426, -2.6699549475823382E-11, 0.09999999999830238, 3.2126828658623563E-12, 7.645985136245771E-12, 0.5204239501042672, -0.12042395010749231, 1.4422465593413845E-12, -3.1890111868219504E-12 }

Is there a way to avoid those constraint breaking weights? Should i have used an ExpressionBasedModel instead?

1 Answers1

0

If you set debug(ConvexSolver.class) on an instance of Optimisation.Options and pass that to the build method of your ConvexSolver.Builder then you may get some info regarding what goes wrong.

    Optimisation.Options options = new Optimisation.Options();
    
    options.debug(ConvexSolver.class);
    
    convexSolverBuilder.build(options);
apete
  • 1,250
  • 1
  • 10
  • 16
  • If i try it like this `Optimisation.Options.debug(nameofmysolver)` i get the following error `Non-static method 'debug(java.lang.Class extends org.ojalgo.optimisation.Optimisation.Solver>)' cannot be referenced from a static context` would changing the order of the inequality constraints fix the problem? is it even recommended to use the ConvexSolver like this directly? ps: shall i still open an issue at GitHub for this? thanks for your help:)! – HubertHauser123 Jun 24 '20 at 10:36
  • Hi apete, sorry for the delayed response. im getting additional information but im actually not sure what to make of it. Last Incl/Excl for example, never uses all needed constraints, therefore i get constrain breaking (negative) results:/ – HubertHauser123 Jun 29 '20 at 08:49