Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
sensitivityanalysis.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 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
27
33
34#include <ql/errors.hpp>
35#include <ql/math/comparison.hpp>
36
37using namespace QuantLib;
38using namespace QuantExt;
39using namespace std;
40using namespace ore::data;
41
42namespace ore {
43namespace analytics {
44
46 const QuantLib::ext::shared_ptr<ore::data::Portfolio>& portfolio, const QuantLib::ext::shared_ptr<ore::data::Market>& market,
47 const string& marketConfiguration, const QuantLib::ext::shared_ptr<ore::data::EngineData>& engineData,
48 const QuantLib::ext::shared_ptr<ScenarioSimMarketParameters>& simMarketData,
49 const QuantLib::ext::shared_ptr<SensitivityScenarioData>& sensitivityData, const bool recalibrateModels,
50 const QuantLib::ext::shared_ptr<ore::data::CurveConfigurations>& curveConfigs,
51 const QuantLib::ext::shared_ptr<ore::data::TodaysMarketParameters>& todaysMarketParams,
52 const bool nonShiftedBaseCurrencyConversion, const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceData,
53 const IborFallbackConfig& iborFallbackConfig, const bool continueOnError, bool dryRun)
54 : market_(market), marketConfiguration_(marketConfiguration), asof_(market ? market->asofDate() : Date()),
55 simMarketData_(simMarketData), sensitivityData_(sensitivityData), recalibrateModels_(recalibrateModels),
56 curveConfigs_(curveConfigs), todaysMarketParams_(todaysMarketParams), overrideTenors_(false),
57 nonShiftedBaseCurrencyConversion_(nonShiftedBaseCurrencyConversion), referenceData_(referenceData),
58 iborFallbackConfig_(iborFallbackConfig), continueOnError_(continueOnError), engineData_(engineData),
59 portfolio_(portfolio), dryRun_(dryRun), useSingleThreadedEngine_(true) {}
60
62 const Size nThreads, const Date& asof, const QuantLib::ext::shared_ptr<ore::data::Loader>& loader,
63 const QuantLib::ext::shared_ptr<ore::data::Portfolio>& portfolio, const string& marketConfiguration,
64 const QuantLib::ext::shared_ptr<ore::data::EngineData>& engineData,
65 const QuantLib::ext::shared_ptr<ore::analytics::ScenarioSimMarketParameters>& simMarketData,
66 const QuantLib::ext::shared_ptr<ore::analytics::SensitivityScenarioData>& sensitivityData, const bool recalibrateModels,
67 const QuantLib::ext::shared_ptr<ore::data::CurveConfigurations>& curveConfigs,
68 const QuantLib::ext::shared_ptr<ore::data::TodaysMarketParameters>& todaysMarketParams,
69 const bool nonShiftedBaseCurrencyConversion, const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceData,
70 const IborFallbackConfig& iborFallbackConfig, const bool continueOnError, bool dryRun, const std::string& context)
71 : marketConfiguration_(marketConfiguration), asof_(asof), simMarketData_(simMarketData),
72 sensitivityData_(sensitivityData), recalibrateModels_(recalibrateModels), curveConfigs_(curveConfigs),
73 todaysMarketParams_(todaysMarketParams), overrideTenors_(false),
74 nonShiftedBaseCurrencyConversion_(nonShiftedBaseCurrencyConversion), referenceData_(referenceData),
75 iborFallbackConfig_(iborFallbackConfig), continueOnError_(continueOnError), engineData_(engineData),
76 portfolio_(portfolio), dryRun_(dryRun), useSingleThreadedEngine_(false), nThreads_(nThreads), loader_(loader),
77 context_(context) {}
78
79namespace {
80std::vector<std::pair<QuantLib::ext::shared_ptr<Portfolio>, QuantLib::ext::shared_ptr<SensitivityScenarioGenerator>>>
81splitPortfolioByScenarioGenerators(
82 const QuantLib::ext::shared_ptr<Portfolio>& portfolio, std::vector<std::string> ids,
83 const std::vector<QuantLib::ext::shared_ptr<SensitivityScenarioGenerator>>& scenarioGenerators) {
84 DLOG("Splitting portfolio by scenario generators for sensitivity run(s)");
85 std::vector<std::pair<QuantLib::ext::shared_ptr<Portfolio>, QuantLib::ext::shared_ptr<SensitivityScenarioGenerator>>> result;
86 for (auto const& gen : scenarioGenerators) {
87 result.push_back(std::make_pair(QuantLib::ext::make_shared<Portfolio>(), gen));
88 }
89 for (auto const& [id, t] : portfolio->trades()) {
90 if (auto f = std::find(ids.begin(), ids.end(), t->sensitivityTemplate()); f != ids.end()) {
91 result[std::distance(ids.begin(), f)].first->add(t);
92 TLOG("adding trade " << std::setw(35) << t->id() << " to sensi run for id " << std::setw(20)
93 << t->sensitivityTemplate() << ", trade sensi-template: " << std::setw(20)
94 << t->sensitivityTemplate());
95 } else {
96 result.front().first->add(t);
97 TLOG("adding trade " << std::setw(35) << t->id() << " to sensi run for id " << std::setw(20) << "<default>"
98 << ", trade sensi-template: " << std::setw(20) << t->sensitivityTemplate());
99 }
100 }
101 return result;
102}
103} // namespace
104
106
107 LOG("Sensitivity analysis started...");
108
110 "SensitivityAnalysis::generateSensitivities(): multi-threaded engine does not support non-shifted base "
111 "ccy conversion currently. This requires a small code extension. Contact Dev.");
112
113 // collect the sensi template ids that are used in the sensitivity config
114
115 std::set<std::string> sensiTemplateIdsFromSensiConfig = getShiftSpecKeys(*sensitivityData_);
116
117 // collect the sensi template ids that are relevant for the portfolio
118
119 std::set<std::string> sensiTemplateIdsFromPortfolio;
120 for (auto const& [_, t] : portfolio_->trades())
121 sensiTemplateIdsFromPortfolio.insert(t->sensitivityTemplate());
122
123 // take the intersection of pricing engine ids and add en empty id for the default config as the first element
124
125 std::vector<std::string> sensiTemplateIds{std::string()};
126 std::set_intersection(sensiTemplateIdsFromSensiConfig.begin(), sensiTemplateIdsFromSensiConfig.end(),
127 sensiTemplateIdsFromPortfolio.begin(), sensiTemplateIdsFromPortfolio.end(),
128 std::back_inserter(sensiTemplateIds));
129
130 LOG("Need to process "
131 << sensiTemplateIds.size() << " sensi scenario sets resulting from " << sensiTemplateIdsFromSensiConfig.size()
132 << " sensi templates in sensi config (different from default config) and "
133 << sensiTemplateIdsFromPortfolio.size()
134 << " sensi templates in portfolio (including default config, if configured in pe config for a trade)");
135
137
138 // handle single threaded sensi analysis
139
140 LOG("SensitivitiyAnalysis::generateSensitivities(): use single-threaded engine to generate sensi cube. Using "
141 "configuration '"
142 << marketConfiguration_ << "'");
143
144 simMarket_ = QuantLib::ext::make_shared<ScenarioSimMarket>(
149
150 std::vector<QuantLib::ext::shared_ptr<SensitivityScenarioGenerator>> scenarioGenerators(sensiTemplateIds.size());
151 for (Size i = 0; i < sensiTemplateIds.size(); ++i) {
152 scenarioGenerators[i] = QuantLib::ext::make_shared<SensitivityScenarioGenerator>(
154 QuantLib::ext::make_shared<DeltaScenarioFactory>(simMarket_->baseScenario()), overrideTenors_,
155 sensiTemplateIds[i], continueOnError_, simMarket_->baseScenarioAbsolute());
156 }
157 scenarioGenerator_ = scenarioGenerators.front();
158
159 map<MarketContext, string> configurations;
160 configurations[MarketContext::pricing] = marketConfiguration_;
161 auto ed = QuantLib::ext::make_shared<EngineData>(*engineData_);
162 ed->globalParameters()["RunType"] =
163 std::string("Sensitivity") + (sensitivityData_->computeGamma() ? "DeltaGamma" : "Delta");
164
165 QuantLib::ext::shared_ptr<DateGrid> dg = QuantLib::ext::make_shared<DateGrid>("1,0W", NullCalendar());
166 vector<QuantLib::ext::shared_ptr<ValuationCalculator>> calculators;
168 // use "original" FX rates to convert sensi to base currency
169 calculators.push_back(QuantLib::ext::make_shared<NPVCalculatorFXT0>(simMarketData_->baseCcy(), market_));
170 else
171 // use the scenario FX rate when converting sensi to base currency
172 calculators.push_back(QuantLib::ext::make_shared<NPVCalculator>(simMarketData_->baseCcy()));
173
174 sensiCubes_.clear();
175 for (auto const& [pf, scenGen] :
176 splitPortfolioByScenarioGenerators(portfolio_, sensiTemplateIds, scenarioGenerators)) {
177 if (pf->trades().empty())
178 continue;
179 LOG("Run Sensitivity Scenarios for " << pf->size() << " out of " << portfolio_->size() << " trades.");
180 QuantLib::ext::shared_ptr<NPVSensiCube> cube =
181 QuantLib::ext::make_shared<DoublePrecisionSensiCube>(pf->ids(), asof_, scenGen->samples());
182 simMarket_->scenarioGenerator() = scenGen;
183 auto factory =
184 QuantLib::ext::make_shared<EngineFactory>(ed, simMarket_, configurations, referenceData_, iborFallbackConfig_);
185 pf->reset();
186 pf->build(factory, "sensi analysis");
188 modelBuilders_ = factory->modelBuilders();
189 else
190 modelBuilders_.clear();
192 for (auto const& i : this->progressIndicators())
194 engine.buildCube(pf, cube, calculators, true, nullptr, nullptr, {}, dryRun_);
195
196 sensiCubes_.push_back(QuantLib::ext::make_shared<SensitivityCube>(cube, scenGen->scenarioDescriptions(),
197 scenarioGenerator_->shiftSizes(),
198 scenGen->shiftSizes(), scenGen->shiftSchemes()));
199 }
200 } else {
201
202 // handle request to use multi-threaded engine
203
204 LOG("SensitivitiyAnalysis::generateSensitivities(): use multi-threaded engine to generate sensi cube. Using "
205 "configuration '"
206 << marketConfiguration_ << "'");
207
208 market_ =
209 QuantLib::ext::make_shared<ore::data::TodaysMarket>(asof_, todaysMarketParams_, loader_, curveConfigs_, true, true,
210 false, referenceData_, false, iborFallbackConfig_, false);
211
212 simMarket_ = QuantLib::ext::make_shared<ScenarioSimMarket>(
216 sensitivityData_->useSpreadedTermStructures(), false, false, iborFallbackConfig_);
217
218 std::vector<QuantLib::ext::shared_ptr<SensitivityScenarioGenerator>> scenarioGenerators(sensiTemplateIds.size());
219 for (Size i = 0; i < sensiTemplateIds.size(); ++i) {
220 scenarioGenerators[i] = QuantLib::ext::make_shared<SensitivityScenarioGenerator>(
222 QuantLib::ext::make_shared<DeltaScenarioFactory>(simMarket_->baseScenario()), overrideTenors_,
223 sensiTemplateIds[i], continueOnError_, simMarket_->baseScenarioAbsolute());
224 }
225 scenarioGenerator_ = scenarioGenerators.front();
226
227 auto ed = QuantLib::ext::make_shared<EngineData>(*engineData_);
228 ed->globalParameters()["RunType"] =
229 std::string("Sensitivity") + (sensitivityData_->computeGamma() ? "DeltaGamma" : "Delta");
230
231 sensiCubes_.clear();
232 for (auto const& [pf, scenGen] :
233 splitPortfolioByScenarioGenerators(portfolio_, sensiTemplateIds, scenarioGenerators)) {
234 if (pf->trades().empty())
235 continue;
236
238 nThreads_, asof_, QuantLib::ext::make_shared<ore::analytics::DateGrid>(), scenGen->numScenarios(), loader_,
240 sensitivityData_->useSpreadedTermStructures(), false,
241 QuantLib::ext::make_shared<ore::analytics::ScenarioFilter>(), referenceData_, iborFallbackConfig_, true, true,
243 [](const QuantLib::Date& asof, const std::set<std::string>& ids, const std::vector<QuantLib::Date>&,
244 const QuantLib::Size samples) {
245 return QuantLib::ext::make_shared<ore::analytics::DoublePrecisionSensiCube>(ids, asof, samples);
246 },
247 {}, {}, context_);
248 for (auto const& i : this->progressIndicators())
250
251 auto baseCcy = simMarketData_->baseCcy();
252 engine.buildCube(
253 pf,
254 [&baseCcy]() -> std::vector<QuantLib::ext::shared_ptr<ValuationCalculator>> {
255 return {QuantLib::ext::make_shared<NPVCalculator>(baseCcy)};
256 },
257 {}, true, dryRun_);
258 std::vector<QuantLib::ext::shared_ptr<NPVSensiCube>> miniCubes;
259 for (auto const& c : engine.outputCubes()) {
260 miniCubes.push_back(QuantLib::ext::dynamic_pointer_cast<NPVSensiCube>(c));
261 QL_REQUIRE(
262 miniCubes.back() != nullptr,
263 "SensitivityAnalysis::generateSensitivities(): internal error, could not cast to NPVSensiCube.");
264 }
265 auto cube = QuantLib::ext::make_shared<JointNPVSensiCube>(miniCubes, pf->ids());
266
267 sensiCubes_.push_back(QuantLib::ext::make_shared<SensitivityCube>(cube, scenGen->scenarioDescriptions(),
268 scenarioGenerator_->shiftSizes(),
269 scenGen->shiftSizes(), scenGen->shiftSchemes()));
270 }
271 }
272
273 simMarket_->scenarioGenerator() = scenarioGenerator_;
274
275 LOG("Sensitivity analysis completed");
276}
277
278Real getShiftSize(const RiskFactorKey& key, const SensitivityScenarioData& sensiParams,
279 const QuantLib::ext::shared_ptr<ScenarioSimMarket>& simMarket, const string& marketConfiguration) {
280
281 Date asof = simMarket->asofDate();
282 RiskFactorKey::KeyType keytype = key.keytype;
283 string keylabel = key.name;
284 Real shiftSize = 0.0;
285 Real shiftMult = 1.0;
286 switch (keytype) {
288 auto itr = sensiParams.fxShiftData().find(keylabel);
289 QL_REQUIRE(itr != sensiParams.fxShiftData().end(), "shiftData not found for " << keylabel);
290 shiftSize = itr->second.shiftSize;
291 if (itr->second.shiftType == ShiftType::Relative) {
292 shiftMult = simMarket->fxRate(keylabel, marketConfiguration)->value();
293 }
294 } break;
296 auto itr = sensiParams.equityShiftData().find(keylabel);
297 QL_REQUIRE(itr != sensiParams.equityShiftData().end(), "shiftData not found for " << keylabel);
298 shiftSize = itr->second.shiftSize;
299 if (itr->second.shiftType == ShiftType::Relative) {
300 shiftMult = simMarket->equitySpot(keylabel, marketConfiguration)->value();
301 }
302 } break;
304 string ccy = keylabel;
305 auto itr = sensiParams.discountCurveShiftData().find(ccy);
306 QL_REQUIRE(itr != sensiParams.discountCurveShiftData().end(), "shiftData not found for " << ccy);
307 shiftSize = itr->second->shiftSize;
308 if (itr->second->shiftType == ShiftType::Relative) {
309 Size keyIdx = key.index;
310 Period p = itr->second->shiftTenors[keyIdx];
311 Handle<YieldTermStructure> yts = simMarket->discountCurve(ccy, marketConfiguration);
312 Time t = yts->dayCounter().yearFraction(asof, asof + p);
313 Real zeroRate = yts->zeroRate(t, Continuous);
314 shiftMult = zeroRate;
315 }
316 } break;
318 string idx = keylabel;
319 auto itr = sensiParams.indexCurveShiftData().find(idx);
320 QL_REQUIRE(itr != sensiParams.indexCurveShiftData().end(), "shiftData not found for " << idx);
321 shiftSize = itr->second->shiftSize;
322 if (itr->second->shiftType == ShiftType::Relative) {
323 Size keyIdx = key.index;
324 Period p = itr->second->shiftTenors[keyIdx];
325 Handle<YieldTermStructure> yts = simMarket->iborIndex(idx, marketConfiguration)->forwardingTermStructure();
326 Time t = yts->dayCounter().yearFraction(asof, asof + p);
327 Real zeroRate = yts->zeroRate(t, Continuous);
328 shiftMult = zeroRate;
329 }
330 } break;
332 string yc = keylabel;
333 auto itr = sensiParams.yieldCurveShiftData().find(yc);
334 QL_REQUIRE(itr != sensiParams.yieldCurveShiftData().end(), "shiftData not found for " << yc);
335 shiftSize = itr->second->shiftSize;
336 if (itr->second->shiftType == ShiftType::Relative) {
337 Size keyIdx = key.index;
338 Period p = itr->second->shiftTenors[keyIdx];
339 Handle<YieldTermStructure> yts = simMarket->yieldCurve(yc, marketConfiguration);
340 Time t = yts->dayCounter().yearFraction(asof, asof + p);
341 Real zeroRate = yts->zeroRate(t, Continuous);
342 shiftMult = zeroRate;
343 }
344 } break;
346 string eq = keylabel;
347 auto itr = sensiParams.dividendYieldShiftData().find(eq);
348 QL_REQUIRE(itr != sensiParams.dividendYieldShiftData().end(), "shiftData not found for " << eq);
349 shiftSize = itr->second->shiftSize;
350
351 if (itr->second->shiftType == ShiftType::Relative) {
352 Size keyIdx = key.index;
353 Period p = itr->second->shiftTenors[keyIdx];
354 Handle<YieldTermStructure> ts = simMarket->equityDividendCurve(eq, marketConfiguration);
355 Time t = ts->dayCounter().yearFraction(asof, asof + p);
356 Real zeroRate = ts->zeroRate(t, Continuous);
357 shiftMult = zeroRate;
358 }
359 } break;
361 string pair = keylabel;
362 auto itr = sensiParams.fxVolShiftData().find(pair);
363 QL_REQUIRE(itr != sensiParams.fxVolShiftData().end(), "shiftData not found for " << pair);
364 shiftSize = itr->second.shiftSize;
365 if (itr->second.shiftType == ShiftType::Relative) {
366 vector<Real> strikes = sensiParams.fxVolShiftData().at(pair).shiftStrikes;
367 QL_REQUIRE(strikes.size() == 1 && close_enough(strikes[0], 0), "Only ATM FX vols supported");
368 Real atmFwd = 0.0; // hardcoded, since only ATM supported
369 Size keyIdx = key.index;
370 Period p = itr->second.shiftExpiries[keyIdx];
371 Handle<BlackVolTermStructure> vts = simMarket->fxVol(pair, marketConfiguration);
372 Time t = vts->dayCounter().yearFraction(asof, asof + p);
373 Real atmVol = vts->blackVol(t, atmFwd);
374 shiftMult = atmVol;
375 }
376 } break;
378 string pair = keylabel;
379 auto itr = sensiParams.equityVolShiftData().find(pair);
380 QL_REQUIRE(itr != sensiParams.equityVolShiftData().end(), "shiftData not found for " << pair);
381 shiftSize = itr->second.shiftSize;
382 if (itr->second.shiftType == ShiftType::Relative) {
383 Size keyIdx = key.index;
384 Period p = itr->second.shiftExpiries[keyIdx];
385 Handle<BlackVolTermStructure> vts = simMarket->equityVol(pair, marketConfiguration);
386 Time t = vts->dayCounter().yearFraction(asof, asof + p);
387 Real atmVol = vts->blackVol(t, Null<Real>());
388 shiftMult = atmVol;
389 }
390 } break;
392 auto itr = sensiParams.swaptionVolShiftData().find(keylabel);
393 QL_REQUIRE(itr != sensiParams.swaptionVolShiftData().end(), "shiftData not found for " << keylabel);
394 shiftSize = itr->second.shiftSize;
395 if (itr->second.shiftType == ShiftType::Relative) {
396 vector<Real> strikes = itr->second.shiftStrikes;
397 vector<Period> tenors = itr->second.shiftTerms;
398 vector<Period> expiries = itr->second.shiftExpiries;
399 Size keyIdx = key.index;
400 Size expIdx = keyIdx / (tenors.size() * strikes.size());
401 Period p_exp = expiries[expIdx];
402 Size tenIdx = keyIdx % tenors.size();
403 Period p_ten = tenors[tenIdx];
404 Real strike = Null<Real>(); // for cubes this will be ATM
405 Handle<SwaptionVolatilityStructure> vts = simMarket->swaptionVol(keylabel, marketConfiguration);
406 Real vol = vts->volatility(p_exp, p_ten, strike);
407 shiftMult = vol;
408 }
409 } break;
411 string securityId = keylabel;
412 auto itr = sensiParams.yieldVolShiftData().find(securityId);
413 QL_REQUIRE(itr != sensiParams.yieldVolShiftData().end(), "shiftData not found for " << securityId);
414 shiftSize = itr->second.shiftSize;
415 if (itr->second.shiftType == ShiftType::Relative) {
416 vector<Real> strikes = itr->second.shiftStrikes;
417 QL_REQUIRE(strikes.size() == 1 && close_enough(strikes[0], 0.0),
418 "shift strikes should be {0.0} for yield volatilities");
419 vector<Period> tenors = itr->second.shiftTerms;
420 vector<Period> expiries = itr->second.shiftExpiries;
421 Size keyIdx = key.index;
422 Size expIdx = keyIdx / (tenors.size() * strikes.size());
423 Period p_exp = expiries[expIdx];
424 Size tenIdx = keyIdx % tenors.size();
425 Period p_ten = tenors[tenIdx];
426 Real strike = Null<Real>(); // for cubes this will be ATM
427 Handle<SwaptionVolatilityStructure> vts = simMarket->yieldVol(securityId, marketConfiguration);
428 Real vol = vts->volatility(p_exp, p_ten, strike);
429 shiftMult = vol;
430 }
431 } break;
433 string ccy = keylabel;
434 auto itr = sensiParams.capFloorVolShiftData().find(ccy);
435 QL_REQUIRE(itr != sensiParams.capFloorVolShiftData().end(), "shiftData not found for " << ccy);
436 shiftSize = itr->second->shiftSize;
437 if (itr->second->shiftType == ShiftType::Relative) {
438 vector<Real> strikes = itr->second->shiftStrikes;
439 vector<Period> expiries = itr->second->shiftExpiries;
440 QL_REQUIRE(strikes.size() > 0, "Only strike capfloor vols supported");
441 shiftMult = simMarket->baseScenario()->get(
443 }
444 } break;
446 string name = keylabel;
447 auto itr = sensiParams.cdsVolShiftData().find(name);
448 QL_REQUIRE(itr != sensiParams.cdsVolShiftData().end(), "shiftData not found for " << name);
449 shiftSize = itr->second.shiftSize;
450 if (itr->second.shiftType == ShiftType::Relative) {
451 vector<Period> expiries = itr->second.shiftExpiries;
452 Size keyIdx = key.index;
453 Size expIdx = keyIdx;
454 Period p_exp = expiries[expIdx];
455 Handle<CreditVolCurve> vts = simMarket->cdsVol(name, marketConfiguration);
456 // hardcoded 5y term
457 Real atmVol = vts->volatility(asof + p_exp, 5.0, Null<Real>(), vts->type());
458 shiftMult = atmVol;
459 }
460 } break;
462 string name = keylabel;
463 auto itr = sensiParams.creditCurveShiftData().find(name);
464 QL_REQUIRE(itr != sensiParams.creditCurveShiftData().end(), "shiftData not found for " << name);
465 shiftSize = itr->second->shiftSize;
466 if (itr->second->shiftType == ShiftType::Relative) {
467 Size keyIdx = key.index;
468 Period p = itr->second->shiftTenors[keyIdx];
469 Handle<DefaultProbabilityTermStructure> ts = simMarket->defaultCurve(name, marketConfiguration)->curve();
470 Time t = ts->dayCounter().yearFraction(asof, asof + p);
471 Real prob = ts->survivalProbability(t);
472 shiftMult = -std::log(prob) / t;
473 }
474 } break;
476 string name = keylabel;
477 auto itr = sensiParams.baseCorrelationShiftData().find(name);
478 QL_REQUIRE(itr != sensiParams.baseCorrelationShiftData().end(), "shiftData not found for " << name);
479 shiftSize = itr->second.shiftSize;
480 if (itr->second.shiftType == ShiftType::Relative) {
481 vector<Real> lossLevels = itr->second.shiftLossLevels;
482 vector<Period> terms = itr->second.shiftTerms;
483 Size keyIdx = key.index;
484 Size lossLevelIdx = keyIdx / terms.size();
485 Real lossLevel = lossLevels[lossLevelIdx];
486 Size termIdx = keyIdx % terms.size();
487 Period term = terms[termIdx];
488 Handle<QuantExt::BaseCorrelationTermStructure> ts = simMarket->baseCorrelation(name, marketConfiguration);
489 Real bc = ts->correlation(asof + term, lossLevel, true); // extrapolate
490 shiftMult = bc;
491 }
492 } break;
494 string idx = keylabel;
495 auto itr = sensiParams.zeroInflationCurveShiftData().find(idx);
496 QL_REQUIRE(itr != sensiParams.zeroInflationCurveShiftData().end(), "shiftData not found for " << idx);
497 shiftSize = itr->second->shiftSize;
498 if (itr->second->shiftType == ShiftType::Relative) {
499 Size keyIdx = key.index;
500 Period p = itr->second->shiftTenors[keyIdx];
501 Handle<ZeroInflationTermStructure> yts =
502 simMarket->zeroInflationIndex(idx, marketConfiguration)->zeroInflationTermStructure();
503 Time t = yts->dayCounter().yearFraction(asof, asof + p);
504 Real zeroRate = yts->zeroRate(t);
505 shiftMult = zeroRate;
506 }
507 } break;
509 string idx = keylabel;
510 auto itr = sensiParams.yoyInflationCurveShiftData().find(idx);
511 QL_REQUIRE(itr != sensiParams.yoyInflationCurveShiftData().end(), "shiftData not found for " << idx);
512 shiftSize = itr->second->shiftSize;
513 if (itr->second->shiftType == ShiftType::Relative) {
514 Size keyIdx = key.index;
515 Period p = sensiParams.yoyInflationCurveShiftData().at(idx)->shiftTenors[keyIdx];
516 Handle<YoYInflationTermStructure> yts =
517 simMarket->yoyInflationIndex(idx, marketConfiguration)->yoyInflationTermStructure();
518 Time t = yts->dayCounter().yearFraction(asof, asof + p);
519 Real yoyRate = yts->yoyRate(t);
520 shiftMult = yoyRate;
521 }
522 } break;
524 string name = keylabel;
525 auto itr = sensiParams.yoyInflationCapFloorVolShiftData().find(name);
526 QL_REQUIRE(itr != sensiParams.yoyInflationCapFloorVolShiftData().end(), "shiftData not found for " << name);
527 shiftSize = itr->second->shiftSize;
528 if (itr->second->shiftType == ShiftType::Relative) {
529 vector<Real> strikes = itr->second->shiftStrikes;
530 vector<Period> expiries = itr->second->shiftExpiries;
531 QL_REQUIRE(strikes.size() > 0, "Only strike yoy inflation capfloor vols supported");
532 Size keyIdx = key.index;
533 Size expIdx = keyIdx / strikes.size();
534 Period p_exp = expiries[expIdx];
535 Size strIdx = keyIdx % strikes.size();
536 Real strike = strikes[strIdx];
537 Handle<QuantExt::YoYOptionletVolatilitySurface> vts = simMarket->yoyCapFloorVol(name, marketConfiguration);
538 Real vol = vts->volatility(p_exp, strike, vts->observationLag());
539 shiftMult = vol;
540 }
541 } break;
543 string name = keylabel;
544 auto itr = sensiParams.zeroInflationCapFloorVolShiftData().find(name);
545 QL_REQUIRE(itr != sensiParams.zeroInflationCapFloorVolShiftData().end(), "shiftData not found for " << name);
546 shiftSize = itr->second->shiftSize;
547 if (itr->second->shiftType == ShiftType::Relative) {
548 vector<Real> strikes = itr->second->shiftStrikes;
549 vector<Period> expiries = itr->second->shiftExpiries;
550 QL_REQUIRE(strikes.size() > 0, "Only strike zc inflation capfloor vols supported");
551 Size keyIdx = key.index;
552 Size expIdx = keyIdx / strikes.size();
553 Period p_exp = expiries[expIdx];
554 Size strIdx = keyIdx % strikes.size();
555 Real strike = strikes[strIdx];
556 Handle<CPIVolatilitySurface> vts =
557 simMarket->cpiInflationCapFloorVolatilitySurface(name, marketConfiguration);
558 Real vol = vts->volatility(p_exp, strike, vts->observationLag());
559 shiftMult = vol;
560 }
561 } break;
563 auto it = sensiParams.commodityCurveShiftData().find(keylabel);
564 QL_REQUIRE(it != sensiParams.commodityCurveShiftData().end(), "shiftData not found for " << keylabel);
565 shiftSize = it->second->shiftSize;
566 if (it->second->shiftType == ShiftType::Relative) {
567 Period p = it->second->shiftTenors[key.index];
568 Handle<PriceTermStructure> priceCurve = simMarket->commodityPriceCurve(keylabel, marketConfiguration);
569 Time t = priceCurve->dayCounter().yearFraction(asof, asof + p);
570 shiftMult = priceCurve->price(t);
571 }
572 } break;
574 auto it = sensiParams.commodityVolShiftData().find(keylabel);
575 QL_REQUIRE(it != sensiParams.commodityVolShiftData().end(), "shiftData not found for " << keylabel);
576
577 shiftSize = it->second.shiftSize;
578 if (it->second.shiftType == ShiftType::Relative) {
579 Size moneynessIndex = key.index / it->second.shiftExpiries.size();
580 Size expiryIndex = key.index % it->second.shiftExpiries.size();
581 Real moneyness = it->second.shiftStrikes[moneynessIndex];
582 Period expiry = it->second.shiftExpiries[expiryIndex];
583 Real spotValue = simMarket->commodityPriceCurve(keylabel, marketConfiguration)->price(0);
584 Handle<BlackVolTermStructure> vts = simMarket->commodityVolatility(keylabel, marketConfiguration);
585 Time t = vts->dayCounter().yearFraction(asof, asof + expiry);
586 Real vol = vts->blackVol(t, moneyness * spotValue);
587 shiftMult = vol;
588 }
589 } break;
591 auto itr = sensiParams.securityShiftData().find(keylabel);
592 QL_REQUIRE(itr != sensiParams.securityShiftData().end(), "shiftData not found for " << keylabel);
593 shiftSize = itr->second.shiftSize;
594 if (itr->second.shiftType == ShiftType::Relative) {
595 shiftMult = 1.0;
596 try {
597 shiftMult = simMarket->securitySpread(keylabel, marketConfiguration)->value();
598 } catch (...) {
599 // if there is no spread given for a security, we return 1.0, this is not relevant anyway,
600 // because there will be no scenario generated for this risk factor
601 }
602 }
603 } break;
604 default:
605 // KeyType::CPIIndex does not get shifted
606 QL_FAIL("KeyType not supported yet - " << keytype);
607 }
608 Real realShift = shiftSize * shiftMult;
609 return realShift;
610}
611
612} // namespace analytics
613} // namespace ore
std::vector< QuantLib::ext::shared_ptr< ore::analytics::NPVCube > > outputCubes() const
void buildCube(const QuantLib::ext::shared_ptr< ore::data::Portfolio > &portfolio, const std::function< std::vector< QuantLib::ext::shared_ptr< ore::analytics::ValuationCalculator > >()> &calculators, const std::function< std::vector< QuantLib::ext::shared_ptr< ore::analytics::CounterpartyCalculator > >()> &cptyCalculators={}, bool mporStickyDate=true, bool dryRun=false)
Data types stored in the scenario class.
Definition: scenario.hpp:48
KeyType keytype
Key type.
Definition: scenario.hpp:89
std::string name
Key name.
Definition: scenario.hpp:94
KeyType
Risk Factor types.
Definition: scenario.hpp:51
QuantLib::ext::shared_ptr< ore::data::CurveConfigurations > curveConfigs_
Optional curve configurations. Used in building the scenario sim market.
QuantLib::ext::shared_ptr< SensitivityScenarioData > sensitivityData_
QuantLib::ext::shared_ptr< ore::data::TodaysMarketParameters > todaysMarketParams_
Optional todays market parameters. Used in building the scenario sim market.
const QuantLib::Date asof() const
The ASOF date for the sensitivity analysis.
void generateSensitivities()
Generate the Sensitivities.
QuantLib::ext::shared_ptr< EngineData > engineData_
the engine data (provided as input, needed to construct the engine factory)
QuantLib::ext::shared_ptr< ScenarioSimMarket > simMarket_
std::vector< QuantLib::ext::shared_ptr< SensitivityCube > > sensiCubes_
sensitivityCube
QuantLib::ext::shared_ptr< Portfolio > portfolio_
the portfolio (provided as input)
SensitivityAnalysis(const QuantLib::ext::shared_ptr< ore::data::Portfolio > &portfolio, const QuantLib::ext::shared_ptr< ore::data::Market > &market, const string &marketConfiguration, const QuantLib::ext::shared_ptr< ore::data::EngineData > &engineData, const QuantLib::ext::shared_ptr< ScenarioSimMarketParameters > &simMarketData, const QuantLib::ext::shared_ptr< SensitivityScenarioData > &sensitivityData, const bool recalibrateModels, const QuantLib::ext::shared_ptr< ore::data::CurveConfigurations > &curveConfigs=nullptr, const QuantLib::ext::shared_ptr< ore::data::TodaysMarketParameters > &todaysMarketParams=nullptr, const bool nonShiftedBaseCurrencyConversion=false, const QuantLib::ext::shared_ptr< ReferenceDataManager > &referenceData=nullptr, const IborFallbackConfig &iborFallbackConfig=IborFallbackConfig::defaultConfig(), const bool continueOnError=false, bool dryRun=false)
Constructor using single-threaded engine.
std::set< std::pair< string, QuantLib::ext::shared_ptr< QuantExt::ModelBuilder > > > modelBuilders_
model builders
QuantLib::ext::shared_ptr< ore::data::ReferenceDataManager > referenceData_
QuantLib::ext::shared_ptr< ore::data::Market > market_
QuantLib::ext::shared_ptr< SensitivityScenarioGenerator > scenarioGenerator_
QuantLib::ext::shared_ptr< ScenarioSimMarketParameters > simMarketData_
QuantLib::ext::shared_ptr< ore::data::Loader > loader_
Description of sensitivity shift scenarios.
const map< string, QuantLib::ext::shared_ptr< CapFloorVolShiftData > > & yoyInflationCapFloorVolShiftData() const
const map< string, QuantLib::ext::shared_ptr< CurveShiftData > > & indexCurveShiftData() const
const map< string, QuantLib::ext::shared_ptr< CapFloorVolShiftData > > & capFloorVolShiftData() const
const map< string, SpotShiftData > & securityShiftData() const
const map< string, VolShiftData > & equityVolShiftData() const
const map< string, QuantLib::ext::shared_ptr< CurveShiftData > > & discountCurveShiftData() const
const map< string, QuantLib::ext::shared_ptr< CurveShiftData > > & dividendYieldShiftData() const
const map< string, VolShiftData > & fxVolShiftData() const
const map< string, GenericYieldVolShiftData > & yieldVolShiftData() const
const map< string, QuantLib::ext::shared_ptr< CurveShiftData > > & zeroInflationCurveShiftData() const
const map< string, QuantLib::ext::shared_ptr< CurveShiftData > > & creditCurveShiftData() const
const map< string, SpotShiftData > & fxShiftData() const
const map< string, GenericYieldVolShiftData > & swaptionVolShiftData() const
const map< string, QuantLib::ext::shared_ptr< CurveShiftData > > & yoyInflationCurveShiftData() const
const map< string, QuantLib::ext::shared_ptr< CapFloorVolShiftData > > & zeroInflationCapFloorVolShiftData() const
const map< string, BaseCorrelationShiftData > & baseCorrelationShiftData() const
const map< string, QuantLib::ext::shared_ptr< CurveShiftData > > & yieldCurveShiftData() const
const map< string, QuantLib::ext::shared_ptr< CurveShiftData > > & commodityCurveShiftData() const
const map< string, VolShiftData > & commodityVolShiftData() const
const map< string, CdsVolShiftData > & cdsVolShiftData() const
const map< string, SpotShiftData > & equityShiftData() const
void buildCube(const QuantLib::ext::shared_ptr< data::Portfolio > &portfolio, QuantLib::ext::shared_ptr< analytics::NPVCube > outputCube, std::vector< QuantLib::ext::shared_ptr< ValuationCalculator > > calculators, bool mporStickyDate=true, QuantLib::ext::shared_ptr< analytics::NPVCube > outputCubeNettingSet=nullptr, QuantLib::ext::shared_ptr< analytics::NPVCube > outputCptyCube=nullptr, std::vector< QuantLib::ext::shared_ptr< CounterpartyCalculator > > cptyCalculators={}, bool dryRun=false)
Build NPV cube.
const std::set< QuantLib::ext::shared_ptr< ProgressIndicator > > & progressIndicators() const
void registerProgressIndicator(const QuantLib::ext::shared_ptr< ProgressIndicator > &indicator)
factory class for cloning a cached scenario
Context & context_
factory class for cloning a cached scenario
join n sensi cubes in terms of stored ids
#define LOG(text)
#define DLOG(text)
#define TLOG(text)
multi-threaded valuation engine
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Real getShiftSize(const RiskFactorKey &key, const SensitivityScenarioData &sensiParams, const QuantLib::ext::shared_ptr< ScenarioSimMarket > &simMarket, const string &marketConfiguration)
std::set< std::string > getShiftSpecKeys(const SensitivityScenarioData &d)
A cube implementation that stores the cube in memory.
Perform sensitivity analysis for a given portfolio.
vector< Real > strikes
vector< string > curveConfigs
Date asof(14, Jun, 2018)
string name
The counterparty cube calculator interface.
The cube valuation core.