Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
marketriskreport.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2024 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
24#include <orea/cube/npvcube.hpp>
27#include <ql/math/matrixutilities/pseudosqrt.hpp>
28#include <ql/math/matrixutilities/symmetricschurdecomposition.hpp>
29#include <boost/regex.hpp>
30
31using namespace QuantLib;
32using namespace ore::data;
33
34namespace ore {
35namespace analytics {
36using std::map;
37
38std::ostream& operator<<(std::ostream& out, const ext::shared_ptr<MarketRiskGroupBase>& riskGroup) {
39 return out << riskGroup->to_string();
40}
41
42std::ostream& operator<<(std::ostream& out, const ext::shared_ptr<TradeGroupBase>& tradeGroup) {
43 return out << tradeGroup->to_string();
44}
45
48}
49
51 return riskClass_ == MarketRiskConfiguration::RiskClass::All && riskType_ == MarketRiskConfiguration::RiskType::All;
52}
53
54map<MarketRiskConfiguration::RiskClass, Size> MarketRiskGroupContainer::CompRisk::rcOrder = {
55 {MarketRiskConfiguration::RiskClass::All, 0}, {MarketRiskConfiguration::RiskClass::InterestRate, 1},
56 {MarketRiskConfiguration::RiskClass::Inflation, 2}, {MarketRiskConfiguration::RiskClass::Credit, 3},
57 {MarketRiskConfiguration::RiskClass::Equity, 4}, {MarketRiskConfiguration::RiskClass::FX, 5}};
58
59map<MarketRiskConfiguration::RiskType, Size> MarketRiskGroupContainer::CompRisk::rtOrder = {
60 {MarketRiskConfiguration::RiskType::All, 0},
61 {MarketRiskConfiguration::RiskType::DeltaGamma, 1},
62 {MarketRiskConfiguration::RiskType::Vega, 2},
63 {MarketRiskConfiguration::RiskType::BaseCorrelation, 3}};
64
65bool MarketRiskGroupContainer::CompRisk::operator()(const QuantLib::ext::shared_ptr<MarketRiskGroup>& lhs,
66 const QuantLib::ext::shared_ptr<MarketRiskGroup>& rhs) const {
67
68 if (rcOrder.at(lhs->riskClass()) < rcOrder.at(rhs->riskClass()))
69 return true;
70 if (rcOrder.at(lhs->riskClass()) > rcOrder.at(rhs->riskClass()))
71 return false;
72
73 if (rtOrder.at(lhs->riskType()) < rtOrder.at(rhs->riskType()))
74 return true;
75 if (rtOrder.at(lhs->riskType()) > rtOrder.at(rhs->riskType()))
76 return false;
77
78 return false;
79}
80
82
83QuantLib::Size MarketRiskGroupContainer::size() { return riskGroups_.size(); }
84
85QuantLib::ext::shared_ptr<MarketRiskGroupBase> MarketRiskGroupContainer::next() {
86 if (rgIdx_ == riskGroups_.end())
87 return nullptr;
88 auto rg = *rgIdx_;
89 ++rgIdx_;
90 return rg;
91}
92
93void MarketRiskGroupContainer::add(const QuantLib::ext::shared_ptr<MarketRiskGroupBase>& riskGroup) {
94 auto rg = QuantLib::ext::dynamic_pointer_cast<MarketRiskGroup>(riskGroup);
95 QL_REQUIRE(rg, "riskGroup must be of type MarketRiskGroup");
96 riskGroups_.insert(rg);
97}
98
99void TradeGroupContainer::reset() { tgIdx_ = tradeGroups_.begin(); }
100
101QuantLib::ext::shared_ptr<TradeGroupBase> TradeGroupContainer::next() {
102 if (tgIdx_ == tradeGroups_.end())
103 return nullptr;
104 auto tg = *tgIdx_;
105 ++tgIdx_;
106 return tg;
107}
108
109void TradeGroupContainer::add(const QuantLib::ext::shared_ptr<TradeGroupBase>& tradeGroup) {
110 auto tg = QuantLib::ext::dynamic_pointer_cast<TradeGroup>(tradeGroup);
111 QL_REQUIRE(tg, "tradeGroup must be of type VarTradeGroup");
112 tradeGroups_.insert(tg);
113}
114
115
117
118 if (multiThreadArgs_ && fullRevalArgs_ && !fullRevalArgs_->simMarket_)
119 initSimMarket();
120
121 // set run type in engine data, make a copy of this before
122 if (fullRevalArgs_ && fullRevalArgs_->engineData_) {
123 fullRevalArgs_->engineData_ = QuantLib::ext::make_shared<EngineData>(*fullRevalArgs_->engineData_);
124 fullRevalArgs_->engineData_->globalParameters()["RunType"] = std::string("HistoricalPnL");
125 }
126
127 // save a sensi pnl calculator
128 if (hisScenGen_) {
129 // Build the filtered historical scenario generator
130 hisScenGen_ = QuantLib::ext::make_shared<HistoricalScenarioGeneratorWithFilteredDates>(
131 timePeriods(), hisScenGen_);
132
133 if (fullRevalArgs_ && fullRevalArgs_->simMarket_)
134 hisScenGen_->baseScenario() = fullRevalArgs_->simMarket_->baseScenario();
135 }
136
137 if (sensiArgs_ && hisScenGen_)
138 sensiPnlCalculator_ =
139 ext::make_shared<HistoricalSensiPnlCalculator>(hisScenGen_, sensiArgs_->sensitivityStream_);
140
141 if (fullRevalArgs_) {
142 LOG("Build the portfolio for full reval bt.");
143
144 if (!multiThreadArgs_) {
145 factory_ = QuantLib::ext::make_shared<EngineFactory>(fullRevalArgs_->engineData_, fullRevalArgs_->simMarket_,
146 map<MarketContext, string>(), fullRevalArgs_->referenceData_,
147 fullRevalArgs_->iborFallbackConfig_);
148
149 DLOG("Building the portfolio");
150 portfolio_->build(factory_, "historical pnl generation");
151 DLOG("Portfolio built");
152
153 LOG("Creating the historical P&L generator (dryRun=" << std::boolalpha << fullRevalArgs_->dryRun_ << ")");
154 ext::shared_ptr<NPVCube> cube = ext::make_shared<DoublePrecisionInMemoryCube>(
155 fullRevalArgs_->simMarket_->asofDate(), portfolio_->ids(),
156 vector<Date>(1, fullRevalArgs_->simMarket_->asofDate()), hisScenGen_->numScenarios());
157
158 histPnlGen_ = ext::make_shared<HistoricalPnlGenerator>(
159 calculationCurrency_, portfolio_, fullRevalArgs_->simMarket_,
160 hisScenGen_, cube, factory_->modelBuilders(), fullRevalArgs_->dryRun_);
161 } else {
162 histPnlGen_ = ext::make_shared<HistoricalPnlGenerator>(
163 calculationCurrency_, portfolio_, hisScenGen_,
164 fullRevalArgs_->engineData_,
165 multiThreadArgs_->nThreads_, multiThreadArgs_->today_, multiThreadArgs_->loader_,
166 multiThreadArgs_->curveConfigs_, multiThreadArgs_->todaysMarketParams_,
167 multiThreadArgs_->configuration_, multiThreadArgs_->simMarketData_, fullRevalArgs_->referenceData_,
168 fullRevalArgs_->iborFallbackConfig_, fullRevalArgs_->dryRun_, multiThreadArgs_->context_);
169 }
170 }
171
172 initialiseRiskGroups();
173}
174
176 riskGroups_ = QuantLib::ext::make_shared<MarketRiskGroupContainer>();
177 tradeGroups_ = QuantLib::ext::make_shared<TradeGroupContainer>();
178
179 // build portfolio filter, if given
180 bool hasFilter = false;
181 boost::regex filter;
182 if (portfolioFilter_ != "") {
183 hasFilter = true;
184 filter = boost::regex(portfolioFilter_);
185 LOG("Portfolio filter: " << portfolioFilter_);
186 }
187
188 Size pos = 0;
189 string allStr = "All";
190 auto tradeGroup = QuantLib::ext::make_shared<TradeGroup>(allStr);
191 tradeGroups_->add(tradeGroup);
192
193 QL_REQUIRE(portfolio_, "No portfolio given");
194 for (const auto& pId : portfolio_->portfolioIds()) {
195 if (breakdown_ && (!hasFilter || boost::regex_match(pId, filter))) {
196 auto tradeGroupP = QuantLib::ext::make_shared<TradeGroup>(pId);
197 tradeGroups_->add(tradeGroupP);
198 }
199 }
200
201 for (auto const& [tradeId, trade] : portfolio_->trades()) {
202 if (!hasFilter && trade->portfolioIds().size() == 0)
203 tradeIdGroups_[allStr].insert(make_pair(tradeId, pos));
204 else {
205 for (auto const& pId : trade->portfolioIds()) {
206 if (!hasFilter || boost::regex_match(pId, filter)) {
207 tradeIdGroups_[allStr].insert(make_pair(tradeId, pos));
208 if (breakdown_)
209 tradeIdGroups_[pId].insert(make_pair(tradeId, pos));
210 }
211 }
212 }
213 pos++;
214 }
215
216 // Create the Var risk groups, pairs of risk class/type
217 for (auto rc : MarketRiskConfiguration::riskClasses(true)) {
218 for (auto rt : MarketRiskConfiguration::riskTypes(true)) {
219 auto riskGroup = QuantLib::ext::make_shared<MarketRiskGroup>(rc, rt);
220 riskGroups_->add(riskGroup);
221 }
222 }
223 riskGroups_->reset();
224 tradeGroups_->reset();
225}
226
228 QL_REQUIRE(multiThreadArgs_ && fullRevalArgs_, "MarketRiskBacktest: must be a multithreaded run");
229
230 // called from the ctors that do not take a sim market as an input (the multithreaded ctors)
231 auto initMarket = QuantLib::ext::make_shared<TodaysMarket>(
232 multiThreadArgs_->today_, multiThreadArgs_->todaysMarketParams_, multiThreadArgs_->loader_,
233 multiThreadArgs_->curveConfigs_, true, true, false, fullRevalArgs_->referenceData_, false,
234 fullRevalArgs_->iborFallbackConfig_);
235
236 fullRevalArgs_->simMarket_ = QuantLib::ext::make_shared<ore::analytics::ScenarioSimMarket>(
237 initMarket, multiThreadArgs_->simMarketData_, multiThreadArgs_->configuration_,
238 *multiThreadArgs_->curveConfigs_, *multiThreadArgs_->todaysMarketParams_, true, false, false, false,
239 fullRevalArgs_->iborFallbackConfig_);
240}
241
243 if (histPnlGen_) {
244 histPnlGen_->unregisterAllProgressIndicators();
245 for (auto const& i : this->progressIndicators())
246 histPnlGen_->registerProgressIndicator(i);
247 }
248}
249
250void MarketRiskReport::calculate(const ext::shared_ptr<MarketRiskReport::Reports>& reports) {
251 initialise();
252 registerProgressIndicators();
253
254 LOG("Creating reports");
255 createReports(reports);
256
257 // Cube to store Sensi Shifts and vector of keys used in cube, per portfolio
258 map<string, QuantLib::ext::shared_ptr<NPVCube>> sensiShiftCube;
259 ext::shared_ptr<SensitivityAggregator> sensiAgg;
260 if (sensiBased_)
261 // Create a sensitivity aggregator. Will be used if running sensi-based backtest.
262 sensiAgg = ext::make_shared<SensitivityAggregator>(tradeIdGroups_);
263
264 bool runDetailTrd = runTradeDetail(reports);
265 addPnlCalculators(reports);
266
267 // Loop over all the risk groups
268 riskGroups_->reset();
269 Size currentRiskGroup = 0;
270 while (ext::shared_ptr<MarketRiskGroupBase> riskGroup = riskGroups_->next()) {
271 LOG("[progress] Processing RiskGroup " << ++currentRiskGroup << " out of " << riskGroups_->size()
272 << ") = " << riskGroup);
273
274 ext::shared_ptr<ScenarioFilter> filter = createScenarioFilter(riskGroup);
275
276 // If this filter disables all risk factors, move to next risk group
277 if (disablesAll(filter))
278 continue;
279
280 updateFilter(riskGroup, filter);
281
282 if (sensiBased_)
283 sensiAgg->aggregate(*sensiArgs_->sensitivityStream_, filter);
284
285 // If doing a full revaluation backtest, generate the cube under this filter
286 if (fullReval_) {
287 if (generateCube(riskGroup)) {
288 histPnlGen_->generateCube(filter);
289 if (fullRevalArgs_->writeCube_) {
290 CubeWriter writer(cubeFilePath(riskGroup));
291 writer.write(histPnlGen_->cube(), {});
292 }
293 }
294 }
295
296 bool runSensiBased = sensiBased_;
297
298 // loop over all the trade groups
299 tradeGroups_->reset();
300 while (ext::shared_ptr<TradeGroupBase> tradeGroup = tradeGroups_->next()) {
301 reset(riskGroup);
302
303 // Only look at this trade group if there required
304 if (!runTradeRiskGroup(tradeGroup, riskGroup))
305 continue;
306
307 MEM_LOG;
308 LOG("Start processing for RiskGroup: " << riskGroup << ", TradeGroup: " << tradeGroup);
309
310 writePnl_ = tradeGroup->allLevel() && riskGroup->allLevel();
311 tradeIdIdxPairs_ = tradeIdGroups_.at(tradeGroupKey(tradeGroup));
312
313 // populate the tradeIds
314 transform(tradeIdIdxPairs_.begin(), tradeIdIdxPairs_.end(), back_inserter(tradeIds_),
315 [](const pair<string, Size>& elem) { return elem.first; });
316
317 set<SensitivityRecord> srs;
318
319 if (runSensiBased && sensiAgg) {
320 map<string, QuantLib::ext::shared_ptr<NPVCube>>::iterator scube;
321
322 auto tradeGpId = tradeGroupKey(tradeGroup);
323 srs = sensiAgg->sensitivities(tradeGpId);
324
325 // Populate the deltas and gammas for a parametric VAR benchmark calculation
326 sensiAgg->generateDeltaGamma(tradeGpId, deltas_, gammas_);
327 vector<RiskFactorKey> deltaKeys;
328 for (const auto& [rfk, _] : deltas_)
329 deltaKeys.push_back(rfk);
330
331 string portfolio = portfolioId(tradeGroup);
332
333 // riskGroups_ and tradeGroups_ are ordered so we should always run
334 // [Product Class, Risk Class, Margin Type] = [All, All, All] first.
335 // This populates every possible scenario shift to a cube for quicker
336 // generation of Sensi PnLs.
337 scube = sensiShiftCube.find(portfolio);
338 if (sensiArgs_->shiftCalculator_) {
339 if (scube == sensiShiftCube.end()) {
340 DLOG("Populating Sensi Shifts for portfolio '" << portfolio << "'");
341
342 // if we have no senis for this run we skip this, and set runSensiBased to false
343 if (srs.size() == 0) {
344 ALOG("No senitivities found for RiskGroup = "
345 << riskGroup << " and tradeGroup " << tradeGroup << "; Skipping Sensi based PnL.");
346 runSensiBased = false;
347 } else {
348 scube = sensiShiftCube.insert({portfolio, QuantLib::ext::shared_ptr<NPVCube>()}).first;
349 vector<RiskFactorKey> riskFactorKeys;
350 transform(deltas_.begin(), deltas_.end(), back_inserter(riskFactorKeys),
351 [](const pair<RiskFactorKey, Real>& kv) { return kv.first; });
352 sensiPnlCalculator_->populateSensiShifts(scube->second, riskFactorKeys,
353 sensiArgs_->shiftCalculator_);
354 }
355 }
356 }
357
358 if (runSensiBased) {
359 ext::shared_ptr<CovarianceCalculator> covCalculator;
360 // if a covariance matrix has been provided as an input we use that
361 if (sensiArgs_->covarianceInput_.size() > 0) {
362 std::vector<bool> sensiKeyHasNonZeroVariance(deltaKeys.size(), false);
363
364 // build global covariance matrix
365 covarianceMatrix_ = Matrix(deltaKeys.size(), deltaKeys.size(), 0.0);
366 Size unusedCovariance = 0;
367 for (const auto& c : sensiArgs_->covarianceInput_) {
368 auto k1 = std::find(deltaKeys.begin(), deltaKeys.end(), c.first.first);
369 auto k2 = std::find(deltaKeys.begin(), deltaKeys.end(), c.first.second);
370 if (k1 != deltaKeys.end() && k2 != deltaKeys.end()) {
371 covarianceMatrix_(k1 - deltaKeys.begin(), k2 - deltaKeys.begin()) = c.second;
372 if (k1 == k2)
373 sensiKeyHasNonZeroVariance[k1 - deltaKeys.begin()] = true;
374 } else
375 ++unusedCovariance;
376 }
377 DLOG("Found " << sensiArgs_->covarianceInput_.size() << " covariance matrix entries, "
378 << unusedCovariance
379 << " do not match a portfolio sensitivity and will not be used.");
380 for (Size i = 0; i < sensiKeyHasNonZeroVariance.size(); ++i) {
381 if (!sensiKeyHasNonZeroVariance[i])
382 WLOG("Zero variance assigned to sensitivity key " << deltaKeys[i]);
383 }
384
385 // make covariance matrix positive semi-definite
386 DLOG("Covariance matrix has dimension " << deltaKeys.size() << " x " << deltaKeys.size());
387 if (salvage_ && !covarianceMatrix_.empty()) {
388 DLOG("Covariance matrix is not salvaged, check for positive semi-definiteness");
389 SymmetricSchurDecomposition ssd(covarianceMatrix_);
390 Real evMin = ssd.eigenvalues().back();
391 QL_REQUIRE(
392 evMin > 0.0 || close_enough(evMin, 0.0),
393 "ParametricVar: input covariance matrix is not positive semi-definite, smallest "
394 "eigenvalue is "
395 << evMin);
396 DLOG("Smallest eigenvalue is " << evMin);
397 salvage_ = QuantLib::ext::make_shared<QuantExt::NoCovarianceSalvage>();
398 }
399 } else
400 covCalculator = ext::make_shared<CovarianceCalculator>(covariancePeriod());
401
402 includeDeltaMargin_ = includeDeltaMargin(riskGroup);
403 includeGammaMargin_ = includeGammaMargin(riskGroup);
404 // bool haveDetailTrd = btArgs_->reports_.count(ReportType::DetailTrade) == 1;
405
406 if (covCalculator || pnlCalculators_.size() > 0) {
407 sensiPnlCalculator_->calculateSensiPnl(srs, deltaKeys, scube->second, pnlCalculators_,
408 covCalculator, tradeIds_, includeGammaMargin_,
409 includeDeltaMargin_, runDetailTrd);
410
411 covarianceMatrix_ = covCalculator->covariance();
412 }
413 handleSensiResults(reports, riskGroup, tradeGroup);
414 }
415 }
416 // Do the full revaluation step
417 if (runFullReval(riskGroup))
418 handleFullRevalResults(reports, riskGroup, tradeGroup);
419
420 writeReports(reports, riskGroup, tradeGroup);
421 }
422 if (sensiBased_)
423 // Reset the sensitivity aggregator before changing the risk filter
424 sensiAgg->reset();
425 }
426 closeReports(reports);
427}
428
429void MarketRiskReport::enableCubeWrite(const string& cubeDir, const string& cubeFilename) {
430 QL_REQUIRE(boost::find_first(cubeFilename, "FILTER"),
431 "cube file name '" << cubeFilename << "' must contain 'FILTER'");
432 fullRevalArgs_->writeCube_ = true;
433 fullRevalArgs_->cubeDir_ = cubeDir;
434 fullRevalArgs_->cubeFilename_ = cubeFilename;
435}
436
437void MarketRiskReport::reset(const ext::shared_ptr<MarketRiskGroupBase>& riskGroup) {
438 deltas_.clear();
439 gammas_.clear();
440 covarianceMatrix_ = Matrix();
441 tradeIdIdxPairs_.clear();
442 tradeIds_.clear();
443 for (const auto& p : pnlCalculators_)
444 p->clear();
445}
446
447void MarketRiskReport::closeReports(const ext::shared_ptr<MarketRiskReport::Reports>& reports) {
448 for (const auto& r : reports->reports())
449 r->end();
450}
451
452QuantLib::ext::shared_ptr<ore::analytics::ScenarioFilter>
453MarketRiskReport::createScenarioFilter(const QuantLib::ext::shared_ptr<MarketRiskGroupBase>& riskGroup) {
454 auto rg = QuantLib::ext::dynamic_pointer_cast<MarketRiskGroup>(riskGroup);
455 QL_REQUIRE(rg, "riskGroup must be of type MarketRiskGroup");
456 return QuantLib::ext::make_shared<RiskFilter>(rg->riskClass(), rg->riskType());
457}
458
459string MarketRiskReport::portfolioId(const QuantLib::ext::shared_ptr<TradeGroupBase>& tradeGroup) const {
460 auto vtg = QuantLib::ext::dynamic_pointer_cast<TradeGroup>(tradeGroup);
461 QL_REQUIRE(vtg, "TradeGroup of type TradeGroup required");
462 return vtg->portfolioId();
463}
464
465string MarketRiskReport::tradeGroupKey(const QuantLib::ext::shared_ptr<TradeGroupBase>& tradeGroup) const {
466 return portfolioId(tradeGroup);
467}
468
469} // namespace analytics
470} // namespace ore
Write an NPV cube to a human readable text file.
Definition: cubewriter.hpp:37
void write(const QuantLib::ext::shared_ptr< NPVCube > &cube, const std::map< std::string, std::string > &nettingSetMap, bool append=false)
Write a cube out to file.
Definition: cubewriter.cpp:33
static std::set< RiskClass > riskClasses(bool includeAll=false)
Give back a set containing the RiskClass values optionally excluding 'All'.
Definition: riskfilter.cpp:83
static std::set< RiskType > riskTypes(bool includeAll=false)
Give back a set containing the RiskType values optionally excluding 'All'.
Definition: riskfilter.cpp:95
std::set< QuantLib::ext::shared_ptr< MarketRiskGroup > >::const_iterator rgIdx_
QuantLib::ext::shared_ptr< MarketRiskGroupBase > next() override
void add(const QuantLib::ext::shared_ptr< MarketRiskGroupBase > &riskGroup) override
std::set< QuantLib::ext::shared_ptr< MarketRiskGroup >, CompRisk > riskGroups_
MarketRiskConfiguration::RiskType riskType_
std::string to_string() override
MarketRiskConfiguration::RiskClass riskClass_
void enableCubeWrite(const std::string &cubeDir, const std::string &cubeFilename)
virtual void closeReports(const QuantLib::ext::shared_ptr< MarketRiskReport::Reports > &reports)
virtual void calculate(const QuantLib::ext::shared_ptr< Reports > &report)
virtual std::string tradeGroupKey(const QuantLib::ext::shared_ptr< TradeGroupBase > &tradeGroup) const
void initSimMarket()
Method to init simMarket_ for multi-threaded ctors.
virtual QuantLib::ext::shared_ptr< ScenarioFilter > createScenarioFilter(const QuantLib::ext::shared_ptr< MarketRiskGroupBase > &riskGroup)
virtual std::string portfolioId(const QuantLib::ext::shared_ptr< TradeGroupBase > &tradeGroup) const
virtual void initialiseRiskGroups()
Method for shared initialisation.
virtual void reset(const QuantLib::ext::shared_ptr< MarketRiskGroupBase > &riskGroup)
void add(const QuantLib::ext::shared_ptr< TradeGroupBase > &tradeGroup) override
QuantLib::ext::shared_ptr< TradeGroupBase > next() override
SafeStack< Filter > filter
A Class to write a cube out to file.
A cube implementation that stores the cube in memory.
#define MEM_LOG
#define LOG(text)
#define DLOG(text)
#define ALOG(text)
#define WLOG(text)
Base class for a market risk report.
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
std::string to_string(const LocationInfo &l)
The base NPV cube class.
Class for aggregating SensitivityRecords.
bool operator()(const QuantLib::ext::shared_ptr< MarketRiskGroup > &lhs, const QuantLib::ext::shared_ptr< MarketRiskGroup > &rhs) const
static std::map< MarketRiskConfiguration::RiskClass, QuantLib::Size > rcOrder
static std::map< MarketRiskConfiguration::RiskType, QuantLib::Size > rtOrder