【C++】开源:量化金融计算库QuantLib配置与使用

😏1. 项目介绍

官网:https://www.quantlib.org/

项目Github地址:https://github.com/lballabio/QuantLib

QuantLib(Quantitative Finance Library)是一个开源的跨平台软件框架,专为量化金融领域设计和开发。它提供了丰富的金融工具和计算功能,用于衍生品定价、风险管理、投资组合管理等多个领域。以下是关于QuantLib的一些主要特点和用途:

1.开源跨平台:QuantLib是完全开源的,可以在不同操作系统上运行,包括Windows、Linux和Mac OS X。这使得它成为量化金融研究和开发的理想工具,能够在不同的环境中使用和定制。

2.丰富的金融工具:QuantLib支持多种金融工具和衍生品的定价和分析,包括利率衍生品(如利率互换、利率期权)、股票衍生品(如期权)、信用衍生品(如信用违约掉期)、外汇衍生品等。

3.数值方法和模型支持:QuantLib提供了广泛的数值方法和模型,用于衍生品定价和风险管理,如蒙特卡洛模拟、有限差分法、解析方法等。它支持的模型包括Black-Scholes模型、Heston模型、Libor Market Model等。

4.投资组合和风险管理:QuantLib能够处理复杂的投资组合和风险管理需求,包括风险测度、对冲分析、压力测试等,为金融机构和量化交易员提供重要的决策支持工具。

5.易于集成和扩展:QuantLib的设计允许用户根据特定需求进行定制和扩展,通过C++编程接口提供了灵活的扩展性,同时也支持Python等编程语言的接口,使得QuantLib能够与其他系统和库集成使用。

😊2. 环境配置

Ubuntu环境安装QuantLib库:

代码语言:javascript
复制
git clone https://github.com/lballabio/QuantLib # 或者下载release版本 1.34
mkdir build && cd build
cmake ..
make
sudo make install

程序g++编译:g++ -o main main.cpp -lQuantLib

😆3. 使用说明

下面是一个简单示例,计算零息债券的定价:

代码语言:javascript
复制
#include <ql/quantlib.hpp>
#include <iostream>

using namespace QuantLib;

int main() {
// 设置评估日期
Date today = Date::todaysDate();
Settings::instance().evaluationDate() = today;

// 定义债券参数
Real faceAmount = 1000.0; // 债券面值
Rate couponRate = 0.05; // 年利率
Date maturity = today + Period(1, Years); // 到期时间

// 创建收益率曲线
Rate marketRate = 0.03; // 市场利率
Handle&lt;YieldTermStructure&gt; discountCurve(boost::shared_ptr&lt;YieldTermStructure&gt;(
    new FlatForward(today, marketRate, Actual360())));

// 创建零息债券
ZeroCouponBond bond(0, NullCalendar(), faceAmount, maturity, Following, 100.0, today);

// 创建定价引擎并设置参数
bond.setPricingEngine(boost::shared_ptr&lt;PricingEngine&gt;(
    new DiscountingBondEngine(discountCurve)));

// 计算债券价格
Real bondPrice = bond.NPV();
std::cout &lt;&lt; &#34;Zero-coupon bond price: &#34; &lt;&lt; bondPrice &lt;&lt; std::endl;

return 0;

}

此外,还有官方示例里的BasketLosses 计算一组金融资产损失示例(看起来还是很复杂的):

代码语言:javascript
复制
/* -- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -- */

/*!
Copyright (C) 2009 Mark Joshi

This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/

QuantLib is free software: you can redistribute it and/or modify it
under the terms of the QuantLib license. You should have received a
copy of the license along with this program; if not, please email
<quantlib-dev@lists.sf.net>. The license is also available online at
<http://quantlib.org/license.shtml>.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include <ql/qldefines.hpp>
#if !defined(BOOST_ALL_NO_LIB) && defined(BOOST_MSVC)

include <ql/auto_link.hpp>

#endif
#include <ql/models/marketmodels/marketmodel.hpp>
#include <ql/models/marketmodels/accountingengine.hpp>
#include <ql/models/marketmodels/pathwiseaccountingengine.hpp>
#include <ql/models/marketmodels/products/multiproductcomposite.hpp>
#include <ql/models/marketmodels/products/multistep/multistepswap.hpp>
#include <ql/models/marketmodels/products/multistep/callspecifiedmultiproduct.hpp>
#include <ql/models/marketmodels/products/multistep/exerciseadapter.hpp>
#include <ql/models/marketmodels/products/multistep/multistepnothing.hpp>
#include <ql/models/marketmodels/products/multistep/multistepinversefloater.hpp>
#include <ql/models/marketmodels/products/pathwise/pathwiseproductswap.hpp>
#include <ql/models/marketmodels/products/pathwise/pathwiseproductinversefloater.hpp>
#include <ql/models/marketmodels/products/pathwise/pathwiseproductcallspecified.hpp>
#include <ql/models/marketmodels/models/flatvol.hpp>
#include <ql/models/marketmodels/callability/swapratetrigger.hpp>
#include <ql/models/marketmodels/callability/swapbasissystem.hpp>
#include <ql/models/marketmodels/callability/swapforwardbasissystem.hpp>
#include <ql/models/marketmodels/callability/nothingexercisevalue.hpp>
#include <ql/models/marketmodels/callability/collectnodedata.hpp>
#include <ql/models/marketmodels/callability/lsstrategy.hpp>
#include <ql/models/marketmodels/callability/upperboundengine.hpp>
#include <ql/models/marketmodels/correlations/expcorrelations.hpp>
#include <ql/models/marketmodels/browniangenerators/mtbrowniangenerator.hpp>
#include <ql/models/marketmodels/browniangenerators/sobolbrowniangenerator.hpp>
#include <ql/models/marketmodels/evolvers/lognormalfwdratepc.hpp>
#include <ql/models/marketmodels/evolvers/lognormalfwdrateeuler.hpp>
#include <ql/models/marketmodels/pathwisegreeks/bumpinstrumentjacobian.hpp>
#include <ql/models/marketmodels/utilities.hpp>
#include <ql/methods/montecarlo/genericlsregression.hpp>
#include <ql/legacy/libormarketmodels/lmlinexpcorrmodel.hpp>
#include <ql/legacy/libormarketmodels/lmextlinexpvolmodel.hpp>
#include <ql/time/schedule.hpp>
#include <ql/time/calendars/nullcalendar.hpp>
#include <ql/time/daycounters/simpledaycounter.hpp>
#include <ql/pricingengines/blackformula.hpp>
#include <ql/pricingengines/blackcalculator.hpp>
#include <ql/utilities/dataformatters.hpp>
#include <ql/math/integrals/segmentintegral.hpp>
#include <ql/math/statistics/convergencestatistics.hpp>
#include <ql/termstructures/volatility/abcd.hpp>
#include <ql/termstructures/volatility/abcdcalibration.hpp>
#include <ql/math/optimization/simplex.hpp>
#include <ql/quotes/simplequote.hpp>
#include <sstream>
#include <iostream>
#include <ctime>

using namespace QuantLib;

std::vector<std::vector<Matrix>>
theVegaBumps(bool factorwiseBumping, const ext::shared_ptr<MarketModel>& marketModel, bool doCaps) {
Real multiplierCutOff = 50.0;
Real projectionTolerance = 1E-4;
Size numberRates= marketModel->numberOfRates();

std::vector&lt;VolatilityBumpInstrumentJacobian::Cap&gt; caps;

if (doCaps)
{

    Rate capStrike = marketModel-&gt;initialRates()[0];

    for (Size i=0; i&lt; numberRates-1; i=i+1)
    {
        VolatilityBumpInstrumentJacobian::Cap nextCap;
        nextCap.startIndex_ = i;
        nextCap.endIndex_ = i+1;
        nextCap.strike_ = capStrike;
        caps.push_back(nextCap);
    }


}

std::vector&lt;VolatilityBumpInstrumentJacobian::Swaption&gt; swaptions(numberRates);

for (Size i=0; i &lt; numberRates; ++i)
{
    swaptions[i].startIndex_ = i;
    swaptions[i].endIndex_ = numberRates;

}

VegaBumpCollection possibleBumps(marketModel,
    factorwiseBumping);

OrthogonalizedBumpFinder  bumpFinder(possibleBumps,
    swaptions,
    caps,
    multiplierCutOff, // if vector length grows by more than this discard
    projectionTolerance);      // if vector projection before scaling less than this discard

std::vector&lt;std::vector&lt;Matrix&gt;&gt; theBumps;

bumpFinder.GetVegaBumps(theBumps);

return theBumps;

}

int Bermudan()
{

Size numberRates =20;
Real accrual = 0.5;
Real firstTime = 0.5;

std::vector&lt;Real&gt; rateTimes(numberRates+1);
for (Size i=0; i &lt; rateTimes.size(); ++i)
    rateTimes[i] = firstTime + i*accrual;

std::vector&lt;Real&gt; paymentTimes(numberRates);
std::vector&lt;Real&gt; accruals(numberRates,accrual);
for (Size i=0; i &lt; paymentTimes.size(); ++i)
    paymentTimes[i] = firstTime + (i+1)*accrual;

Real fixedRate = 0.05;
std::vector&lt;Real&gt; strikes(numberRates,fixedRate);
Real receive = -1.0;

// 0. a payer swap
MultiStepSwap payerSwap(rateTimes, accruals, accruals, paymentTimes,
    fixedRate, true);

// 1. the equivalent receiver swap
MultiStepSwap receiverSwap(rateTimes, accruals, accruals, paymentTimes,
    fixedRate, false);

//exercise schedule, we can exercise on any rate time except the last one
std::vector&lt;Rate&gt; exerciseTimes(rateTimes);
exerciseTimes.pop_back();

// naive exercise strategy, exercise above a trigger level
std::vector&lt;Rate&gt; swapTriggers(exerciseTimes.size(), fixedRate);
SwapRateTrigger naifStrategy(rateTimes, swapTriggers, exerciseTimes);

// Longstaff-Schwartz exercise strategy
std::vector&lt;std::vector&lt;NodeData&gt;&gt; collectedData;
std::vector&lt;std::vector&lt;Real&gt;&gt; basisCoefficients;

// control that does nothing, need it because some control is expected
NothingExerciseValue control(rateTimes);

// SwapForwardBasisSystem basisSystem(rateTimes,exerciseTimes);
SwapBasisSystem basisSystem(rateTimes,exerciseTimes);

// rebate that does nothing, need it because some rebate is expected
// when you break a swap nothing happens.
NothingExerciseValue nullRebate(rateTimes);

CallSpecifiedMultiProduct dummyProduct =
    CallSpecifiedMultiProduct(receiverSwap, naifStrategy,
    ExerciseAdapter(nullRebate));

const EvolutionDescription&amp; evolution = dummyProduct.evolution();

// parameters for models
Size seed = 12332; // for Sobol generator
Size trainingPaths = 65536;
Size paths = 16384;
Size vegaPaths = 16384*64;

std::cout &lt;&lt; &#34;training paths, &#34; &lt;&lt; trainingPaths &lt;&lt; &#34;\n&#34;;
std::cout &lt;&lt; &#34;paths, &#34; &lt;&lt; paths &lt;&lt; &#34;\n&#34;;
std::cout &lt;&lt; &#34;vega Paths, &#34; &lt;&lt; vegaPaths &lt;&lt; &#34;\n&#34;;

#ifdef _DEBUG
trainingPaths = 512;
paths = 1024;
vegaPaths = 1024;
#endif

// set up a calibration, this would typically be done by using a calibrator

Real rateLevel =0.05;

Real initialNumeraireValue = 0.95;

Real volLevel = 0.11;
Real beta = 0.2;
Real gamma = 1.0;
Size numberOfFactors = std::min&lt;Size&gt;(5,numberRates);

Spread displacementLevel =0.02;

// set up vectors
std::vector&lt;Rate&gt; initialRates(numberRates,rateLevel);
std::vector&lt;Volatility&gt; volatilities(numberRates, volLevel);
std::vector&lt;Spread&gt; displacements(numberRates, displacementLevel);

ExponentialForwardCorrelation correlations(
    rateTimes,volLevel, beta,gamma);

FlatVol  calibration(
    volatilities,
    ext::make_shared&lt;ExponentialForwardCorrelation&gt;(correlations),
    evolution,
    numberOfFactors,
    initialRates,
    displacements);

auto marketModel = ext::make_shared&lt;FlatVol&gt;(calibration);

// we use a factory since there is data that will only be known later
SobolBrownianGeneratorFactory generatorFactory(
    SobolBrownianGenerator::Diagonal, seed);

std::vector&lt;Size&gt; numeraires( moneyMarketMeasure(evolution));

// the evolver will actually evolve the rates
LogNormalFwdRatePc  evolver(marketModel,
    generatorFactory,
    numeraires   // numeraires for each step
    );

auto evolverPtr = ext::make_shared&lt;LogNormalFwdRatePc&gt;(evolver);

int t1= clock();

// gather data before computing exercise strategy
collectNodeData(evolver,
    receiverSwap,
    basisSystem,
    nullRebate,
    control,
    trainingPaths,
    collectedData);

int t2 = clock();

// calculate the exercise strategy&#39;s coefficients
genericLongstaffSchwartzRegression(collectedData,
    basisCoefficients);

// turn the coefficients into an exercise strategy
LongstaffSchwartzExerciseStrategy exerciseStrategy(
    basisSystem, basisCoefficients,
    evolution, numeraires,
    nullRebate, control);

//  bermudan swaption to enter into the payer swap
CallSpecifiedMultiProduct bermudanProduct =
    CallSpecifiedMultiProduct(
    MultiStepNothing(evolution),
    exerciseStrategy, payerSwap);

//  callable receiver swap
CallSpecifiedMultiProduct callableProduct =
    CallSpecifiedMultiProduct(
    receiverSwap, exerciseStrategy,
    ExerciseAdapter(nullRebate));

// lower bound: evolve all 4 products togheter
MultiProductComposite allProducts;
allProducts.add(payerSwap);
allProducts.add(receiverSwap);
allProducts.add(bermudanProduct);
allProducts.add(callableProduct);
allProducts.finalize();

AccountingEngine accounter(evolverPtr,
    Clone&lt;MarketModelMultiProduct&gt;(allProducts),
    initialNumeraireValue);

SequenceStatisticsInc stats;

accounter.multiplePathValues (stats,paths);

int t3 = clock();

std::vector&lt;Real&gt; means(stats.mean());

for (Real mean : means)
    std::cout &lt;&lt; mean &lt;&lt; &#34;\n&#34;;

std::cout &lt;&lt; &#34; time to build strategy, &#34; &lt;&lt; (t2-t1)/static_cast&lt;Real&gt;(CLOCKS_PER_SEC)&lt;&lt; &#34;, seconds.\n&#34;;
std::cout &lt;&lt; &#34; time to price, &#34; &lt;&lt; (t3-t2)/static_cast&lt;Real&gt;(CLOCKS_PER_SEC)&lt;&lt; &#34;, seconds.\n&#34;;

// vegas

// do it twice once with factorwise bumping, once without
Size pathsToDoVegas = vegaPaths;

for (Size i=0; i &lt; 4; ++i)
{

    bool allowFactorwiseBumping = i % 2 &gt; 0 ;

    bool doCaps = i / 2 &gt; 0 ;





    LogNormalFwdRateEuler evolverEuler(marketModel,
        generatorFactory,
        numeraires
        ) ;

    MarketModelPathwiseSwap receiverPathwiseSwap(  rateTimes,
        accruals,
        strikes,
        receive);
    Clone&lt;MarketModelPathwiseMultiProduct&gt; receiverPathwiseSwapPtr(receiverPathwiseSwap.clone());

    //  callable receiver swap
    CallSpecifiedPathwiseMultiProduct callableProductPathwise(receiverPathwiseSwapPtr,
        exerciseStrategy);

    Clone&lt;MarketModelPathwiseMultiProduct&gt; callableProductPathwisePtr(callableProductPathwise.clone());


    std::vector&lt;std::vector&lt;Matrix&gt;&gt; theBumps(theVegaBumps(allowFactorwiseBumping,
        marketModel,
        doCaps));

    PathwiseVegasOuterAccountingEngine
        accountingEngineVegas(ext::make_shared&lt;LogNormalFwdRateEuler&gt;(evolverEuler),
        callableProductPathwisePtr,
        marketModel,
        theBumps,
        initialNumeraireValue);

    std::vector&lt;Real&gt; values,errors;

    accountingEngineVegas.multiplePathValues(values,errors,pathsToDoVegas);


    std::cout &lt;&lt; &#34;vega output \n&#34;;
    std::cout &lt;&lt; &#34; factorwise bumping &#34; &lt;&lt; allowFactorwiseBumping &lt;&lt; &#34;\n&#34;;
    std::cout &lt;&lt; &#34; doCaps &#34; &lt;&lt; doCaps &lt;&lt; &#34;\n&#34;;



    Size r=0;

    std::cout &lt;&lt; &#34; price estimate, &#34; &lt;&lt; values[r++] &lt;&lt; &#34;\n&#34;;

    for (Size i=0; i &lt; numberRates; ++i, ++r)
        std::cout &lt;&lt; &#34; Delta, &#34; &lt;&lt; i &lt;&lt; &#34;, &#34; &lt;&lt; values[r] &lt;&lt; &#34;, &#34; &lt;&lt; errors[r] &lt;&lt; &#34;\n&#34;;

    Real totalVega = 0.0;

    for (; r &lt; values.size(); ++r)
    {
        std::cout &lt;&lt; &#34; vega, &#34; &lt;&lt; r - 1 -  numberRates&lt;&lt; &#34;, &#34; &lt;&lt; values[r] &lt;&lt; &#34; ,&#34; &lt;&lt; errors[r] &lt;&lt; &#34;\n&#34;;
        totalVega +=  values[r];
    }

    std::cout &lt;&lt; &#34; total Vega, &#34; &lt;&lt; totalVega &lt;&lt; &#34;\n&#34;;
}

// upper bound

MTBrownianGeneratorFactory uFactory(seed+142);

auto upperEvolver = ext::make_shared&lt;LogNormalFwdRatePc&gt;(ext::make_shared&lt;FlatVol&gt;(calibration),
        uFactory,
        numeraires   // numeraires for each step
        );

std::vector&lt;ext::shared_ptr&lt;MarketModelEvolver&gt;&gt; innerEvolvers;

std::valarray&lt;bool&gt; isExerciseTime =   isInSubset(evolution.evolutionTimes(),    exerciseStrategy.exerciseTimes());

for (Size s=0; s &lt; isExerciseTime.size(); ++s)
{
    if (isExerciseTime[s])
    {
        MTBrownianGeneratorFactory iFactory(seed+s);
        auto e = ext::make_shared&lt;LogNormalFwdRatePc&gt;(ext::make_shared&lt;FlatVol&gt;(calibration),
                uFactory,
                numeraires,  // numeraires for each step
                s);

        innerEvolvers.push_back(e);
    }
}

UpperBoundEngine uEngine(upperEvolver,  // does outer paths
                         innerEvolvers, // for sub-simulations that do continuation values
                         receiverSwap,
                         nullRebate,
                         receiverSwap,
                         nullRebate,
                         exerciseStrategy,
                         initialNumeraireValue);

Statistics uStats;
Size innerPaths = 255;
Size outerPaths =256;

int t4 = clock();

uEngine.multiplePathValues(uStats,outerPaths,innerPaths);
Real upperBound = uStats.mean();
Real upperSE = uStats.errorEstimate();

int t5=clock();

std::cout &lt;&lt; &#34; Upper - lower is, &#34; &lt;&lt; upperBound &lt;&lt; &#34;, with standard error &#34; &lt;&lt; upperSE &lt;&lt; &#34;\n&#34;;
std::cout &lt;&lt; &#34; time to compute upper bound is,  &#34; &lt;&lt; (t5-t4)/static_cast&lt;Real&gt;(CLOCKS_PER_SEC) &lt;&lt; &#34;, seconds.\n&#34;;

return 0;

}

int InverseFloater(Real rateLevel)
{

Size numberRates =20;
Real accrual = 0.5;
Real firstTime = 0.5;

Real strike =0.15;
Real fixedMultiplier = 2.0;
Real floatingSpread =0.0;
bool payer = true;


std::vector&lt;Real&gt; rateTimes(numberRates+1);
for (Size i=0; i &lt; rateTimes.size(); ++i)
    rateTimes[i] = firstTime + i*accrual;

std::vector&lt;Real&gt; paymentTimes(numberRates);
std::vector&lt;Real&gt; accruals(numberRates,accrual);
std::vector&lt;Real&gt; fixedStrikes(numberRates,strike);
std::vector&lt;Real&gt; floatingSpreads(numberRates,floatingSpread);
std::vector&lt;Real&gt; fixedMultipliers(numberRates,fixedMultiplier);

for (Size i=0; i &lt; paymentTimes.size(); ++i)
    paymentTimes[i] = firstTime + (i+1)*accrual;

MultiStepInverseFloater inverseFloater(
rateTimes,
accruals,
accruals,
fixedStrikes,
fixedMultipliers,
floatingSpreads,
paymentTimes,
payer);

//exercise schedule, we can exercise on any rate time except the last one
std::vector&lt;Rate&gt; exerciseTimes(rateTimes);
exerciseTimes.pop_back();

// naive exercise strategy, exercise above a trigger level
Real trigger =0.05;
std::vector&lt;Rate&gt; swapTriggers(exerciseTimes.size(), trigger);
SwapRateTrigger naifStrategy(rateTimes, swapTriggers, exerciseTimes);

// Longstaff-Schwartz exercise strategy
std::vector&lt;std::vector&lt;NodeData&gt;&gt; collectedData;
std::vector&lt;std::vector&lt;Real&gt;&gt; basisCoefficients;

// control that does nothing, need it because some control is expected
NothingExerciseValue control(rateTimes);

SwapForwardBasisSystem basisSystem(rateTimes,exerciseTimes);
// SwapBasisSystem basisSystem(rateTimes,exerciseTimes);

// rebate that does nothing, need it because some rebate is expected
// when you break a swap nothing happens.
NothingExerciseValue nullRebate(rateTimes);

CallSpecifiedMultiProduct dummyProduct =
    CallSpecifiedMultiProduct(inverseFloater, naifStrategy,
    ExerciseAdapter(nullRebate));

const EvolutionDescription&amp; evolution = dummyProduct.evolution();


// parameters for models


Size seed = 12332; // for Sobol generator
Size trainingPaths = 65536;
Size paths = 65536;
Size vegaPaths =16384;

#ifdef _DEBUG
trainingPaths = 8192;
paths = 8192;
vegaPaths = 1024;
#endif

std::cout &lt;&lt;  &#34; inverse floater \n&#34;;
std::cout &lt;&lt; &#34; fixed strikes :  &#34;  &lt;&lt; strike &lt;&lt; &#34;\n&#34;;
std::cout &lt;&lt; &#34; number rates :  &#34; &lt;&lt; numberRates &lt;&lt; &#34;\n&#34;;

std::cout &lt;&lt; &#34;training paths, &#34; &lt;&lt; trainingPaths &lt;&lt; &#34;\n&#34;;
std::cout &lt;&lt; &#34;paths, &#34; &lt;&lt; paths &lt;&lt; &#34;\n&#34;;
std::cout &lt;&lt; &#34;vega Paths, &#34; &lt;&lt; vegaPaths &lt;&lt; &#34;\n&#34;;


// set up a calibration, this would typically be done by using a calibrator



//Real rateLevel =0.08;

std::cout &lt;&lt; &#34; rate level &#34; &lt;&lt;  rateLevel &lt;&lt; &#34;\n&#34;;

Real initialNumeraireValue = 0.95;

Real volLevel = 0.11;
Real beta = 0.2;
Real gamma = 1.0;
Size numberOfFactors = std::min&lt;Size&gt;(5,numberRates);

Spread displacementLevel =0.02;

// set up vectors
std::vector&lt;Rate&gt; initialRates(numberRates,rateLevel);
std::vector&lt;Volatility&gt; volatilities(numberRates, volLevel);
std::vector&lt;Spread&gt; displacements(numberRates, displacementLevel);

ExponentialForwardCorrelation correlations(
    rateTimes,volLevel, beta,gamma);




FlatVol  calibration(
    volatilities,
    ext::make_shared&lt;ExponentialForwardCorrelation&gt;(correlations),
    evolution,
    numberOfFactors,
    initialRates,
    displacements);

auto marketModel = ext::make_shared&lt;FlatVol&gt;(calibration);

// we use a factory since there is data that will only be known later
SobolBrownianGeneratorFactory generatorFactory(
    SobolBrownianGenerator::Diagonal, seed);

std::vector&lt;Size&gt; numeraires( moneyMarketMeasure(evolution));

// the evolver will actually evolve the rates
LogNormalFwdRatePc  evolver(marketModel,
    generatorFactory,
    numeraires   // numeraires for each step
    );

auto evolverPtr = ext::make_shared&lt;LogNormalFwdRatePc&gt;(evolver);

int t1= clock();

// gather data before computing exercise strategy
collectNodeData(evolver,
    inverseFloater,
    basisSystem,
    nullRebate,
    control,
    trainingPaths,
    collectedData);

int t2 = clock();


// calculate the exercise strategy&#39;s coefficients
genericLongstaffSchwartzRegression(collectedData,
    basisCoefficients);


// turn the coefficients into an exercise strategy
LongstaffSchwartzExerciseStrategy exerciseStrategy(
    basisSystem, basisCoefficients,
    evolution, numeraires,
    nullRebate, control);


//  callable receiver swap
CallSpecifiedMultiProduct callableProduct =
    CallSpecifiedMultiProduct(
    inverseFloater, exerciseStrategy,
    ExerciseAdapter(nullRebate));

 MultiProductComposite allProducts;
allProducts.add(inverseFloater);
allProducts.add(callableProduct);
allProducts.finalize();


AccountingEngine accounter(evolverPtr,
    Clone&lt;MarketModelMultiProduct&gt;(allProducts),
    initialNumeraireValue);

SequenceStatisticsInc stats;

accounter.multiplePathValues (stats,paths);

int t3 = clock();

std::vector&lt;Real&gt; means(stats.mean());

for (Real mean : means)
    std::cout &lt;&lt; mean &lt;&lt; &#34;\n&#34;;

std::cout &lt;&lt; &#34; time to build strategy, &#34; &lt;&lt; (t2-t1)/static_cast&lt;Real&gt;(CLOCKS_PER_SEC)&lt;&lt; &#34;, seconds.\n&#34;;
std::cout &lt;&lt; &#34; time to price, &#34; &lt;&lt; (t3-t2)/static_cast&lt;Real&gt;(CLOCKS_PER_SEC)&lt;&lt; &#34;, seconds.\n&#34;;

// vegas

// do it twice once with factorwise bumping, once without
Size pathsToDoVegas = vegaPaths;

for (Size i=0; i &lt; 4; ++i)
{

    bool allowFactorwiseBumping = i % 2 &gt; 0 ;

    bool doCaps = i / 2 &gt; 0 ;


    LogNormalFwdRateEuler evolverEuler(marketModel,
        generatorFactory,
        numeraires
        ) ;

    MarketModelPathwiseInverseFloater pathwiseInverseFloater(
                                                     rateTimes,
                                                     accruals,
                                                     accruals,
                                                     fixedStrikes,
                                                     fixedMultipliers,
                                                     floatingSpreads,
                                                     paymentTimes,
                                                     payer);

    Clone&lt;MarketModelPathwiseMultiProduct&gt; pathwiseInverseFloaterPtr(pathwiseInverseFloater.clone());

    //  callable inverse floater
    CallSpecifiedPathwiseMultiProduct callableProductPathwise(pathwiseInverseFloaterPtr,
                                                                                                                                           exerciseStrategy);

    Clone&lt;MarketModelPathwiseMultiProduct&gt; callableProductPathwisePtr(callableProductPathwise.clone());


    std::vector&lt;std::vector&lt;Matrix&gt;&gt; theBumps(theVegaBumps(allowFactorwiseBumping,
        marketModel,
        doCaps));

    PathwiseVegasOuterAccountingEngine
        accountingEngineVegas(ext::make_shared&lt;LogNormalFwdRateEuler&gt;(evolverEuler),

// pathwiseInverseFloaterPtr,
callableProductPathwisePtr,
marketModel,
theBumps,
initialNumeraireValue);

    std::vector&lt;Real&gt; values,errors;

    accountingEngineVegas.multiplePathValues(values,errors,pathsToDoVegas);


    std::cout &lt;&lt; &#34;vega output \n&#34;;
    std::cout &lt;&lt; &#34; factorwise bumping &#34; &lt;&lt; allowFactorwiseBumping &lt;&lt; &#34;\n&#34;;
    std::cout &lt;&lt; &#34; doCaps &#34; &lt;&lt; doCaps &lt;&lt; &#34;\n&#34;;



    Size r=0;

    std::cout &lt;&lt; &#34; price estimate, &#34; &lt;&lt; values[r++] &lt;&lt; &#34;\n&#34;;

    for (Size i=0; i &lt; numberRates; ++i, ++r)
        std::cout &lt;&lt; &#34; Delta, &#34; &lt;&lt; i &lt;&lt; &#34;, &#34; &lt;&lt; values[r] &lt;&lt; &#34;, &#34; &lt;&lt; errors[r] &lt;&lt; &#34;\n&#34;;

    Real totalVega = 0.0;

    for (; r &lt; values.size(); ++r)
    {
        std::cout &lt;&lt; &#34; vega, &#34; &lt;&lt; r - 1 -  numberRates&lt;&lt; &#34;, &#34; &lt;&lt; values[r] &lt;&lt; &#34; ,&#34; &lt;&lt; errors[r] &lt;&lt; &#34;\n&#34;;
        totalVega +=  values[r];
    }

    std::cout &lt;&lt; &#34; total Vega, &#34; &lt;&lt; totalVega &lt;&lt; &#34;\n&#34;;
}

// upper bound

MTBrownianGeneratorFactory uFactory(seed+142);


auto upperEvolver = ext::make_shared&lt;LogNormalFwdRatePc&gt;(ext::make_shared&lt;FlatVol&gt;(calibration),
        uFactory,
        numeraires   // numeraires for each step
        );

std::vector&lt;ext::shared_ptr&lt;MarketModelEvolver&gt;&gt; innerEvolvers;

std::valarray&lt;bool&gt; isExerciseTime =   isInSubset(evolution.evolutionTimes(),    exerciseStrategy.exerciseTimes());

for (Size s=0; s &lt; isExerciseTime.size(); ++s)
{
    if (isExerciseTime[s])
    {
        MTBrownianGeneratorFactory iFactory(seed+s);
        auto e = ext::make_shared&lt;LogNormalFwdRatePc&gt;(ext::make_shared&lt;FlatVol&gt;(calibration),
                uFactory,
                numeraires ,  // numeraires for each step
                s);

        innerEvolvers.push_back(e);
    }
}



UpperBoundEngine uEngine(upperEvolver,  // does outer paths
                         innerEvolvers, // for sub-simulations that do continuation values
                         inverseFloater,
                         nullRebate,
                         inverseFloater,
                         nullRebate,
                         exerciseStrategy,
                         initialNumeraireValue);

Statistics uStats;
Size innerPaths = 255;
Size outerPaths =256;

int t4 = clock();

uEngine.multiplePathValues(uStats,outerPaths,innerPaths);
Real upperBound = uStats.mean();
Real upperSE = uStats.errorEstimate();

int t5=clock();

std::cout &lt;&lt; &#34; Upper - lower is, &#34; &lt;&lt; upperBound &lt;&lt; &#34;, with standard error &#34; &lt;&lt; upperSE &lt;&lt; &#34;\n&#34;;
std::cout &lt;&lt; &#34; time to compute upper bound is,  &#34; &lt;&lt; (t5-t4)/static_cast&lt;Real&gt;(CLOCKS_PER_SEC) &lt;&lt; &#34;, seconds.\n&#34;;

return 0;

}

int main()
{
try {
for (Size i=5; i < 10; ++i)
InverseFloater(i/100.0);

    return 0;
} catch (std::exception&amp; e) {
    std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
    return 1;
} catch (...) {
    std::cerr &lt;&lt; &#34;unknown error&#34; &lt;&lt; std::endl;
    return 1;
}

}