Initializing help system before first use

QP

To adapt the model developed in Chapter 2 to the new way of looking at the problem, we need to make the following changes:

  • New objective function: mean variance instead of total return.
  • The risk-related constraint disappears.
  • Addition of a new constraint: target yield.

The new objective function is the mean variance of the portfolio, namely:

s,t ∈ SHARES VARst·fracs ·fract

where VARst is the variance/covariance matrix of all shares. This is a quadratic objective function (an objective function becomes quadratic either when a variable is squared, e.g., frac12, or when two variables are multiplied together, e.g., frac1 · frac2).

The target yield constraint can be written as follows:

s ∈ SHARES RETs·fracs ≥ TARGET

The limit on the North-American shares as well as the requirement to spend all the money, and the upper bounds on the fraction invested into every share are retained. We therefore obtain the following complete mathematical model formulation:

minimize s,t ∈ SHARES VARst·fracs ·fract
s ∈ NA fracs ≥ 0.5
s ∈ SHARES fracs = 1
s ∈ SHARES RETs·fracs ≥ TARGET
∀ s ∈ SHARES: 0 ≤ fracs ≤ 0.3

Implementation with BCL

The estimated returns and the variance/covariance matrix are given in the data file foliocppqp.dat:

! trs  haw  thr  tel  brw  hgw  car  bnk  sof  elc
0.1    0    0    0    0    0    0    0    0    0 ! treasury
  0   19   -2    4    1    1    1  0.5   10    5 ! hardware
  0   -2   28    1    2    1    1    0   -2   -1 ! theater
  0    4    1   22    0    1    2    0    3    4 ! telecom
  0    1    2    0    4 -1.5   -2   -1    1    1 ! brewery
  0    1    1    1 -1.5  3.5    2  0.5    1  1.5 ! highways
  0    1    1    2   -2    2    5  0.5    1  2.5 ! cars
  0  0.5    0    0   -1  0.5  0.5    1  0.5  0.5 ! bank
  0   10   -2    3    1    1    1  0.5   25    8 ! software
  0    5   -1    4    1  1.5  2.5  0.5    8   16 ! electronics

We may read this datafile with the function XPRBreadarrlinecb: all comments preceded by ! and also empty lines are skipped. We read an entire line at once indicating the format of an entry (`g') and the separator (any number of spaces or tabulations).

For the definition of the objective function we now use a quadratic expression (equally represented by the class XPRBexpr). Since we now wish to minimize the problem, we use the default optimization sense setting and optimization as a continuous problem is again started with the method lpOptimize (with empty string argument indicating the default algorithm).

#include <iostream>
#include "xprb_cpp.h"

using namespace std;
using namespace ::dashoptimization;

#define DATAFILE "foliocppqp.dat"

#define TARGET 9                     // Target yield
#define MAXNUM 4                     // Max. number of different assets

#define NSHARES 10                   // Number of shares
#define NNA 4                        // Number of North-American shares

double RET[] = {5,17,26,12,8,9,7,6,31,21};  // Estimated return in investment
int NA[] = {0,1,2,3};                // Shares issued in N.-America
double VAR[NSHARES][NSHARES];        // Variance/covariance matrix of
                                     // estimated returns

int main(int argc, char **argv)
{
 int s,t;
 XPRBprob p("FolioQP");              // Initialize a new problem in BCL
 XPRBexpr Na,Return,Cap,Num,Variance;
 XPRBvar frac[NSHARES];              // Fraction of capital used per share
 FILE *datafile;

// Read `VAR' data from file
 datafile=fopen(DATAFILE,"r");
 for(s=0;s<NSHARES;s++)
  XPRBreadarrlinecb(XPRB_FGETS, datafile, 200, "g ", VAR[s], NSHARES);
 fclose(datafile);

// Create the decision variables
 for(s=0;s<NSHARES;s++)
  frac[s] = p.newVar(XPRBnewname("frac(%d)",s+1), XPRB_PL, 0, 0.3);

// Objective: mean variance
 for(s=0;s<NSHARES;s++)
  for(t=0;t<NSHARES;t++) Variance += VAR[s][t]*frac[s]*frac[t];
 p.setObj(Variance);                 // Set the objective function

// Minimum amount of North-American values
 for(s=0;s<NNA;s++) Na += frac[NA[s]];
 p.newCtr(Na >= 0.5);

// Spend all the capital
 for(s=0;s<NSHARES;s++) Cap += frac[s];
 p.newCtr(Cap == 1);

// Target yield
 for(s=0;s<NSHARES;s++) Return += RET[s]*frac[s];
 p.newCtr(Return >= TARGET);

// Solve the problem
 p.lpOptimize("");

// Solution printing
 cout << "With a target of " << TARGET << " minimum variance is " <<
         p.getObjVal() << endl;
 for(s=0;s<NSHARES;s++)
  cout << s << ": " << frac[s].getSol()*100 << "%" << endl;

 return 0;
}

This program produces the following solution output with a dual-core processor (notice that the default algorithm for solving QP problems is Newton-Barrier, not the Simplex as in all previous examples):

Reading Problem FolioQP
Problem Statistics
           3 (      0 spare) rows
          10 (      0 spare) structural columns
          24 (      0 spare) non-zero elements
          76 quadratic elements
Global Statistics
           0 entities        0 sets        0 set members
Minimizing QP FolioQP
Original problem has:
         3 rows           10 cols           24 elements
        76 qobjelem
Presolved problem has:
         3 rows           10 cols           24 elements
        76 qobjelem
Barrier cache sizes : L1=32K L2=8192K
Using AVX support
Cores per CPU (CORESPERCPU): 8
Barrier starts, using up to 8 threads, 4 cores
Matrix ordering - Dense cols.:      9   NZ(L):        92   Flops:          584

  Its   P.inf      D.inf      U.inf      Primal obj.     Dual obj.      Compl.
   0  1.90e+001  1.85e+002  3.70e+000  8.7840000e+002 -1.1784000e+003  4.5e+003
   1  1.69e-001  1.58e+000  3.29e-002  7.1810240e+000 -2.7042733e+002  3.1e+002
   2  3.31e-003  1.48e-002  6.45e-004  5.1672666e+000 -1.2127681e+001  1.7e+001
   3  6.12e-007  2.66e-015  2.78e-017  1.5558934e+000 -4.8803143e+000  6.4e+000
   4  9.71e-017  1.39e-015  2.78e-017  7.2498306e-001  1.4062618e-001  5.8e-001
   5  3.64e-017  1.01e-015  5.55e-017  5.6634270e-001  5.2690100e-001  3.9e-002
   6  3.97e-017  7.15e-016  5.55e-017  5.5894833e-001  5.5591229e-001  3.0e-003
   7  1.22e-016  1.33e-015  5.55e-017  5.5760205e-001  5.5726378e-001  3.4e-004
   8  9.18e-017  1.68e-015  5.55e-017  5.5741308e-001  5.5738503e-001  2.8e-005
   9  2.36e-016  3.29e-016  5.55e-017  5.5739403e-001  5.5739328e-001  7.5e-007
Barrier method finished in 0 seconds
Uncrunching matrix
Optimal solution found
Barrier solved problem
  9 barrier iterations in 0s

Final objective                         : 5.573940299651456e-01
  Max primal violation      (abs / rel) : 7.347e-17 / 7.347e-17
  Max dual violation        (abs / rel) :       0.0 /       0.0
  Max complementarity viol. (abs / rel) : 6.075e-07 / 1.012e-07
All values within tolerances
With a target of 9 minimum variance is 0.557394
0: 30%
1: 7.15401%
2: 7.38237%
3: 5.46362%
4: 12.6561%
5: 5.91283%
6: 0.333491%
7: 29.9979%
8: 1.0997%
9: 6.97039e-06%

© 2001-2020 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.