MATLAB Coder

Portfolio Optimization (Black Litterman Approach)

This example shows how to generate a MEX function and C source code from MATLAB code that performs portfolio optimization using the Black Litterman approach.

Prerequisites

There are no prerequisites for this example.

Create a New Folder and Copy Relevant Files

The following code will create a folder in your current working folder (pwd). The new folder will only contain the files that are relevant for this example. If you do not want to affect the current folder (or if you cannot generate files in this folder), you should change your working folder.

Run Command: Create a New Folder and Copy Relevant Files

coderdemo_setup('coderdemo_portfolio_optimization');

About the 'hlblacklitterman' Function

The hblblacklitterman.mhblblacklitterman.m function reads in financial information regarding a portfolio and performs portfolio optimization using the Black Litterman approach.

type hlblacklitterman
function [er, ps, w, pw, lambda, theta] = hlblacklitterman(delta, weq, sigma, tau, P, Q, Omega)%#codegen
% hlblacklitterman
%   This function performs the Black-Litterman blending of the prior
%   and the views into a new posterior estimate of the returns as
%   described in the paper by He and Litterman.
% Inputs
%   delta  - Risk tolerance from the equilibrium portfolio
%   weq    - Weights of the assets in the equilibrium portfolio
%   sigma  - Prior covariance matrix
%   tau    - Coefficiet of uncertainty in the prior estimate of the mean (pi)
%   P      - Pick matrix for the view(s)
%   Q      - Vector of view returns
%   Omega  - Matrix of variance of the views (diagonal)
% Outputs
%   Er     - Posterior estimate of the mean returns
%   w      - Unconstrained weights computed given the Posterior estimates
%            of the mean and covariance of returns.
%   lambda - A measure of the impact of each view on the posterior estimates.
%   theta  - A measure of the share of the prior and sample information in the
%            posterior precision.

% Reverse optimize and back out the equilibrium returns
% This is formula (12) page 6.
pi = weq * sigma * delta;
% We use tau * sigma many places so just compute it once
ts = tau * sigma;
% Compute posterior estimate of the mean
% This is a simplified version of formula (8) on page 4.
er = pi' + ts * P' * inv(P * ts * P' + Omega) * (Q - P * pi');
% We can also do it the long way to illustrate that d1 + d2 = I
d = inv(inv(ts) + P' * inv(Omega) * P);
d1 = d * inv(ts);
d2 = d * P' * inv(Omega) * P;
er2 = d1 * pi' + d2 * pinv(P) * Q;
% Compute posterior estimate of the uncertainty in the mean
% This is a simplified and combined version of formulas (9) and (15)
ps = ts - ts * P' * inv(P * ts * P' + Omega) * P * ts;
posteriorSigma = sigma + ps;
% Compute the share of the posterior precision from prior and views,
% then for each individual view so we can compare it with lambda
theta=zeros(1,2+size(P,1));
theta(1,1) = (trace(inv(ts) * ps) / size(ts,1));
theta(1,2) = (trace(P'*inv(Omega)*P* ps) / size(ts,1));
for i=1:size(P,1)
    theta(1,2+i) = (trace(P(i,:)'*inv(Omega(i,i))*P(i,:)* ps) / size(ts,1));
end
% Compute posterior weights based solely on changed covariance
w = (er' * inv(delta * posteriorSigma))';
% Compute posterior weights based on uncertainty in mean and covariance
pw = (pi * inv(delta * posteriorSigma))';
% Compute lambda value
% We solve for lambda from formula (17) page 7, rather than formula (18)
% just because it is less to type, and we've already computed w*.
lambda = pinv(P)' * (w'*(1+tau) - weq)';
end

% Black-Litterman example code for MatLab (hlblacklitterman.m)
% Copyright (c) Jay Walters, blacklitterman.org, 2008.
%
% Redistribution and use in source and binary forms, 
% with or without modification, are permitted provided 
% that the following conditions are met:
%
% Redistributions of source code must retain the above 
% copyright notice, this list of conditions and the following 
% disclaimer.
% 
% Redistributions in binary form must reproduce the above 
% copyright notice, this list of conditions and the following 
% disclaimer in the documentation and/or other materials 
% provided with the distribution.
%  
% Neither the name of blacklitterman.org nor the names of its
% contributors may be used to endorse or promote products 
% derived from this software without specific prior written
% permission.
%  
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
% CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
% INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
% DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
% CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
% SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
% SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
% OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
% DAMAGE.
%
% This program uses the examples from the paper "The Intuition 
% Behind Black-Litterman Model  Portfolios", by He and Litterman,
% 1999.  You can find a copy of this  paper at the following url.
%     http:%papers.ssrn.com/sol3/papers.cfm?abstract_id=334304
%
% For more details on the Black-Litterman model you can also view
% "The BlackLitterman Model: A Detailed Exploration", by this author
% at the following url.
%     http:%www.blacklitterman.org/Black-Litterman.pdf
%

The %#codegen directive indicates that the MATLAB code is intended for code generation.

Generate the MEX Function for Testing

Generate a MEX function using the 'codegen' command.

codegen hlblacklitterman -args {0, zeros(1, 7), zeros(7,7), 0, zeros(1, 7), 0, 0}

Before generating C code, you should first test the MEX function in MATLAB to ensure that it is functionally equivalent to the original MATLAB code and that no run-time errors occur. By default, 'codegen' generates a MEX function named 'hlblacklitterman_mex' in the current folder. This allows you to test the MATLAB code and MEX function and compare the results.

Run the MEX Function

Call the generated MEX function

testMex();
View 1
Country        P       mu      w*
Australia	     0	 4.328	 1.524
Canada   	     0	 7.576	 2.095
France   	 -29.5	 9.288	-3.948
Germany  	   100	 11.04	 35.41
Japan    	     0	 4.506	 11.05
UK       	 -70.5	 6.953	-9.462
USA      	     0	 8.069	 58.57
q        	     5
omega/tau	     0.0213
lambda   	     0.317
theta   	     0.0714
pr theta  	     0.929


View 1
Country        P       mu      w*
Australia	     0	 4.328	 1.524
Canada   	     0	 7.576	 2.095
France   	 -29.5	 9.288	-3.948
Germany  	   100	 11.04	 35.41
Japan    	     0	 4.506	 11.05
UK       	 -70.5	 6.953	-9.462
USA      	     0	 8.069	 58.57
q        	     5
omega/tau	     0.0213
lambda   	     0.317
theta   	     0.0714
pr theta  	     0.929

Execution Time - MATLAB function: 0.027287 seconds
Execution Time - MEX function   : 0.00663 seconds

Generate C Code

cfg = coder.config('lib');
codegen -config cfg hlblacklitterman  -args {0, zeros(1, 7), zeros(7,7), 0, zeros(1, 7), 0, 0}

Using 'codegen' with the specified '-config cfg' option produces a standalone C library.

Inspect the Generated Code

By default, the code generated for the library is in the folder codegen/lib/hbblacklitterman/

The files are:

dir codegen/lib/hlblacklitterman/
.                              inv.c                          
..                             inv.h                          
buildInfo.mat                  inv.o                          
codeInfo.mat                   pinv.c                         
hlblacklitterman.a             pinv.h                         
hlblacklitterman.c             pinv.o                         
hlblacklitterman.h             rtGetInf.c                     
hlblacklitterman.o             rtGetInf.h                     
hlblacklitterman_initialize.c  rtGetInf.o                     
hlblacklitterman_initialize.h  rtGetNaN.c                     
hlblacklitterman_initialize.o  rtGetNaN.h                     
hlblacklitterman_ref.rsp       rtGetNaN.o                     
hlblacklitterman_rtw.mk        rt_nonfinite.c                 
hlblacklitterman_terminate.c   rt_nonfinite.h                 
hlblacklitterman_terminate.h   rt_nonfinite.o                 
hlblacklitterman_terminate.o   rtw_proj.tmw                   
hlblacklitterman_types.h       rtwtypes.h                     
interface                      

Inspect the C Code for the 'hlblacklitterman.c' Function

type codegen/lib/hlblacklitterman/hlblacklitterman.c
/*
 * File: hlblacklitterman.c
 *
 * MATLAB Coder version            : 2.7
 * C/C++ source code generated on  : 04-Sep-2014 03:05:26
 */

/* Include Files */
#include "rt_nonfinite.h"
#include "hlblacklitterman.h"
#include "pinv.h"
#include "inv.h"

/* Function Definitions */

/*
 * hlblacklitterman
 *    This function performs the Black-Litterman blending of the prior
 *    and the views into a new posterior estimate of the returns as
 *    described in the paper by He and Litterman.
 *  Inputs
 *    delta  - Risk tolerance from the equilibrium portfolio
 *    weq    - Weights of the assets in the equilibrium portfolio
 *    sigma  - Prior covariance matrix
 *    tau    - Coefficiet of uncertainty in the prior estimate of the mean (pi)
 *    P      - Pick matrix for the view(s)
 *    Q      - Vector of view returns
 *    Omega  - Matrix of variance of the views (diagonal)
 *  Outputs
 *    Er     - Posterior estimate of the mean returns
 *    w      - Unconstrained weights computed given the Posterior estimates
 *             of the mean and covariance of returns.
 *    lambda - A measure of the impact of each view on the posterior estimates.
 *    theta  - A measure of the share of the prior and sample information in the
 *             posterior precision.
 * Arguments    : double delta
 *                const double weq[7]
 *                const double sigma[49]
 *                double tau
 *                const double P[7]
 *                double Q
 *                double Omega
 *                double er[7]
 *                double ps[49]
 *                double w[7]
 *                double pw[7]
 *                double *lambda
 *                double theta[3]
 * Return Type  : void
 */
void hlblacklitterman(double delta, const double weq[7], const double sigma[49],
                      double tau, const double P[7], double Q, double Omega,
                      double er[7], double ps[49], double w[7], double pw[7],
                      double *lambda, double theta[3])
{
  double b_weq[7];
  double pi[7];
  int i0;
  int i1;
  double ts[49];
  double b;
  double b_b;
  double b_ts[7];
  double unusedExpr[7];
  double c_ts[7];
  double d_ts[49];
  int i2;
  double posteriorSigma[49];
  double y[49];
  double x[49];

  /*  Reverse optimize and back out the equilibrium returns */
  /*  This is formula (12) page 6. */
  for (i0 = 0; i0 < 7; i0++) {
    b_weq[i0] = 0.0;
    for (i1 = 0; i1 < 7; i1++) {
      b_weq[i0] += weq[i1] * sigma[i1 + 7 * i0];
    }

    pi[i0] = b_weq[i0] * delta;
  }

  /*  We use tau * sigma many places so just compute it once */
  for (i0 = 0; i0 < 49; i0++) {
    ts[i0] = tau * sigma[i0];
  }

  /*  Compute posterior estimate of the mean */
  /*  This is a simplified version of formula (8) on page 4. */
  b = 0.0;
  for (i0 = 0; i0 < 7; i0++) {
    b_weq[i0] = 0.0;
    for (i1 = 0; i1 < 7; i1++) {
      b_weq[i0] += P[i1] * ts[i1 + 7 * i0];
    }

    b += b_weq[i0] * P[i0];
  }

  b_b = inv(b + Omega);
  b = 0.0;
  for (i0 = 0; i0 < 7; i0++) {
    b += P[i0] * pi[i0];
    b_ts[i0] = 0.0;
    for (i1 = 0; i1 < 7; i1++) {
      b_ts[i0] += ts[i0 + 7 * i1] * P[i1];
    }
  }

  b = Q - b;
  for (i0 = 0; i0 < 7; i0++) {
    er[i0] = pi[i0] + b_ts[i0] * b_b * b;
  }

  /*  We can also do it the long way to illustrate that d1 + d2 = I */
  pinv(P, unusedExpr);

  /*  Compute posterior estimate of the uncertainty in the mean */
  /*  This is a simplified and combined version of formulas (9) and (15) */
  b = 0.0;
  for (i0 = 0; i0 < 7; i0++) {
    b_weq[i0] = 0.0;
    for (i1 = 0; i1 < 7; i1++) {
      b_weq[i0] += P[i1] * ts[i1 + 7 * i0];
    }

    b += b_weq[i0] * P[i0];
    c_ts[i0] = 0.0;
    for (i1 = 0; i1 < 7; i1++) {
      c_ts[i0] += ts[i0 + 7 * i1] * P[i1];
    }
  }

  b_b = inv(b + Omega);
  for (i0 = 0; i0 < 7; i0++) {
    b_ts[i0] = c_ts[i0] * b_b;
    for (i1 = 0; i1 < 7; i1++) {
      d_ts[i0 + 7 * i1] = b_ts[i0] * P[i1];
    }
  }

  for (i0 = 0; i0 < 7; i0++) {
    for (i1 = 0; i1 < 7; i1++) {
      b = 0.0;
      for (i2 = 0; i2 < 7; i2++) {
        b += d_ts[i0 + 7 * i2] * ts[i2 + 7 * i1];
      }

      ps[i0 + 7 * i1] = ts[i0 + 7 * i1] - b;
    }
  }

  for (i0 = 0; i0 < 49; i0++) {
    posteriorSigma[i0] = sigma[i0] + ps[i0];
  }

  /*  Compute the share of the posterior precision from prior and views, */
  /*  then for each individual view so we can compare it with lambda */
  invNxN(ts, y);
  b = 0.0;
  for (i0 = 0; i0 < 7; i0++) {
    for (i1 = 0; i1 < 7; i1++) {
      x[i0 + 7 * i1] = 0.0;
      for (i2 = 0; i2 < 7; i2++) {
        x[i0 + 7 * i1] += y[i0 + 7 * i2] * ps[i2 + 7 * i1];
      }
    }

    b += x[i0 + 7 * i0];
  }

  theta[0] = b / 7.0;
  b_b = inv(Omega);
  b = 0.0;
  for (i0 = 0; i0 < 7; i0++) {
    for (i1 = 0; i1 < 7; i1++) {
      d_ts[i0 + 7 * i1] = P[i0] * b_b * P[i1];
    }

    for (i1 = 0; i1 < 7; i1++) {
      y[i0 + 7 * i1] = 0.0;
      for (i2 = 0; i2 < 7; i2++) {
        y[i0 + 7 * i1] += d_ts[i0 + 7 * i2] * ps[i2 + 7 * i1];
      }
    }

    b += y[i0 + 7 * i0];
  }

  theta[1] = b / 7.0;
  b_b = inv(Omega);
  b = 0.0;
  for (i0 = 0; i0 < 7; i0++) {
    for (i1 = 0; i1 < 7; i1++) {
      d_ts[i0 + 7 * i1] = P[i0] * b_b * P[i1];
    }

    for (i1 = 0; i1 < 7; i1++) {
      y[i0 + 7 * i1] = 0.0;
      for (i2 = 0; i2 < 7; i2++) {
        y[i0 + 7 * i1] += d_ts[i0 + 7 * i2] * ps[i2 + 7 * i1];
      }
    }

    b += y[i0 + 7 * i0];
  }

  theta[2] = b / 7.0;

  /*  Compute posterior weights based solely on changed covariance */
  for (i0 = 0; i0 < 49; i0++) {
    y[i0] = delta * posteriorSigma[i0];
  }

  invNxN(y, x);
  for (i0 = 0; i0 < 7; i0++) {
    b_weq[i0] = 0.0;
    for (i1 = 0; i1 < 7; i1++) {
      b_weq[i0] += er[i1] * x[i1 + 7 * i0];
    }

    w[i0] = b_weq[i0];
  }

  /*  Compute posterior weights based on uncertainty in mean and covariance */
  for (i0 = 0; i0 < 49; i0++) {
    posteriorSigma[i0] *= delta;
  }

  invNxN(posteriorSigma, y);
  for (i0 = 0; i0 < 7; i0++) {
    b_weq[i0] = 0.0;
    for (i1 = 0; i1 < 7; i1++) {
      b_weq[i0] += pi[i1] * y[i1 + 7 * i0];
    }

    pw[i0] = b_weq[i0];
  }

  /*  Compute lambda value */
  /*  We solve for lambda from formula (17) page 7, rather than formula (18) */
  /*  just because it is less to type, and we've already computed w*. */
  pinv(P, b_weq);
  *lambda = 0.0;
  for (i0 = 0; i0 < 7; i0++) {
    *lambda += b_weq[i0] * (w[i0] * (1.0 + tau) - weq[i0]);
  }

  /*  Black-Litterman example code for MatLab (hlblacklitterman.m) */
  /*  Copyright (c) Jay Walters, blacklitterman.org, 2008. */
  /*  */
  /*  Redistribution and use in source and binary forms,  */
  /*  with or without modification, are permitted provided  */
  /*  that the following conditions are met: */
  /*  */
  /*  Redistributions of source code must retain the above  */
  /*  copyright notice, this list of conditions and the following  */
  /*  disclaimer. */
  /*   */
  /*  Redistributions in binary form must reproduce the above  */
  /*  copyright notice, this list of conditions and the following  */
  /*  disclaimer in the documentation and/or other materials  */
  /*  provided with the distribution. */
  /*    */
  /*  Neither the name of blacklitterman.org nor the names of its */
  /*  contributors may be used to endorse or promote products  */
  /*  derived from this software without specific prior written */
  /*  permission. */
  /*    */
  /*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND  */
  /*  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,  */
  /*  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF  */
  /*  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  */
  /*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR  */
  /*  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  */
  /*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,  */
  /*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR  */
  /*  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  */
  /*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  */
  /*  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  */
  /*  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  */
  /*  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH  */
  /*  DAMAGE. */
  /*  */
  /*  This program uses the examples from the paper "The Intuition  */
  /*  Behind Black-Litterman Model  Portfolios", by He and Litterman, */
  /*  1999.  You can find a copy of this  paper at the following url. */
  /*      http:%papers.ssrn.com/sol3/papers.cfm?abstract_id=334304 */
  /*  */
  /*  For more details on the Black-Litterman model you can also view */
  /*  "The BlackLitterman Model: A Detailed Exploration", by this author */
  /*  at the following url. */
  /*      http:%www.blacklitterman.org/Black-Litterman.pdf */
  /*  */
}

/*
 * File trailer for hlblacklitterman.c
 *
 * [EOF]
 */

Cleanup

Remove files and return to original folder

Run Command: Cleanup

cleanup