73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 LOG(
"XvaEngineCG: started");
90 boost::timer::cpu_timer timer;
91
92
93
94 LOG(
"XvaEngineCG: build init market");
95
99
100 boost::timer::nanosecond_type timing1 = timer.elapsed().wall;
101
102
103
104 LOG(
"XvaEngineCG: build sim market");
105
106
107 simMarket_ = QuantLib::ext::make_shared<ore::analytics::ScenarioSimMarket>(
110
111 boost::timer::nanosecond_type timing2 = timer.elapsed().wall;
112
113
114
115 LOG(
"XvaEngineCG: build cam model builder");
116
117
118 camBuilder_ = QuantLib::ext::make_shared<CrossAssetModelBuilder>(
121 std::string(), SalvagingAlgorithm::Spectral, "xva engine cg - cam builder");
122
123
124
125 LOG(
"XvaEngineCG: build cam cg model");
126
127 QL_REQUIRE(
129 "XvaEngineCG: cam is required to use discretization 'Euler', please update simulation parameters accordingly.");
130
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;
138
139
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")));
143
144
147
148 Size timeStepsPerYear = 1;
149
150
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;
157
158
159
160 LOG(
"XvaEngineCG: build trades against global cam cg model");
161
162 auto edCopy = QuantLib::ext::make_shared<EngineData>(*
engineData_);
163 edCopy->globalParameters()["GenerateAdditionalResults"] = "false";
164 edCopy->globalParameters()["RunType"] = "NPV";
165 map<MarketContext, string> configurations;
169 auto factory =
171 EngineBuilderFactory::instance().generateAmcCgEngineBuilders(
173 true);
174
175 portfolio_->build(factory,
"xva engine cg",
true);
176
177 boost::timer::nanosecond_type timing4 = timer.elapsed().wall;
178
179
180
181
182 LOG(
"XvaEngineCG: build computation graph for all trades");
183
184 std::vector<std::vector<std::size_t>> amcNpvNodes;
185
186 auto g =
model_->computationGraph();
187
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.");
194 g->startRedBlock();
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))));
200 }
201 amcNpvNodes.push_back(tmp);
202 g->endRedBlock();
203 }
204
205 boost::timer::nanosecond_type timing5 = timer.elapsed().wall;
206
207
208
209
210
211
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]);
217 }
218 pfPathExposureNodes.push_back(sumNode);
219 pfExposureNodes.push_back(
220 model_->npv(sumNode, i == 0 ?
model_->referenceDate() : *std::next(simulationDates.begin(), i - 1),
222 }
223
224 boost::timer::nanosecond_type timing6 = timer.elapsed().wall;
225
226
227
228
229
230
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); });
241 }
242
243 boost::timer::nanosecond_type timing7 = timer.elapsed().wall;
244
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;
251 }
252
253
254
255 std::vector<RandomVariable> values(g->size(), RandomVariable(
model_->size(), 0.0));
256 std::vector<RandomVariable> derivatives(g->size(), RandomVariable(
model_->size(), 0.0));
257
258
259
261 boost::timer::nanosecond_type timing8 = timer.elapsed().wall;
262
263
264
268 boost::timer::nanosecond_type timing9 = timer.elapsed().wall;
269
270 std::size_t rvMemMax = numberOfStochasticRvs(values) + numberOfStochasticRvs(derivatives);
271
272
273
274
275
276
277
278
279
280 LOG(
"XvaEngineCG: do forward evaluation");
281
282 Real eps = 0.0;
284 Null<Real>());
287
288 std::vector<bool> keepNodes(g->size(), false);
289
290 for (auto const& c : g->constants()) {
291 keepNodes[c.second] = true;
292 }
293
295 keepNodes[n] = true;
296 }
297
298 for (auto const n : g->redBlockDependencies()) {
299 keepNodes[n] = true;
300 }
301
303 for (
auto const& rv :
model_->randomVariates())
304 for (auto const& v : rv)
305 keepNodes[v] = true;
306 }
307
308 for (auto const& n : pfExposureNodes) {
309 keepNodes[n] = true;
310 }
311
313
314 boost::timer::nanosecond_type timing10 = timer.elapsed().wall;
315
316
317
318 {
319 epeReport_ = QuantLib::ext::make_shared<InMemoryReport>();
320 epeReport_->addColumn(
"Date", Date()).addColumn(
"EPE",
double(), 4).addColumn(
"ENE",
double(), 4);
321
322 for (Size i = 0; i < simulationDates.size() + 1; ++i) {
324 epeReport_->add(i == 0 ?
model_->referenceDate() : *std::next(simulationDates.begin(), i - 1))
327 }
329 epeReport_->toFile(
"Output/xvacg-exposure.csv");
330 }
331
333 LOG(
"XvaEngineCG: Calcuated CVA (node " << cvaNode <<
") = " << cva);
334
335 rvMemMax = std::max(rvMemMax, numberOfStochasticRvs(values) + numberOfStochasticRvs(derivatives));
336
337
338
339 boost::timer::nanosecond_type timing11 = timer.elapsed().wall;
340
342
344
345 LOG(
"XvaEngineCG: run backward derivatives");
346
347 derivatives[cvaNode] = RandomVariable(
model_->size(), 1.0);
348
349 std::vector<bool> keepNodesDerivatives(g->size(), false);
350
352 keepNodesDerivatives[n] = true;
353
354
355
359
360
361
362 Size i = 0;
364 modelParamDerivatives[i++] =
expectation(derivatives[n]).
at(0);
365 }
366
367
368
369 rvMemMax = std::max(rvMemMax, numberOfStochasticRvs(values) + numberOfStochasticRvs(derivatives));
370
371 LOG(
"XvaEngineCG: got " << modelParamDerivatives.size()
372 << " model parameter derivatives from run backward derivatives");
373
374 timing11 = timer.elapsed().wall;
375
376
377
378
379 values.clear();
380 derivatives.clear();
381 }
382
383
384
385 LOG(
"XvaEngineCG: running sensi scenarios");
386
389 QuantLib::ext::make_shared<DeltaScenarioFactory>(
simMarket_->baseScenario()),
false, std::string(), continueOnError,
391
393
394 auto resultCube = QuantLib::ext::make_shared<DoublePrecisionSensiCube>(std::set<std::string>{
"CVA"},
asof_,
396 resultCube->setT0(cva, 0, 0);
397
398 model_->alwaysForwardNotifications();
399
400 Size activeScenarios = 0;
401 for (Size sample = 0; sample < resultCube->samples(); ++sample) {
402
403
404
408
409
410
412
413 Real sensi = 0.0;
414
415
416
417 if (!
model_->isCalculated()) {
418
420 ++activeScenarios;
421
423
424
425
426 auto modelParameters =
model_->modelParameters();
427 Size i = 0;
428 boost::accumulators::accumulator_set<
429 double, boost::accumulators::stats<boost::accumulators::tag::weighted_sum>, double>
430 acc;
432 Real v1 = modelParameters[i].second;
433 acc(modelParamDerivatives[i], boost::accumulators::weight = (v1 - v0));
434 ++i;
435 }
436 sensi = boost::accumulators::weighted_sum(acc);
437
438 } else {
439
440
441
445
446 }
447 }
448
449
450
451 resultCube->set(cva + sensi, 0, 0, sample, 0);
452 }
453
454 boost::timer::nanosecond_type timing12 = timer.elapsed().wall;
455
456 LOG(
"XvaEngineCG: finished running " << resultCube->samples() <<
" sensi scenarios, thereof " << activeScenarios
457 << " active.");
458
459
460
461 {
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());
467 ReportWriter().writeScenarioReport(*
sensiReport_, {sensiCube}, 0.0);
468 sensiReport_->toFile(
"Output/xvacg-cva-sensi-scenario.csv");
469 }
470
471
472
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()
478 << " MB");
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
481 << " ms");
482 LOG(
"XvaEngineCG: Part A CG build : " << std::fixed << std::setprecision(1) << (timing3 - timing2) / 1E6
483 << " ms");
484 LOG(
"XvaEngineCG: Portfolio build : " << std::fixed << std::setprecision(1) << (timing4 - timing3) / 1E6
485 << " ms");
486 LOG(
"XvaEngineCG: Part B CG build : " << std::fixed << std::setprecision(1) << (timing5 - timing4) / 1E6
487 << " ms");
488 LOG(
"XvaEngineCG: Part C CG build : " << std::fixed << std::setprecision(1) << (timing6 - timing5) / 1E6
489 << " ms");
490 LOG(
"XvaEngineCG: Part D CG build : " << std::fixed << std::setprecision(1) << (timing7 - timing6) / 1E6
491 << " ms");
492 LOG(
"XvaEngineCG: RV gen : " << std::fixed << std::setprecision(1) << (timing8 - timing7) / 1E6
493 << " ms");
494 LOG(
"XvaEngineCG: Const and Model params : " << std::fixed << std::setprecision(1) << (timing9 - timing8) / 1E6
495 << " ms");
496 LOG(
"XvaEngineCG: Forward eval : " << std::fixed << std::setprecision(1) << (timing10 - timing9) / 1E6
497 << " ms");
498 LOG(
"XvaEngineCG: Backward deriv : " << std::fixed << std::setprecision(1) << (timing11 - timing10) / 1E6
499 << " ms");
500 LOG(
"XvaEngineCG: Sensi Cube Gen : " << std::fixed << std::setprecision(1) << (timing12 - timing11) / 1E6
501 << " ms");
502 LOG(
"XvaEngineCG: total : " << std::fixed << std::setprecision(1) << timing12 / 1E6 <<
" ms");
503}
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_
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_
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()
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={})
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)
static std::function< void(RandomVariable &)> deleter
Real at(const Size i) const
static constexpr std::size_t ConditionalExpectation