Black Litterman 法を使用してポートフォリオを最適化するコードの生成
この例では、Black Litterman 法を使用して、ポートフォリオの最適化を実行する MATLAB® コードから MEX 関数と C のソース コードを生成する方法を説明します。
前提条件
この例には前提条件はありません。
関数 hlblacklitterman について
関数 hlblacklitterman.m は、ポートフォリオに関する財務情報を読み込み、Black Litterman 法を使用してポートフォリオの最適化を実行します。
type hlblacklittermanfunction [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
%
%#codegen 命令は、当該の MATLAB コードがコード生成用であることを示します。
検定に使用する MEX 関数の生成
codegen コマンドを使用して MEX 関数を生成します。
codegen hlblacklitterman -args {0, zeros(1, 7), zeros(7,7), 0, zeros(1, 7), 0, 0}
Code generation successful.
C コードを生成する前に、MATLAB で MEX 関数をテストして、その関数が元の MATLAB コードと機能的に等価であることと実行時のエラーが発生しないことを確認しなければなりません。既定で、codegen は、現在のフォルダーに hlblacklitterman_mex という名前の MEX 関数を生成します。これにより、MATLAB コードと MEX 関数をテストして結果を比較することができます。
MEX 関数の実行
生成された MEX 関数の呼び出し
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.07022 seconds Execution Time - MEX function : 0.028119 seconds
C コードの生成
cfg = coder.config('lib'); codegen -config cfg hlblacklitterman -args {0, zeros(1, 7), zeros(7,7), 0, zeros(1, 7), 0, 0}
Code generation successful.
-config cfg オプションを指定して codegen を使用すると、スタンドアロン C ライブラリが生成されます。
生成されたコードの確認
既定では、ライブラリ用に生成されたコードは codegen/lib/hbblacklitterman/ フォルダーにあります。
ファイルは、以下のとおりです。
dir codegen/lib/hlblacklitterman/. .. _clang-format buildInfo.mat codeInfo.mat codedescriptor.dmr compileInfo.mat examples hlblacklitterman.a hlblacklitterman.c hlblacklitterman.h hlblacklitterman.o hlblacklitterman_data.c hlblacklitterman_data.h hlblacklitterman_data.o hlblacklitterman_initialize.c hlblacklitterman_initialize.h hlblacklitterman_initialize.o hlblacklitterman_rtw.mk hlblacklitterman_terminate.c hlblacklitterman_terminate.h hlblacklitterman_terminate.o hlblacklitterman_types.h interface inv.c inv.h inv.o pinv.c pinv.h pinv.o rtGetInf.c rtGetInf.h rtGetInf.o rtGetNaN.c rtGetNaN.h rtGetNaN.o rt_nonfinite.c rt_nonfinite.h rt_nonfinite.o rtw_proj.tmw rtwtypes.h
関数 hlblacklitterman.c の C コードの検査
type codegen/lib/hlblacklitterman/hlblacklitterman.c/*
* File: hlblacklitterman.c
*
* MATLAB Coder version : 25.1
* C/C++ source code generated on : 13-Jul-2025 16:33:00
*/
/* Include Files */
#include "hlblacklitterman.h"
#include "hlblacklitterman_data.h"
#include "hlblacklitterman_initialize.h"
#include "inv.h"
#include "pinv.h"
#include "rt_nonfinite.h"
#include <emmintrin.h>
#include <string.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])
{
__m128d r;
__m128d r1;
__m128d r2;
double b_y[49];
double ts[49];
double b_ts[7];
double c_P[7];
double pi[7];
double b_P;
double d_P;
double y;
int i;
int i1;
int i2;
int k;
int ps_tmp;
int ts_tmp;
if (!isInitialized_hlblacklitterman) {
hlblacklitterman_initialize();
}
/* Reverse optimize and back out the equilibrium returns */
/* This is formula (12) page 6. */
memset(&pi[0], 0, 7U * sizeof(double));
for (k = 0; k < 7; k++) {
b_P = pi[k];
for (i = 0; i < 7; i++) {
b_P += weq[i] * sigma[i + 7 * k];
}
pi[k] = b_P * delta;
}
/* We use tau * sigma many places so just compute it once */
for (k = 0; k <= 46; k += 2) {
_mm_storeu_pd(&ts[k],
_mm_mul_pd(_mm_set1_pd(tau), _mm_loadu_pd(&sigma[k])));
}
ts[48] = tau * sigma[48];
/* Compute posterior estimate of the mean */
/* This is a simplified version of formula (8) on page 4. */
memset(&c_P[0], 0, 7U * sizeof(double));
b_P = 0.0;
memset(&b_ts[0], 0, 7U * sizeof(double));
for (k = 0; k < 7; k++) {
d_P = c_P[k];
for (i = 0; i < 7; i++) {
d_P += P[i] * ts[i + 7 * k];
}
c_P[k] = d_P;
y = P[k];
b_P += d_P * y;
r = _mm_loadu_pd(&ts[7 * k]);
r1 = _mm_loadu_pd(&b_ts[0]);
r2 = _mm_set1_pd(y);
_mm_storeu_pd(&b_ts[0], _mm_add_pd(r1, _mm_mul_pd(r, r2)));
r = _mm_loadu_pd(&ts[7 * k + 2]);
r1 = _mm_loadu_pd(&b_ts[2]);
_mm_storeu_pd(&b_ts[2], _mm_add_pd(r1, _mm_mul_pd(r, r2)));
r = _mm_loadu_pd(&ts[7 * k + 4]);
r1 = _mm_loadu_pd(&b_ts[4]);
_mm_storeu_pd(&b_ts[4], _mm_add_pd(r1, _mm_mul_pd(r, r2)));
b_ts[6] += ts[7 * k + 6] * y;
}
b_P = 1.0 / (b_P + Omega);
d_P = 0.0;
for (k = 0; k < 7; k++) {
b_ts[k] *= b_P;
d_P += P[k] * pi[k];
}
d_P = Q - d_P;
/* We can also do it the long way to illustrate that d1 + d2 = I */
y = 1.0 / Omega;
/* Compute posterior estimate of the uncertainty in the mean */
/* This is a simplified and combined version of formulas (9) and (15) */
for (k = 0; k < 7; k++) {
er[k] = pi[k] + b_ts[k] * d_P;
r = _mm_loadu_pd(&b_ts[0]);
b_P = P[k];
r1 = _mm_set1_pd(b_P);
_mm_storeu_pd(&b_y[7 * k], _mm_mul_pd(r, r1));
r = _mm_loadu_pd(&b_ts[2]);
_mm_storeu_pd(&b_y[7 * k + 2], _mm_mul_pd(r, r1));
r = _mm_loadu_pd(&b_ts[4]);
_mm_storeu_pd(&b_y[7 * k + 4], _mm_mul_pd(r, r1));
b_y[7 * k + 6] = b_ts[6] * b_P;
}
for (i = 0; i < 7; i++) {
for (i1 = 0; i1 < 7; i1++) {
b_P = 0.0;
for (k = 0; k < 7; k++) {
b_P += b_y[i + 7 * k] * ts[k + 7 * i1];
}
ps_tmp = i + 7 * i1;
ps[ps_tmp] = ts[ps_tmp] - b_P;
}
}
/* Compute the share of the posterior precision from prior and views, */
/* then for each individual view so we can compare it with lambda */
inv(ts, b_y);
memset(&ts[0], 0, 49U * sizeof(double));
for (k = 0; k < 7; k++) {
ps_tmp = 7 * k + 2;
i2 = 7 * k + 4;
ts_tmp = 7 * k + 6;
for (i = 0; i < 7; i++) {
b_P = ps[i + 7 * k];
r = _mm_loadu_pd(&b_y[7 * i]);
r1 = _mm_loadu_pd(&ts[7 * k]);
r2 = _mm_set1_pd(b_P);
_mm_storeu_pd(&ts[7 * k], _mm_add_pd(r1, _mm_mul_pd(r, r2)));
r = _mm_loadu_pd(&b_y[7 * i + 2]);
r1 = _mm_loadu_pd(&ts[ps_tmp]);
_mm_storeu_pd(&ts[ps_tmp], _mm_add_pd(r1, _mm_mul_pd(r, r2)));
r = _mm_loadu_pd(&b_y[7 * i + 4]);
r1 = _mm_loadu_pd(&ts[i2]);
_mm_storeu_pd(&ts[i2], _mm_add_pd(r1, _mm_mul_pd(r, r2)));
ts[ts_tmp] += b_y[7 * i + 6] * b_P;
}
}
b_P = 0.0;
for (k = 0; k < 7; k++) {
b_P += ts[k + 7 * k];
}
theta[0] = b_P / 7.0;
r = _mm_set1_pd(y);
for (k = 0; k < 7; k++) {
r1 = _mm_set1_pd(P[k]);
_mm_storeu_pd(&b_y[7 * k],
_mm_mul_pd(_mm_mul_pd(_mm_loadu_pd(&P[0]), r), r1));
_mm_storeu_pd(&b_y[7 * k + 2],
_mm_mul_pd(_mm_mul_pd(_mm_loadu_pd(&P[2]), r), r1));
_mm_storeu_pd(&b_y[7 * k + 4],
_mm_mul_pd(_mm_mul_pd(_mm_loadu_pd(&P[4]), r), r1));
b_y[7 * k + 6] = P[6] * y * P[k];
}
memset(&ts[0], 0, 49U * sizeof(double));
for (k = 0; k < 7; k++) {
ps_tmp = 7 * k + 2;
i2 = 7 * k + 4;
ts_tmp = 7 * k + 6;
for (i = 0; i < 7; i++) {
b_P = ps[i + 7 * k];
r = _mm_loadu_pd(&b_y[7 * i]);
r1 = _mm_loadu_pd(&ts[7 * k]);
r2 = _mm_set1_pd(b_P);
_mm_storeu_pd(&ts[7 * k], _mm_add_pd(r1, _mm_mul_pd(r, r2)));
r = _mm_loadu_pd(&b_y[7 * i + 2]);
r1 = _mm_loadu_pd(&ts[ps_tmp]);
_mm_storeu_pd(&ts[ps_tmp], _mm_add_pd(r1, _mm_mul_pd(r, r2)));
r = _mm_loadu_pd(&b_y[7 * i + 4]);
r1 = _mm_loadu_pd(&ts[i2]);
_mm_storeu_pd(&ts[i2], _mm_add_pd(r1, _mm_mul_pd(r, r2)));
ts[ts_tmp] += b_y[7 * i + 6] * b_P;
}
}
b_P = 0.0;
for (k = 0; k < 7; k++) {
b_P += ts[k + 7 * k];
}
b_P /= 7.0;
theta[1] = b_P;
theta[2] = b_P;
/* Compute posterior weights based solely on changed covariance */
for (k = 0; k <= 46; k += 2) {
r = _mm_loadu_pd(&ps[k]);
_mm_storeu_pd(&b_y[k], _mm_mul_pd(_mm_set1_pd(delta),
_mm_add_pd(_mm_loadu_pd(&sigma[k]), r)));
}
b_y[48] = delta * (sigma[48] + ps[48]);
inv(b_y, ts);
memset(&c_P[0], 0, 7U * sizeof(double));
for (k = 0; k < 7; k++) {
b_P = c_P[k];
for (i = 0; i < 7; i++) {
b_P += er[i] * ts[i + 7 * k];
}
c_P[k] = b_P;
w[k] = b_P;
}
/* Compute posterior weights based on uncertainty in mean and covariance */
memset(&c_P[0], 0, 7U * sizeof(double));
for (k = 0; k < 7; k++) {
b_P = c_P[k];
for (i = 0; i < 7; i++) {
b_P += pi[i] * ts[i + 7 * k];
}
c_P[k] = b_P;
pw[k] = b_P;
}
/* 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_ts);
memset(&c_P[0], 0, 7U * sizeof(double));
b_P = 0.0;
for (k = 0; k < 7; k++) {
d_P = c_P[k];
for (i = 0; i < 7; i++) {
d_P += er[i] * ts[i + 7 * k];
}
c_P[k] = d_P;
b_P += b_ts[k] * (d_P * (tau + 1.0) - weq[k]);
}
*lambda = b_P;
}
/*
* File trailer for hlblacklitterman.c
*
* [EOF]
*/