Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
parstressscenarioconverter.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2024 AcadiaSoft Inc.
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
23#include <ql/instrument.hpp>
24#include <ql/instruments/capfloor.hpp>
26
27namespace ore {
28namespace analytics {
29
30// Utility functions
31namespace {
32
33const static std::set<RiskFactorKey::KeyType> supportedCurveShiftTypes = {
36
37
38double computeTargetRate(const double fairRate, const double shift, const ShiftType shiftType) {
39 double shiftedRate = fairRate;
40 if (shiftType == ShiftType::Absolute) {
41 shiftedRate += shift;
42 } else {
43 shiftedRate *= (1.0 + shift);
44 }
45 return shiftedRate;
46}
47
48QuantLib::Period getYieldCurvePeriod(const RiskFactorKey& rfKey,
49 const boost::shared_ptr<ScenarioSimMarketParameters>& params) {
50 QL_REQUIRE(rfKey.index < params->yieldCurveTenors(rfKey.name).size(), "Please align pillars, internal error");
51 return params->yieldCurveTenors(rfKey.name)[rfKey.index];
52}
53
54//! Compute the
55double getCurveStressShift(const RiskFactorKey& key, const StressTestScenarioData::StressTestData& stressScenario) {
56 const std::vector<double>* shifts = nullptr;
57 double shift = 0.0;
58 switch (key.keytype) {
60 auto it = stressScenario.discountCurveShifts.find(key.name);
61 if (it != stressScenario.discountCurveShifts.end()) {
62 shifts = &(it->second.shifts);
63 }
64 break;
65 }
67 auto it = stressScenario.yieldCurveShifts.find(key.name);
68 if (it != stressScenario.yieldCurveShifts.end()) {
69 shifts = &(it->second.shifts);
70 }
71 break;
72 }
74 auto it = stressScenario.indexCurveShifts.find(key.name);
75 if (it != stressScenario.indexCurveShifts.end()) {
76 shifts = &(it->second.shifts);
77 }
78 break;
79 }
81 auto it = stressScenario.survivalProbabilityShifts.find(key.name);
82 if (it != stressScenario.survivalProbabilityShifts.end()) {
83 shifts = &(it->second.shifts);
84 }
85 break;
86 }
87 default:
88 QL_FAIL("ParStressScenario to ZeroConversion: Unsupported riskfactor, can not compute time to maturity "
89 "from curve");
90 }
91 if (shifts != nullptr && key.index < shifts->size()) {
92 shift = shifts->at(key.index);
93 }
94 return shift;
95}
96
97double getCapFloorStressShift(const RiskFactorKey& key, const StressTestScenarioData::StressTestData& stressScenario,
98 const boost::shared_ptr<ScenarioSimMarketParameters>& params) {
99 double shift = 0.0;
100 auto it = stressScenario.capVolShifts.find(key.name);
101 if (it != stressScenario.capVolShifts.end()) {
102 const auto& cfData = it->second;
103 size_t nStrikes = params->capFloorVolStrikes(key.name).size();
104 size_t n = key.index;
105 size_t tenorId = n / nStrikes;
106 size_t strikeId = n % nStrikes;
107 const Period& tenor = cfData.shiftExpiries[tenorId];
108 if (cfData.shiftStrikes.empty()) {
109 shift = cfData.shifts.at(tenor).front();
110 } else {
111 shift = cfData.shifts.at(tenor)[strikeId];
112 }
113 }
114 return shift;
115}
116
117//! Creates a copy from the parStressScenario and delete all par shifts but keeps all zeroShifts
118StressTestScenarioData::StressTestData
119removeParShiftsCopy(const StressTestScenarioData::StressTestData& parStressScenario) {
120 StressTestScenarioData::StressTestData zeroStressScenario = parStressScenario;
121 if (parStressScenario.irCapFloorParShifts) {
122 zeroStressScenario.capVolShifts.clear();
123 }
124 if (parStressScenario.creditCurveParShifts) {
125 zeroStressScenario.survivalProbabilityShifts.clear();
126 }
127 if (parStressScenario.irCurveParShifts) {
128 zeroStressScenario.discountCurveShifts.clear();
129 zeroStressScenario.indexCurveShifts.clear();
130 zeroStressScenario.yieldCurveShifts.clear();
131 }
132 zeroStressScenario.irCapFloorParShifts = false;
133 zeroStressScenario.irCurveParShifts = false;
134 zeroStressScenario.creditCurveParShifts = false;
135 return zeroStressScenario;
136}
137} // namespace
138
139std::set<RiskFactorKey::KeyType> disabledParRates(bool irCurveParRates, bool irCapFloorParRates, bool creditParRates) {
140 set<RiskFactorKey::KeyType> disabledParRates;
141 if (!irCurveParRates) {
145 }
146 if (!irCapFloorParRates) {
148 }
149 if (!creditParRates) {
151 }
152 return disabledParRates;
153}
154
155//! Checks the the tenors for curves in a stresstest scenario are alligned with par sensitivity config
156bool checkCurveShiftData(const std::string& name, const StressTestScenarioData::CurveShiftData& stressShiftData,
157 const std::map<std::string, QuantLib::ext::shared_ptr<SensitivityScenarioData::CurveShiftData>>& sensiData) {
158 auto it = sensiData.find(name);
159 if (it == sensiData.end()) {
161 "StressScenario", name, "Par Shift to zero conversion",
162 "no par sensitivity scenario found. Please add par sensi config").log();
163 return false;
164 }
165 auto parShiftData = ext::dynamic_pointer_cast<SensitivityScenarioData::CurveShiftParData>(it->second);
166 if (parShiftData == nullptr) {
167 StructuredConfigurationWarningMessage("StressScenario", name, "Par Shift to zero conversion",
168 "no par sensitivity scenario found. Please add par sensi config")
169 .log();
170 return false;
171 }
172 const size_t nParShifts = parShiftData->shiftTenors.size();
173 const size_t nStressShifts = stressShiftData.shiftTenors.size();
174 if (nParShifts != nStressShifts) {
176 "StressScenario", name, "Par Shift to zero conversion",
177 "mismatch between tenors, we have " + to_string(nParShifts) + " parInstruments defined but " +
178 to_string(nStressShifts) +
179 " shifts in the scenario. Please align pillars of stress test and par sensi config")
180 .log();
181 return false;
182 }
183 for (size_t i = 0; i < nParShifts; ++i){
184 if (parShiftData->shiftTenors[i] != stressShiftData.shiftTenors[i]){
185 StructuredConfigurationWarningMessage("StressScenario", name, "Par Shift to zero conversion",
186 "tenors are not aligned, " + to_string(i) + " par Pillar is " +
187 to_string(parShiftData->shiftTenors[i]) +
188 " vs stress shift piller " +
189 to_string(stressShiftData.shiftTenors[i]) +
190 ". Please align pillars of stress test and par sensi config")
191 .log();
192 return false;
193 }
194 }
195 return true;
196}
197
198
199//! Checks the the strikes and expiries of cap floors in stresstest scenario are alligned with par sensitivity config
200bool checkCapFloorShiftData(const std::string& name, const StressTestScenarioData::CapFloorVolShiftData& stressShiftData,
201 const std::map<std::string, QuantLib::ext::shared_ptr<SensitivityScenarioData::CapFloorVolShiftData>>& sensiData){
202
203 auto it = sensiData.find(name);
204 if (it == sensiData.end()) {
206 "StressScenario", name, "Par Shift to zero conversion",
207 "no par cap floor sensitivity scenario found. Please add par sensi config")
208 .log();
209 return false;
210 }
211 auto parShiftData = ext::dynamic_pointer_cast<SensitivityScenarioData::CapFloorVolShiftParData>(it->second);
212 if (parShiftData == nullptr) {
214 "StressScenario", name, "Par Shift to zero conversion",
215 "no par cap floor sensitivity scenario found. Please add par sensi config")
216 .log();
217 return false;
218 }
219 const size_t nParShifts = parShiftData->shiftExpiries.size();
220 const size_t nStressShifts = stressShiftData.shiftExpiries.size();
221 if (nParShifts != nStressShifts) {
223 "StressScenario", name, "Par Shift to zero conversion",
224 "mismatch between capFloor expiries, we have " + to_string(nParShifts) + " parInstruments defined but " +
225 to_string(nStressShifts) +
226 " shifts in the scenario. Please align pillars of stress test and par sensi config")
227 .log();
228 return false;
229 }
230 for (size_t i = 0; i < nParShifts; ++i) {
231 if (parShiftData->shiftExpiries[i] != stressShiftData.shiftExpiries[i]) {
233 "StressScenario", name, "Par Shift to zero conversion",
234 "CapFloor expiries are not aligned, " + to_string(i) + " CapFloor Pillar is " +
235 to_string(parShiftData->shiftExpiries[i]) + " vs stress shift piller " +
236 to_string(stressShiftData.shiftExpiries[i]) +
237 ". Please align pillars of stress test and par sensi config")
238 .log();
239 return false;
240 }
241 }
242 if(!stressShiftData.shiftStrikes.empty()){
243 const size_t nParStrikes = parShiftData->shiftStrikes.size();
244 const size_t nSressStrikes = stressShiftData.shiftStrikes.size();
245
246 if (nParStrikes != nSressStrikes) {
248 "StressScenario", name, "Par Shift to zero conversion",
249 "mismatch between capFloor strikes, we have " + to_string(nParStrikes) + " par strikes defined but " +
250 to_string(nSressStrikes) +
251 " strikes in the scenario. Please align strikes of stress test and par sensi config")
252 .log();
253 return false;
254 }
255 for (size_t i = 0; i < nParStrikes; ++i) {
256 if (parShiftData->shiftStrikes[i] != stressShiftData.shiftStrikes[i]) {
258 "StressScenario", name, "Par Shift to zero conversion",
259 "CapFloor expiries are not aligned, " + to_string(i) + " CapFloor strike is " +
260 to_string(parShiftData->shiftStrikes[i]) + " vs stress shift strike " +
261 to_string(stressShiftData.shiftStrikes[i]) +
262 ". Please align strikes of stress test and par sensi config")
263 .log();
264 return false;
265 }
266 }
267 }
268 return true;
269}
270
271//! Check if the scenarios defines a shift for each par rate defined in the sensitivityScenarioData;
273 const StressTestScenarioData::StressTestData& parStressScenario) const {
274 DLOG("Check if the par stresstest scenario is compatible with the parInstruments");
275 bool result = true;
276 if (parStressScenario.irCurveParShifts) {
277 // We require that shifts between stress scenario and sensi scenario are aligned
278 for (const auto& [ccy, curveShifts] : parStressScenario.discountCurveShifts) {
279 DLOG("Check if pillars between stress test and sensi config are alligned for discount curve " << ccy);
280 result = result && checkCurveShiftData(ccy, curveShifts, sensiScenarioData_->discountCurveShiftData());
281 }
282
283 for (const auto& [indexName, curveShifts] : parStressScenario.indexCurveShifts) {
284 DLOG("Check if pillars between stress test and sensi config are alligned for index curve " << indexName);
285 result = result && checkCurveShiftData(indexName, curveShifts, sensiScenarioData_->indexCurveShiftData());
286 }
287
288 for (const auto& [curveName, curveShifts] : parStressScenario.yieldCurveShifts) {
289 DLOG("Check if pillars between stress test and sensi config are alligned for yield curve " << curveName);
290 result = result && checkCurveShiftData(curveName, curveShifts, sensiScenarioData_->yieldCurveShiftData());
291 }
292 }
293
294 if (parStressScenario.creditCurveParShifts) {
295 for (const auto& [curveName, curveShifts] : parStressScenario.survivalProbabilityShifts) {
296 DLOG("Check if pillars between stress test and sensi config are alligned for credit curve " << curveName);
297 result = result && checkCurveShiftData(curveName, curveShifts, sensiScenarioData_->creditCurveShiftData());
298 }
299 }
300
301 if (parStressScenario.irCapFloorParShifts) {
302 for (const auto& [capSurfaceName, capShifts] : parStressScenario.capVolShifts) {
303 DLOG("Check if pillars and strikes between stress test and sensi config are alligned for cap floor surface "
304 << capSurfaceName);
305 result =
306 result && checkCapFloorShiftData(capSurfaceName, capShifts, sensiScenarioData_->capFloorVolShiftData());
307 }
308 }
309
310 return result;
311}
312
314 const QuantLib::Date& asof, const std::vector<RiskFactorKey>& sortedParInstrumentRiskFactorKeys,
315 const QuantLib::ext::shared_ptr<ore::analytics::ScenarioSimMarketParameters>& simMarketParams,
316 const QuantLib::ext::shared_ptr<ore::analytics::SensitivityScenarioData>& sensiScenarioData,
317 const QuantLib::ext::shared_ptr<ore::analytics::ScenarioSimMarket>& simMarket,
318 const ore::analytics::ParSensitivityInstrumentBuilder::Instruments& parInstruments, bool useSpreadedTermStructure)
319 : asof_(asof), sortedParInstrumentRiskFactorKeys_(sortedParInstrumentRiskFactorKeys),
320 simMarketParams_(simMarketParams), sensiScenarioData_(sensiScenarioData), simMarket_(simMarket),
321 parInstruments_(parInstruments), useSpreadedTermStructure_(useSpreadedTermStructure) {}
322
325
326 if (!parStressScenario.containsParShifts()) {
327 return parStressScenario;
328 }
329
330 if (!scenarioCanBeConverted(parStressScenario)) {
331 WLOG("Can not convert scenario " << parStressScenario.label << " Skip it and apply all shifts as zero shifts.");
332 return parStressScenario;
333 }
334
335 simMarket_->reset();
336
337 LOG("ParStressConverter: Scenario " << parStressScenario.label << " has IR Curve Par Shifts = "
338 << ore::data::to_string(parStressScenario.irCurveParShifts));
339
340 LOG("ParStressConverter: Scenario " << parStressScenario.label << " has CapFloor Par Shifts = "
341 << ore::data::to_string(parStressScenario.irCapFloorParShifts));
342
343 LOG("ParStressConverter: Scenario " << parStressScenario.label << " has Credit Par Shifts = "
344 << ore::data::to_string(parStressScenario.creditCurveParShifts));
345
346 std::set<RiskFactorKey::KeyType> excludedParRates =
347 disabledParRates(parStressScenario.irCurveParShifts, parStressScenario.irCapFloorParShifts,
348 parStressScenario.creditCurveParShifts);
349
350 LOG("ParStressConverter: Copy scenario and remove parShifts from scenario");
351 auto zeroStressScenario = removeParShiftsCopy(parStressScenario);
352 DLOG("ParStressConverter: Clone base scenario");
353 auto zeroSimMarketScenario = simMarket_->baseScenario()->clone();
354
355 // Derive t0 (unshifted) fair Rates
356 std::vector<RiskFactorKey> relevantParKeys;
357 std::map<RiskFactorKey, double> fairRates;
358 std::map<RiskFactorKey, double> baseScenarioValues;
359 std::map<RiskFactorKey, double> targets;
360 LOG("ParStressConverter: Compute fair rate and target rate for all ParInstruments");
361 for (const auto& rfKey : sortedParInstrumentRiskFactorKeys_) {
362 if (excludedParRates.count(rfKey.keytype) == 0 &&
363 (supportedCurveShiftTypes.count(rfKey.keytype) == 1 ||
365 relevantParKeys.push_back(rfKey);
366 const double fairRate = impliedParRate(rfKey);
367 // TODO get shift type;
368 const double target =
369 computeTargetRate(fairRate, getStressShift(rfKey, parStressScenario), ShiftType::Absolute);
370 const double baseScenarioValue = simMarket_->baseScenario()->get(rfKey);
371 const double baseScenarioAbsoluteValue = simMarket_->baseScenarioAbsolute()->get(rfKey);
372 DLOG("ParStressConverter: ParInstrument "
373 << rfKey << ", fair rate = " << fairRate << ", target rate = " << target << ", baseScenarioValue = "
374 << baseScenarioValue << ", baseScenarioAbsoluteValue = " << baseScenarioAbsoluteValue);
375 fairRates[rfKey] = fairRate;
376 targets[rfKey] = target;
377 baseScenarioValues[rfKey] = baseScenarioValue;
378 } else {
379 DLOG("Skip parInsturment " << rfKey << " the shifts for this risk factor type are in zero domain.");
380 }
381 }
382 // Optimize IR curves and Credit curves
383 LOG("ParStressConverter: Imply zero shifts");
384 std::map<RiskFactorKey, double> shifts;
385 for (const auto& rfKey : relevantParKeys) {
386 DLOG("Imply zero shifts for parInstrument " << rfKey);
387 const double target = targets[rfKey];
388 auto targetFunction = [this, &target, &rfKey, &zeroSimMarketScenario](double x) {
389 zeroSimMarketScenario->add(rfKey, x);
390 simMarket_->applyScenario(zeroSimMarketScenario);
391 return (impliedParRate(rfKey) - target) * 1e6;
392 };
393 double targetValue;
394 Brent brent;
395 try {
396 DLOG("ParStressConverter: Try to imply zero rate" << rfKey << " with bounds << " << lowerBound(rfKey) << ","
397 << upperBound(rfKey));
398 targetValue =
399 brent.solve(targetFunction, accuracy_, baseScenarioValues[rfKey], lowerBound(rfKey), upperBound(rfKey));
400 } catch (const std::exception& e) {
401 ALOG("ParStressConverter: Couldn't find a solution to imply a zero rate for parRate " << rfKey << ", got "
402 << e.what());
403 targetValue = simMarket_->baseScenario()->get(rfKey);
404 }
405 zeroSimMarketScenario->add(rfKey, targetValue);
406 shifts[rfKey] = shiftsSizeForScenario(rfKey, targetValue, baseScenarioValues[rfKey]);
407 updateTargetStressTestScenarioData(zeroStressScenario, rfKey, shifts[rfKey]);
408 }
409 simMarket_->applyScenario(zeroSimMarketScenario);
410 DLOG("ParStressConverter: Implied Scenario");
411 DLOG("parInstrument;fairRate;targetFairRate;zeroBaseValue;shift");
412 for (const auto& rfKey : relevantParKeys) {
413 DLOG(rfKey << ";" << impliedParRate(rfKey) << ";" << targets[rfKey] << ";" << baseScenarioValues[rfKey] << ";"
414 << shifts[rfKey]);
415 }
416 simMarket_->reset();
417 return zeroStressScenario;
418}
419
421 boost::shared_ptr<QuantLib::TermStructure> ts;
422 QuantLib::Period tenor;
423 switch (rfKey.keytype) {
425 ts = *simMarket_->discountCurve(rfKey.name);
426 tenor = getYieldCurvePeriod(rfKey, simMarketParams_);
427 break;
429 ts = *simMarket_->yieldCurve(rfKey.name);
430 tenor = getYieldCurvePeriod(rfKey, simMarketParams_);
431 break;
433 ts = *simMarket_->iborIndex(rfKey.name)->forwardingTermStructure();
434 tenor = getYieldCurvePeriod(rfKey, simMarketParams_);
435 break;
437 ts = *simMarket_->defaultCurve(rfKey.name)->curve();
438 QL_REQUIRE(rfKey.index < simMarketParams_->defaultTenors(rfKey.name).size(),
439 "Please align pillars, internal error");
440 tenor = simMarketParams_->defaultTenors(rfKey.name)[rfKey.index];
441 break;
443 ts = *simMarket_->capFloorVol(rfKey.name);
444 auto [tenorId, strikeId] = getCapFloorTenorAndStrikeIds(rfKey);
445 QL_REQUIRE(
446 simMarketParams_->capFloorVolExpiries(rfKey.name).size() > tenorId,
447 "Internal Error: ParStressScenarioConversion, simmarket and par sensitivity instruments are not aligned.");
448 tenor = simMarketParams_->capFloorVolExpiries(rfKey.name)[tenorId];
449 }
450 default:
451 QL_FAIL("ParStressScenario to ZeroConversion: Unsupported riskfactor, can not compute time to maturity "
452 "from curve");
453 }
454 return ts->dayCounter().yearFraction(asof_, asof_ + tenor);
455}
456
457std::pair<size_t, size_t> ParStressScenarioConverter::getCapFloorTenorAndStrikeIds(const RiskFactorKey& rfKey) const {
458 size_t nStrikes = simMarketParams_->capFloorVolStrikes(rfKey.name).size();
459 size_t n = rfKey.index;
460 size_t tenorId = n / nStrikes;
461 size_t strikeId = n % nStrikes;
462 return {tenorId, strikeId};
463}
464
466 double baseValue) const {
467 DLOG("compute shift for" << rfKey << " targetZeroValue " << targetValue << " baseValue " << baseValue);
468 double shift = 0.0;
469 switch (rfKey.keytype) {
474 double ttm = maturityTime(rfKey);
475 DLOG("TTM " << ttm);
477 shift = -std::log(targetValue / baseValue) / ttm;
478 } else {
479 shift = -std::log(targetValue) / ttm;
480 }
481 DLOG("Shift = " << shift);
482 break;
483 }
486 shift = targetValue - baseValue;
487 } else {
488 shift = targetValue;
489 };
490 break;
491 default:
492 QL_FAIL("ShiftSizeForScenario: Unsupported par instruments type " << rfKey.keytype);
493 }
494 return shift;
495}
496
500 } else if (supportedCurveShiftTypes.count(key.keytype) == 1) {
501 auto parInstIt = parInstruments_.parHelpers_.find(key);
502 QL_REQUIRE(parInstIt != parInstruments_.parHelpers_.end(),
503 "Internal error, trying to compute parRate but havent build parRateHelper");
504 return impliedQuote(parInstIt->second);
505 } else {
506 QL_FAIL("Unsupported parRate");
507 }
508}
509
511 const StressTestScenarioData::StressTestData& stressScenario) const {
513 return getCapFloorStressShift(key, stressScenario, simMarketParams_);
514 } else {
515 return getCurveStressShift(key, stressScenario);
516 }
517}
518
520 StressTestScenarioData::StressTestData& stressScenario, const RiskFactorKey& key, const double zeroShift) const {
522 if (stressScenario.discountCurveShifts.count(key.name) == 0) {
525 newData.shiftTenors = simMarketParams_->yieldCurveTenors(key.name);
526 newData.shifts = std::vector<double>(newData.shiftTenors.size(), 0.0);
527 newData.shifts[key.index] = zeroShift;
528 stressScenario.discountCurveShifts.insert({key.name, newData});
529 } else {
530 stressScenario.discountCurveShifts.at(key.name).shifts[key.index] = zeroShift;
531 }
533 if (stressScenario.indexCurveShifts.count(key.name) == 0) {
536 newData.shiftTenors = simMarketParams_->yieldCurveTenors(key.name);
537 newData.shifts = std::vector<double>(newData.shiftTenors.size(), 0.0);
538 newData.shifts[key.index] = zeroShift;
539 stressScenario.indexCurveShifts.insert({key.name, newData});
540 } else {
541 stressScenario.indexCurveShifts.at(key.name).shifts[key.index] = zeroShift;
542 }
544 if (stressScenario.survivalProbabilityShifts.count(key.name) == 0) {
547 newData.shiftTenors = simMarketParams_->defaultTenors(key.name);
548 newData.shifts = std::vector<double>(newData.shiftTenors.size(), 0.0);
549 newData.shifts[key.index] = zeroShift;
550 stressScenario.survivalProbabilityShifts.insert({key.name, newData});
551 } else {
552 stressScenario.survivalProbabilityShifts.at(key.name).shifts[key.index] = zeroShift;
553 }
555 if (stressScenario.capVolShifts.count(key.name) == 0) {
558 newData.shiftExpiries = simMarketParams_->capFloorVolExpiries(key.name);
559 newData.shiftStrikes = simMarketParams_->capFloorVolStrikes(key.name);
560 for (size_t i = 0; i < newData.shiftExpiries.size(); ++i) {
561 newData.shifts[newData.shiftExpiries[i]] = std::vector<double>(newData.shiftStrikes.size(), 0.0);
562 }
563 const auto& [expiryId, strikeId] = getCapFloorTenorAndStrikeIds(key);
564 newData.shifts[newData.shiftExpiries[expiryId]][strikeId] = zeroShift;
565 stressScenario.capVolShifts.insert({key.name, newData});
566 } else {
567 StressTestScenarioData::CapFloorVolShiftData& newData = stressScenario.capVolShifts.at(key.name);
568 const auto& [expiryId, strikeId] = getCapFloorTenorAndStrikeIds(key);
569 newData.shifts[newData.shiftExpiries[expiryId]][strikeId] = zeroShift;
570 }
571 }
572}
573
576 return minVol_ - simMarket_->baseScenarioAbsolute()->get(key);
578 return minVol_;
583 return minDiscountFactor_ / simMarket_->baseScenarioAbsolute()->get(key);
584 } else {
585 return minDiscountFactor_;
586 }
587}
588
591 return maxVol_ * simMarket_->baseScenarioAbsolute()->get(key);
593 return maxVol_ * simMarket_->baseScenarioAbsolute()->get(key);
598 return maxDiscountFactor_ / simMarket_->baseScenarioAbsolute()->get(key);
599 } else {
600 return maxDiscountFactor_;
601 }
602}
603} // namespace analytics
604} // namespace ore
double maturityTime(const RiskFactorKey &key) const
compute the time to tenor of the risk factor key
bool scenarioCanBeConverted(const StressTestScenarioData::StressTestData &parStressScenario) const
check if the scenario can be converted
QuantLib::ext::shared_ptr< ore::analytics::ScenarioSimMarketParameters > simMarketParams_
double shiftsSizeForScenario(const RiskFactorKey rfKey, double targetValue, double baseValue) const
convert the scenario value to the corresponding zero shift size for the stress test data
double getStressShift(const RiskFactorKey &key, const StressTestScenarioData::StressTestData &stressScenario) const
get the par stress shift size from stress test data
QuantLib::ext::shared_ptr< ore::analytics::SensitivityScenarioData > sensiScenarioData_
ore::analytics::StressTestScenarioData::StressTestData convertScenario(const StressTestScenarioData::StressTestData &scenario) const
Convert par shifts in a stress scenario to zero shifts.
double impliedParRate(const RiskFactorKey &key) const
Compute the implied fair rate of the par instrument.
const std::vector< RiskFactorKey > sortedParInstrumentRiskFactorKeys_
ParStressScenarioConverter(const QuantLib::Date &asof, const std::vector< RiskFactorKey > &sortedParInstrumentRiskFactorKeys, const QuantLib::ext::shared_ptr< ore::analytics::ScenarioSimMarketParameters > &simMarketParams, const QuantLib::ext::shared_ptr< ore::analytics::SensitivityScenarioData > &sensiScenarioData, const QuantLib::ext::shared_ptr< ore::analytics::ScenarioSimMarket > &simMarket, const ore::analytics::ParSensitivityInstrumentBuilder::Instruments &parInstruments, bool useSpreadedTermStructure)
double upperBound(const RiskFactorKey key) const
const ore::analytics::ParSensitivityInstrumentBuilder::Instruments & parInstruments_
void updateTargetStressTestScenarioData(StressTestScenarioData::StressTestData &stressScenario, const RiskFactorKey &key, const double zeroShift) const
add zero shifts in the stress test data
std::pair< size_t, size_t > getCapFloorTenorAndStrikeIds(const RiskFactorKey &rfKey) const
get the strike and tenor from a optionlet riskfactor key
double lowerBound(const RiskFactorKey key) const
QuantLib::ext::shared_ptr< ore::analytics::ScenarioSimMarket > simMarket_
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
#define LOG(text)
#define DLOG(text)
#define ALOG(text)
#define WLOG(text)
std::set< RiskFactorKey::KeyType > disabledParRates(bool irCurveParRates, bool irCapFloorParRates, bool creditParRates)
Volatility impliedVolatility(const QuantLib::CapFloor &cap, Real targetValue, const Handle< YieldTermStructure > &d, Volatility guess, VolatilityType type, Real displacement)
Real impliedQuote(const QuantLib::ext::shared_ptr< Instrument > &i)
bool checkCapFloorShiftData(const std::string &name, const StressTestScenarioData::CapFloorVolShiftData &stressShiftData, const std::map< std::string, QuantLib::ext::shared_ptr< SensitivityScenarioData::CapFloorVolShiftData > > &sensiData)
Checks the the strikes and expiries of cap floors in stresstest scenario are alligned with par sensit...
bool checkCurveShiftData(const std::string &name, const StressTestScenarioData::CurveShiftData &stressShiftData, const std::map< std::string, QuantLib::ext::shared_ptr< SensitivityScenarioData::CurveShiftData > > &sensiData)
Checks the the tenors for curves in a stresstest scenario are alligned with par sensitivity config.
std::string to_string(const LocationInfo &l)
Convert all par shifts in a single stress test scenario to a zero shifts.
A class to hold the parametrisation for building sensitivity scenarios.
std::map< ore::analytics::RiskFactorKey, QuantLib::ext::shared_ptr< QuantLib::Instrument > > parHelpers_
par helpers (all except cap/floors)
Date asof(14, Jun, 2018)
string name