38#include <boost/accumulators/accumulators.hpp>
39#include <boost/accumulators/statistics/stats.hpp>
40#include <boost/accumulators/statistics/weighted_sum.hpp>
41#include <boost/timer/timer.hpp>
47std::size_t numberOfStochasticRvs(
const std::vector<RandomVariable>& v) {
48 return std::count_if(v.begin(), v.end(),
49 [](
const RandomVariable& r) { return r.initialised() && !r.deterministic(); });
54 const QuantLib::ext::shared_ptr<ore::data::CurveConfigurations>& curveConfigs,
55 const QuantLib::ext::shared_ptr<ore::data::TodaysMarketParameters>& todaysMarketParams,
56 const QuantLib::ext::shared_ptr<ore::analytics::ScenarioSimMarketParameters>& simMarketData,
57 const QuantLib::ext::shared_ptr<ore::data::EngineData>& engineData,
58 const QuantLib::ext::shared_ptr<ore::analytics::CrossAssetModelData>& crossAssetModelData,
59 const QuantLib::ext::shared_ptr<ore::analytics::ScenarioGeneratorData>& scenarioGeneratorData,
60 const QuantLib::ext::shared_ptr<ore::data::Portfolio>& portfolio,
const string& marketConfiguration,
61 const string& marketConfigurationInCcy,
62 const QuantLib::ext::shared_ptr<ore::analytics::SensitivityScenarioData>& sensitivityData,
63 const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceData,
65 const bool bumpCvaSensis,
const bool continueOnCalibrationError,
66 const bool continueOnError,
const std::string& context)
68 todaysMarketParams_(todaysMarketParams), simMarketData_(simMarketData), engineData_(engineData),
69 crossAssetModelData_(crossAssetModelData), scenarioGeneratorData_(scenarioGeneratorData), portfolio_(portfolio),
70 marketConfiguration_(marketConfiguration), marketConfigurationInCcy_(marketConfigurationInCcy),
71 sensitivityData_(sensitivityData), referenceData_(referenceData), iborFallbackConfig_(iborFallbackConfig),
72 bumpCvaSensis_(bumpCvaSensis), continueOnCalibrationError_(continueOnCalibrationError), continueOnError_(continueOnError),
context_(context) {
89 LOG(
"XvaEngineCG: started");
90 boost::timer::cpu_timer timer;
94 LOG(
"XvaEngineCG: build init market");
100 boost::timer::nanosecond_type timing1 = timer.elapsed().wall;
104 LOG(
"XvaEngineCG: build sim market");
107 simMarket_ = QuantLib::ext::make_shared<ore::analytics::ScenarioSimMarket>(
111 boost::timer::nanosecond_type timing2 = timer.elapsed().wall;
115 LOG(
"XvaEngineCG: build cam model builder");
118 camBuilder_ = QuantLib::ext::make_shared<CrossAssetModelBuilder>(
121 std::string(), SalvagingAlgorithm::Spectral,
"xva engine cg - cam builder");
125 LOG(
"XvaEngineCG: build cam cg model");
129 "XvaEngineCG: cam is required to use discretization 'Euler', please update simulation parameters accordingly.");
131 std::vector<std::string> currencies;
132 std::vector<Handle<YieldTermStructure>> curves;
133 std::vector<Handle<Quote>> fxSpots;
134 std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<InterestRateIndex>>> irIndices;
135 std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<ZeroInflationIndex>>> infIndices;
136 std::vector<std::string> indices;
137 std::vector<std::string> indexCurrencies;
140 currencies.push_back(
"EUR");
141 curves.push_back(
camBuilder_->model()->irModel(0)->termStructure());
142 irIndices.push_back(std::make_pair(
"EUR-EURIBOR-6M", *
simMarket_->iborIndex(
"EUR-EURIBOR-6M")));
148 Size timeStepsPerYear = 1;
152 curves, fxSpots, irIndices, infIndices, indices, indexCurrencies,
153 simulationDates, timeStepsPerYear, iborFallbackConfig,
154 std::vector<Size>(), std::vector<std::string>(),
true);
156 boost::timer::nanosecond_type timing3 = timer.elapsed().wall;
160 LOG(
"XvaEngineCG: build trades against global cam cg model");
162 auto edCopy = QuantLib::ext::make_shared<EngineData>(*
engineData_);
163 edCopy->globalParameters()[
"GenerateAdditionalResults"] =
"false";
164 edCopy->globalParameters()[
"RunType"] =
"NPV";
165 map<MarketContext, string> configurations;
171 EngineBuilderFactory::instance().generateAmcCgEngineBuilders(
175 portfolio_->build(factory,
"xva engine cg",
true);
177 boost::timer::nanosecond_type timing4 = timer.elapsed().wall;
182 LOG(
"XvaEngineCG: build computation graph for all trades");
184 std::vector<std::vector<std::size_t>> amcNpvNodes;
186 auto g =
model_->computationGraph();
188 for (
auto const& [
id, trade] :
portfolio_->trades()) {
189 auto qlInstr = QuantLib::ext::dynamic_pointer_cast<ScriptedInstrument>(trade->instrument()->qlInstrument());
190 QL_REQUIRE(qlInstr,
"XvaEngineCG: expeced trade to provide ScriptedInstrument, trade '" <<
id <<
"' does not.");
191 auto engine = QuantLib::ext::dynamic_pointer_cast<ScriptedInstrumentPricingEngineCG>(qlInstr->pricingEngine());
192 QL_REQUIRE(engine,
"XvaEngineCG: expected to get ScriptedInstrumentPricingEngineCG, trade '"
193 <<
id <<
"' has a different engine.");
195 engine->buildComputationGraph();
196 std::vector<std::size_t> tmp;
197 tmp.push_back(
cg_add(*g,
cg_const(*g, 0.0), g->variable(engine->npvName() +
"_0")));
198 for (std::size_t i = 0; i < simulationDates.size(); ++i) {
199 tmp.push_back(
cg_add(*g,
cg_const(*g, 0.0), g->variable(
"_AMC_NPV_" + std::to_string(i))));
201 amcNpvNodes.push_back(tmp);
205 boost::timer::nanosecond_type timing5 = timer.elapsed().wall;
212 std::vector<std::size_t> pfPathExposureNodes, pfExposureNodes;
213 for (Size i = 0; i < simulationDates.size() + 1; ++i) {
214 std::size_t sumNode =
cg_const(*g, 0.0);
215 for (
auto const& v : amcNpvNodes) {
216 sumNode =
cg_add(*g, sumNode, v[i]);
218 pfPathExposureNodes.push_back(sumNode);
219 pfExposureNodes.push_back(
220 model_->npv(sumNode, i == 0 ?
model_->referenceDate() : *std::next(simulationDates.begin(), i - 1),
224 boost::timer::nanosecond_type timing6 = timer.elapsed().wall;
231 auto defaultCurve =
simMarket_->defaultCurve(
"BANK")->curve();
232 model_->registerWith(defaultCurve);
234 for (Size i = 0; i < simulationDates.size(); ++i) {
235 Date d = i == 0 ?
model_->referenceDate() : *std::next(simulationDates.begin(), i - 1);
236 Date e = *std::next(simulationDates.begin(), i);
237 std::size_t defaultProb =
239 [defaultCurve, d, e]() { return defaultCurve->defaultProbability(d, e); });
243 boost::timer::nanosecond_type timing7 = timer.elapsed().wall;
245 LOG(
"XvaEngineCG: graph building complete, size is " << g->size());
246 LOG(
"XvaEngineCG: got " << g->redBlockDependencies().size() <<
" red block dependencies.");
247 std::size_t sumRedNodes = 0;
248 for (
auto const& r : g->redBlockRanges()) {
249 DLOG(
"XvaEngineCG: red block range " << r.first <<
" ... " << r.second);
250 sumRedNodes += r.second - r.first;
261 boost::timer::nanosecond_type timing8 = timer.elapsed().wall;
268 boost::timer::nanosecond_type timing9 = timer.elapsed().wall;
270 std::size_t rvMemMax = numberOfStochasticRvs(values) + numberOfStochasticRvs(derivatives);
280 LOG(
"XvaEngineCG: do forward evaluation");
288 std::vector<bool> keepNodes(g->size(),
false);
290 for (
auto const& c : g->constants()) {
291 keepNodes[c.second] =
true;
298 for (
auto const n : g->redBlockDependencies()) {
303 for (
auto const& rv :
model_->randomVariates())
304 for (
auto const& v : rv)
308 for (
auto const& n : pfExposureNodes) {
314 boost::timer::nanosecond_type timing10 = timer.elapsed().wall;
319 epeReport_ = QuantLib::ext::make_shared<InMemoryReport>();
320 epeReport_->addColumn(
"Date", Date()).addColumn(
"EPE",
double(), 4).addColumn(
"ENE",
double(), 4);
322 for (Size i = 0; i < simulationDates.size() + 1; ++i) {
324 epeReport_->add(i == 0 ?
model_->referenceDate() : *std::next(simulationDates.begin(), i - 1))
329 epeReport_->toFile(
"Output/xvacg-exposure.csv");
333 LOG(
"XvaEngineCG: Calcuated CVA (node " << cvaNode <<
") = " << cva);
335 rvMemMax = std::max(rvMemMax, numberOfStochasticRvs(values) + numberOfStochasticRvs(derivatives));
339 boost::timer::nanosecond_type timing11 = timer.elapsed().wall;
345 LOG(
"XvaEngineCG: run backward derivatives");
349 std::vector<bool> keepNodesDerivatives(g->size(),
false);
352 keepNodesDerivatives[n] =
true;
364 modelParamDerivatives[i++] =
expectation(derivatives[n]).
at(0);
369 rvMemMax = std::max(rvMemMax, numberOfStochasticRvs(values) + numberOfStochasticRvs(derivatives));
371 LOG(
"XvaEngineCG: got " << modelParamDerivatives.size()
372 <<
" model parameter derivatives from run backward derivatives");
374 timing11 = timer.elapsed().wall;
385 LOG(
"XvaEngineCG: running sensi scenarios");
389 QuantLib::ext::make_shared<DeltaScenarioFactory>(
simMarket_->baseScenario()),
false, std::string(), continueOnError,
394 auto resultCube = QuantLib::ext::make_shared<DoublePrecisionSensiCube>(std::set<std::string>{
"CVA"},
asof_,
396 resultCube->setT0(cva, 0, 0);
398 model_->alwaysForwardNotifications();
400 Size activeScenarios = 0;
401 for (Size sample = 0; sample < resultCube->samples(); ++sample) {
417 if (!
model_->isCalculated()) {
426 auto modelParameters =
model_->modelParameters();
428 boost::accumulators::accumulator_set<
429 double, boost::accumulators::stats<boost::accumulators::tag::weighted_sum>,
double>
432 Real v1 = modelParameters[i].second;
433 acc(modelParamDerivatives[i], boost::accumulators::weight = (v1 - v0));
436 sensi = boost::accumulators::weighted_sum(acc);
451 resultCube->set(cva + sensi, 0, 0, sample, 0);
454 boost::timer::nanosecond_type timing12 = timer.elapsed().wall;
456 LOG(
"XvaEngineCG: finished running " << resultCube->samples() <<
" sensi scenarios, thereof " << activeScenarios
462 sensiReport_ = QuantLib::ext::make_shared<InMemoryReport>();
463 auto sensiCube = QuantLib::ext::make_shared<SensitivityCube>(
466 auto sensiStream = QuantLib::ext::make_shared<SensitivityCubeStream>(sensiCube,
simMarketData_->baseCcy());
468 sensiReport_->toFile(
"Output/xvacg-cva-sensi-scenario.csv");
473 LOG(
"XvaEngineCG: graph size : " << g->size());
474 LOG(
"XvaEngineCG: red nodes : " << sumRedNodes);
475 LOG(
"XvaEngineCG: red node dependendices : " << g->redBlockDependencies().size());
477 LOG(
"XvaEngineCG: Peak theoretical rv mem : " <<
static_cast<double>(rvMemMax) / 1024 / 1024 * 8 *
model_->size()
479 LOG(
"XvaEngineCG: T0 market build : " << std::fixed << std::setprecision(1) << timing1 / 1E6 <<
" ms");
480 LOG(
"XvaEngineCG: Sim market build : " << std::fixed << std::setprecision(1) << (timing2 - timing1) / 1E6
482 LOG(
"XvaEngineCG: Part A CG build : " << std::fixed << std::setprecision(1) << (timing3 - timing2) / 1E6
484 LOG(
"XvaEngineCG: Portfolio build : " << std::fixed << std::setprecision(1) << (timing4 - timing3) / 1E6
486 LOG(
"XvaEngineCG: Part B CG build : " << std::fixed << std::setprecision(1) << (timing5 - timing4) / 1E6
488 LOG(
"XvaEngineCG: Part C CG build : " << std::fixed << std::setprecision(1) << (timing6 - timing5) / 1E6
490 LOG(
"XvaEngineCG: Part D CG build : " << std::fixed << std::setprecision(1) << (timing7 - timing6) / 1E6
492 LOG(
"XvaEngineCG: RV gen : " << std::fixed << std::setprecision(1) << (timing8 - timing7) / 1E6
494 LOG(
"XvaEngineCG: Const and Model params : " << std::fixed << std::setprecision(1) << (timing9 - timing8) / 1E6
496 LOG(
"XvaEngineCG: Forward eval : " << std::fixed << std::setprecision(1) << (timing10 - timing9) / 1E6
498 LOG(
"XvaEngineCG: Backward deriv : " << std::fixed << std::setprecision(1) << (timing11 - timing10) / 1E6
500 LOG(
"XvaEngineCG: Sensi Cube Gen : " << std::fixed << std::setprecision(1) << (timing12 - timing11) / 1E6
502 LOG(
"XvaEngineCG: total : " << std::fixed << std::setprecision(1) << timing12 / 1E6 <<
" ms");
507 DLOG(
"XvaEngineCG: populate random variates");
509 auto const& rv =
model_->randomVariates();
514 for (Size path = 0; path <
model_->size(); ++path) {
515 auto p = gen->next();
516 for (Size j = 0; j < rv.front().
size(); ++j) {
517 for (Size k = 0; k < rv.size(); ++k) {
518 values[rv[k][j]].set(path, p.value[j][k]);
522 DLOG(
"XvaEngineCG: generated rvs for " << rv.size() <<
" underlyings and " << rv.front().size()
529 DLOG(
"XvaEngineCG: populate constants");
531 auto g =
model_->computationGraph();
532 for (
auto const& c : g->constants()) {
536 DLOG(
"XvaEngineCG: set " << g->constants().size() <<
" constants");
540 const std::vector<std::pair<std::size_t, double>>& modelParameters)
const {
542 DLOG(
"XvaEngineCG: populate model parameters");
544 for (
auto const& [n, v] : modelParameters) {
548 DLOG(
"XvaEngineCG: set " << modelParameters.size() <<
" model parameters.");
Write ORE outputs to reports.
virtual void writeScenarioReport(ore::data::Report &report, const std::vector< QuantLib::ext::shared_ptr< SensitivityCube > > &sensitivityCubes, QuantLib::Real outputThreshold=0.0)
QuantLib::ext::shared_ptr< ore::data::CurveConfigurations > curveConfigs_
QuantLib::ext::shared_ptr< ore::data::TodaysMarketParameters > todaysMarketParams_
void populateModelParameters(std::vector< RandomVariable > &values, const std::vector< std::pair< std::size_t, double > > &modelParameters) const
std::string marketConfiguration_
QuantLib::ext::shared_ptr< ore::analytics::SensitivityScenarioData > sensitivityData_
QuantLib::ext::shared_ptr< SensitivityScenarioGenerator > sensiScenarioGenerator_
QuantLib::ext::shared_ptr< InMemoryReport > sensiReport_
QuantLib::ext::shared_ptr< ore::analytics::CrossAssetModelData > crossAssetModelData_
QuantLib::ext::shared_ptr< ore::analytics::ScenarioSimMarketParameters > simMarketData_
std::vector< std::pair< std::size_t, double > > baseModelParams_
bool continueOnCalibrationError_
std::vector< RandomVariableOpNodeRequirements > opNodeRequirements_
QuantLib::ext::shared_ptr< CrossAssetModelBuilder > camBuilder_
std::vector< RandomVariableOp > ops_
QuantLib::ext::shared_ptr< ore::data::Portfolio > portfolio_
QuantLib::ext::shared_ptr< InMemoryReport > epeReport_
XvaEngineCG(const Size nThreads, const Date &asof, const QuantLib::ext::shared_ptr< ore::data::Loader > &loader, const QuantLib::ext::shared_ptr< ore::data::CurveConfigurations > &curveConfigs, const QuantLib::ext::shared_ptr< ore::data::TodaysMarketParameters > &todaysMarketParams, const QuantLib::ext::shared_ptr< ore::analytics::ScenarioSimMarketParameters > &simMarketData, const QuantLib::ext::shared_ptr< ore::data::EngineData > &engineData, const QuantLib::ext::shared_ptr< ore::analytics::CrossAssetModelData > &crossAssetModelData, const QuantLib::ext::shared_ptr< ore::analytics::ScenarioGeneratorData > &scenarioGeneratorData, const QuantLib::ext::shared_ptr< ore::data::Portfolio > &portfolio, const string &marketConfiguration=Market::defaultConfiguration, const string &marketConfigurationInCcy=Market::inCcyConfiguration, const QuantLib::ext::shared_ptr< ore::analytics::SensitivityScenarioData > &sensitivityData=nullptr, const QuantLib::ext::shared_ptr< ReferenceDataManager > &referenceData=nullptr, const IborFallbackConfig &iborFallbackConfig=IborFallbackConfig::defaultConfig(), const bool bumpCvaSensis=false, const bool continueOnCalibrationError=true, const bool continueOnError=true, const std::string &context="xva engine cg")
void populateConstants(std::vector< RandomVariable > &values) const
std::string marketConfigurationInCcy_
IborFallbackConfig iborFallbackConfig_
std::vector< RandomVariableGrad > grads_
QuantLib::ext::shared_ptr< ore::analytics::ScenarioGeneratorData > scenarioGeneratorData_
QuantLib::ext::shared_ptr< ore::data::Market > initMarket_
QuantLib::ext::shared_ptr< ReferenceDataManager > referenceData_
QuantLib::ext::shared_ptr< GaussianCamCG > model_
QuantLib::ext::shared_ptr< ore::data::Loader > loader_
void populateRandomVariates(std::vector< RandomVariable > &values) const
QuantLib::ext::shared_ptr< ore::analytics::ScenarioSimMarket > simMarket_
QuantLib::ext::shared_ptr< ore::data::EngineData > engineData_
factory class for cloning a cached scenario
unsigned long long getPeakMemoryUsageBytes()
RandomVariable max(RandomVariable x, const RandomVariable &y)
std::size_t cg_const(ComputationGraph &g, const double value)
void forwardEvaluation(const ComputationGraph &g, std::vector< T > &values, const std::vector< std::function< T(const std::vector< const T * > &)> > &ops, std::function< void(T &)> deleter={}, bool keepValuesForDerivatives=true, const std::vector< std::function< std::pair< std::vector< bool >, bool >(const std::size_t)> > &opRequiresNodesForDerivatives={}, const std::vector< bool > &keepNodes={})
std::size_t cg_max(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::vector< RandomVariableOpNodeRequirements > getRandomVariableOpNodeRequirements()
boost::shared_ptr< MultiPathVariateGeneratorBase > makeMultiPathVariateGenerator(const SequenceType s, const Size dimension, const Size timeSteps, const BigNatural seed, const SobolBrownianGenerator::Ordering ordering, const SobolRsg::DirectionIntegers directionIntegers)
RandomVariable expectation(const RandomVariable &r)
std::size_t cg_mult(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_add(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::vector< RandomVariableOp > getRandomVariableOps(const Size size, const std::map< Size, std::vector< std::function< RandomVariable(const std::vector< const RandomVariable * > &)> > > &basisFn)
std::vector< RandomVariableGrad > getRandomVariableGradients(const Size size, const double eps, const std::vector< std::function< RandomVariable(const std::vector< const RandomVariable * > &)> > &basisFn)
void backwardDerivatives(const ComputationGraph &g, const std::vector< T > &values, std::vector< T > &derivatives, const std::vector< std::function< std::vector< T >(const std::vector< const T * > &, const T *)> > &grad, std::function< void(T &)> deleter={}, const std::vector< bool > &keepNodes={})
Size size(const ValueType &v)
std::size_t addModelParameter(ComputationGraph &g, std::vector< std::pair< std::size_t, std::function< double(void)> > > &m, const std::string &id, std::function< double(void)> f)
An NPV cube for storing NPVs resulting from risk factor shifts.
A Class to write ORE outputs to reports.
A cube implementation that stores the cube in memory.
holds a grid of NPVs for a list of trades under various scenarios
Class for streaming SensitivityRecords from a SensitivityCube.
static std::function< void(RandomVariable &)> deleter
Real at(const Size i) const
static constexpr std::size_t ConditionalExpectation
vector< string > curveConfigs
xva engine using cg infrastructure