21#include <ql/time/calendars/target.hpp>
31 const QuantLib::ext::shared_ptr<Scenario>& baseScenario,
32 const QuantLib::ext::shared_ptr<ScenarioSimMarketParameters>& simMarketData,
33 const QuantLib::ext::shared_ptr<ScenarioSimMarket>& simMarket,
34 const QuantLib::ext::shared_ptr<ScenarioFactory>& stressScenarioFactory,
35 const QuantLib::ext::shared_ptr<Scenario>& baseScenarioAbsolute)
37 stressScenarioFactory_(stressScenarioFactory),
38 baseScenarioAbsolute_(baseScenarioAbsolute == nullptr ? baseScenario : baseScenarioAbsolute) {
40 QL_REQUIRE(
stressData_,
"StressScenarioGenerator: stressData is null");
49 DLOG(
"Generate stress scenario #" << i <<
" '" <<
data.label <<
"'");
50 QuantLib::ext::shared_ptr<Scenario> scenario =
77 DLOG(
"stress scenario generator: all scenarios generated.");
81 QuantLib::ext::shared_ptr<Scenario>& scenario) {
83 string ccypair = d.first;
96 string foreign = ccypair.substr(0, 3);
97 string domestic = ccypair.substr(3);
98 QL_REQUIRE((domestic == baseCcy) || (foreign == baseCcy),
99 "SensitivityScenarioGenerator does not support cross FX pairs("
100 << ccypair <<
", but base currency is " << baseCcy <<
")");
102 TLOG(
"Apply stress scenario to fx " << ccypair);
111 Real rate = scenario->get(key);
112 Real newRate = relShift ? rate * (1.0 +
size) : (rate +
size);
114 stressData_->useSpreadedTermStructures() ? newRate / rate : newRate);
116 DLOG(
"FX scenarios done");
120 QuantLib::ext::shared_ptr<Scenario>& scenario) {
122 string equity = d.first;
132 Real newRate = relShift ? rate * (1.0 +
size) : (rate +
size);
134 stressData_->useSpreadedTermStructures() ? newRate / rate : newRate);
136 DLOG(
"Equity scenarios done");
140 QuantLib::ext::shared_ptr<Scenario>& scenario) {
144 string ccy = d.first;
145 TLOG(
"Apply stress scenario to discount curve " << ccy);
149 std::vector<Real> zeros(n_ten);
150 std::vector<Real> times(n_ten);
152 std::vector<Real> shiftedZeros(n_ten);
159 dc = s->discountCurve(ccy)->dayCounter();
161 QL_FAIL(
"Internal error: could not lock simMarket. Contact dev.");
164 for (Size j = 0; j < n_ten; ++j) {
166 times[j] = dc.yearFraction(
asof, d);
169 zeros[j] = -std::log(quote) / times[j];
172 std::vector<Period> shiftTenors =
data.shiftTenors;
173 QL_REQUIRE(shiftTenors.size() > 0,
"Discount shift tenors not specified");
174 std::vector<Real> shifts =
data.shifts;
175 QL_REQUIRE(shiftTenors.size() == shifts.size(),
"shift tenor and shift size vectors do not match");
176 std::vector<Time> shiftTimes(shiftTenors.size());
177 for (Size j = 0; j < shiftTenors.size(); ++j)
178 shiftTimes[j] = dc.yearFraction(
asof,
asof + shiftTenors[j]);
181 for (Size j = 0; j < shiftTenors.size(); ++j)
182 applyShift(j, shifts[j],
true, shiftType, shiftTimes, zeros, times, shiftedZeros, j == 0 ?
true :
false);
185 for (Size k = 0; k < n_ten; ++k) {
187 Real shiftedDiscount =
exp(-shiftedZeros[k] * times[k]);
189 Real discount =
exp(-zeros[k] * times[k]);
190 scenario->add(key, shiftedDiscount / discount);
192 scenario->add(key, shiftedDiscount);
196 DLOG(
"Discount curve stress scenarios done");
200 QuantLib::ext::shared_ptr<Scenario>& scenario) {
204 string name = d.first;
205 TLOG(
"Apply stress scenario to " <<
name);
209 std::vector<Real> zeros(n_ten);
210 std::vector<Real> times(n_ten);
212 std::vector<Real> shiftedZeros(n_ten);
219 dc = s->defaultCurve(
name)->curve()->dayCounter();
221 QL_FAIL(
"Internal error: could not lock simMarket. Contact dev.");
224 for (Size j = 0; j < n_ten; ++j) {
226 times[j] = dc.yearFraction(
asof, d);
229 zeros[j] = -std::log(quote) / times[j];
232 std::vector<Period> shiftTenors =
data.shiftTenors;
233 QL_REQUIRE(shiftTenors.size() > 0,
"Survival Probability shift tenors not specified");
234 std::vector<Real> shifts =
data.shifts;
235 QL_REQUIRE(shiftTenors.size() == shifts.size(),
"shift tenor and shift size vectors do not match");
236 std::vector<Time> shiftTimes(shiftTenors.size());
237 for (Size j = 0; j < shiftTenors.size(); ++j)
238 shiftTimes[j] = dc.yearFraction(
asof,
asof + shiftTenors[j]);
241 for (Size j = 0; j < shiftTenors.size(); ++j)
242 applyShift(j, shifts[j],
true, shiftType, shiftTimes, zeros, times, shiftedZeros, j == 0 ?
true :
false);
245 for (Size k = 0; k < n_ten; ++k) {
247 Real shiftedSurvivalProbability =
exp(-shiftedZeros[k] * times[k]);
249 Real survivalProbability =
exp(-zeros[k] * times[k]);
250 scenario->add(key, shiftedSurvivalProbability / survivalProbability);
252 scenario->add(key, shiftedSurvivalProbability);
256 DLOG(
"Default Curve stress scenarios done");
260 QuantLib::ext::shared_ptr<Scenario>& scenario) {
264 string indexName = d.first;
265 TLOG(
"Apply stress scenario to index curve " << indexName);
270 std::vector<Real> zeros(n_ten);
271 std::vector<Real> times(n_ten);
274 std::vector<Real> shiftedZeros(n_ten);
281 dc = s->iborIndex(indexName)->forwardingTermStructure()->dayCounter();
283 QL_FAIL(
"Internal error: could not lock simMarket. Contact dev.");
286 for (Size j = 0; j < n_ten; ++j) {
288 times[j] = dc.yearFraction(
asof, d);
291 zeros[j] = -std::log(quote) / times[j];
294 std::vector<Period> shiftTenors =
data.shiftTenors;
295 QL_REQUIRE(shiftTenors.size() > 0,
"Index curve shift tenors not specified");
296 std::vector<Real> shifts =
data.shifts;
297 QL_REQUIRE(shiftTenors.size() == shifts.size(),
"shift tenor and shift size vectors do not match");
298 std::vector<Time> shiftTimes(shiftTenors.size());
299 for (Size j = 0; j < shiftTenors.size(); ++j)
300 shiftTimes[j] = dc.yearFraction(
asof,
asof + shiftTenors[j]);
302 for (Size j = 0; j < shiftTenors.size(); ++j)
303 applyShift(j, shifts[j],
true, shiftType, shiftTimes, zeros, times, shiftedZeros, j == 0 ?
true :
false);
306 for (Size k = 0; k < n_ten; ++k) {
308 Real shiftedDiscount =
exp(-shiftedZeros[k] * times[k]);
310 Real discount =
exp(-zeros[k] * times[k]);
311 scenario->add(key, shiftedDiscount / discount);
313 scenario->add(key, shiftedDiscount);
317 DLOG(
"Index curve scenarios done");
321 QuantLib::ext::shared_ptr<Scenario>& scenario) {
325 string name = d.first;
326 TLOG(
"Apply stress scenario to yield curve " <<
name);
331 std::vector<Real> zeros(n_ten);
332 std::vector<Real> times(n_ten);
335 std::vector<Real> shiftedZeros(n_ten);
342 dc = s->yieldCurve(
name)->dayCounter();
344 QL_FAIL(
"Internal error: could not lock simMarket. Contact dev.");
347 for (Size j = 0; j < n_ten; ++j) {
349 times[j] = dc.yearFraction(
asof, d);
352 zeros[j] = -std::log(quote) / times[j];
355 std::vector<Period> shiftTenors =
data.shiftTenors;
356 QL_REQUIRE(shiftTenors.size() > 0,
"Yield curve shift tenors not specified");
357 std::vector<Real> shifts =
data.shifts;
358 QL_REQUIRE(shiftTenors.size() == shifts.size(),
"shift tenor and shift size vectors do not match");
359 std::vector<Time> shiftTimes(shiftTenors.size());
360 for (Size j = 0; j < shiftTenors.size(); ++j)
361 shiftTimes[j] = dc.yearFraction(
asof,
asof + shiftTenors[j]);
363 for (Size j = 0; j < shiftTenors.size(); ++j) {
367 applyShift(j, shifts[j],
true, shiftType, shiftTimes, zeros, times, shiftedZeros, j == 0 ?
true :
false);
371 for (Size k = 0; k < n_ten; ++k) {
373 Real shiftedDiscount =
exp(-shiftedZeros[k] * times[k]);
375 Real discount =
exp(-zeros[k] * times[k]);
376 scenario->add(key, shiftedDiscount / discount);
378 scenario->add(key, shiftedDiscount);
382 DLOG(
"Yield curve scenarios done");
386 QuantLib::ext::shared_ptr<Scenario>& scenario) {
390 string ccypair = d.first;
391 TLOG(
"Apply stress scenario to fx vol structure " << ccypair);
395 std::vector<Real> values(n_fxvol_exp);
396 std::vector<Real> times(n_fxvol_exp);
399 std::vector<Real> shiftedValues(n_fxvol_exp);
406 dc = s->fxVol(ccypair)->dayCounter();
408 QL_FAIL(
"Internal error: could not lock simMarket. Contact dev.");
410 for (Size j = 0; j < n_fxvol_exp; ++j) {
416 times[j] = dc.yearFraction(
asof, d);
420 std::vector<Period> shiftTenors =
data.shiftExpiries;
421 std::vector<Time> shiftTimes(shiftTenors.size());
422 vector<Real> shifts =
data.shifts;
423 QL_REQUIRE(shiftTenors.size() > 0,
"FX vol shift tenors not specified");
424 QL_REQUIRE(shiftTenors.size() == shifts.size(),
"shift tenor and shift size vectors do not match");
426 for (Size j = 0; j < shiftTenors.size(); ++j)
427 shiftTimes[j] = dc.yearFraction(
asof,
asof + shiftTenors[j]);
430 for (Size j = 0; j < shiftTenors.size(); ++j) {
432 applyShift(j, shifts[j],
true, shiftType, shiftTimes, values, times, shiftedValues, j == 0 ?
true :
false);
435 for (Size k = 0; k < n_fxvol_exp; ++k) {
438 scenario->add(key, shiftedValues[k] - values[k]);
440 scenario->add(key, shiftedValues[k]);
444 DLOG(
"FX vol scenarios done");
448 QuantLib::ext::shared_ptr<Scenario>& scenario) {
452 string equity = d.first;
453 TLOG(
"Apply stress scenario to equity vol structure " << equity);
454 Size n_eqvol_exp =
simMarketData_->equityVolExpiries(equity).size();
456 std::vector<Real> values(n_eqvol_exp);
457 std::vector<Real> times(n_eqvol_exp);
460 std::vector<Real> shiftedValues(n_eqvol_exp);
467 dc = s->equityVol(equity)->dayCounter();
469 QL_FAIL(
"Internal error: could not lock simMarket. Contact dev.");
471 for (Size j = 0; j < n_eqvol_exp; ++j) {
477 times[j] = dc.yearFraction(
asof, d);
481 std::vector<Period> shiftTenors =
data.shiftExpiries;
482 std::vector<Time> shiftTimes(shiftTenors.size());
483 vector<Real> shifts =
data.shifts;
484 QL_REQUIRE(shiftTenors.size() > 0,
"Equity vol shift tenors not specified");
485 QL_REQUIRE(shiftTenors.size() == shifts.size(),
"shift tenor and shift size vectors do not match");
487 for (Size j = 0; j < shiftTenors.size(); ++j)
488 shiftTimes[j] = dc.yearFraction(
asof,
asof + shiftTenors[j]);
491 for (Size j = 0; j < shiftTenors.size(); ++j) {
493 applyShift(j, shifts[j],
true, shiftType, shiftTimes, values, times, shiftedValues, j == 0 ?
true :
false);
496 for (Size k = 0; k < n_eqvol_exp; ++k) {
499 scenario->add(key, shiftedValues[k] - values[k]);
501 scenario->add(key, shiftedValues[k]);
505 DLOG(
"Equity vol scenarios done");
509 QuantLib::ext::shared_ptr<Scenario>& scenario) {
513 std::string key = d.first;
514 TLOG(
"Apply stress scenario to swaption vol structure '" << key <<
"'");
519 vector<vector<Real>> volData(n_swvol_exp, vector<Real>(n_swvol_term, 0.0));
520 vector<Real> volExpiryTimes(n_swvol_exp, 0.0);
521 vector<Real> volTermTimes(n_swvol_term, 0.0);
522 vector<vector<Real>> shiftedVolData(n_swvol_exp, vector<Real>(n_swvol_term, 0.0));
526 map<pair<Period, Period>, Real> shifts =
data.shifts;
528 vector<Real> shiftExpiryTimes(
data.shiftExpiries.size(), 0.0);
529 vector<Real> shiftTermTimes(
data.shiftTerms.size(), 0.0);
533 dc = s->swaptionVol(key)->dayCounter();
535 QL_FAIL(
"Internal error: could not lock simMarket. Contact dev.");
539 for (Size j = 0; j < n_swvol_exp; ++j) {
541 volExpiryTimes[j] = dc.yearFraction(
asof, expiry);
543 for (Size j = 0; j < n_swvol_term; ++j) {
545 volTermTimes[j] = dc.yearFraction(
asof, term);
547 for (Size j = 0; j < n_swvol_exp; ++j) {
548 for (Size k = 0; k < n_swvol_term; ++k) {
549 Size idx = j * n_swvol_term + k;
557 for (Size j = 0; j < shiftExpiryTimes.size(); ++j)
558 shiftExpiryTimes[j] = dc.yearFraction(
asof,
asof +
data.shiftExpiries[j]);
559 for (Size j = 0; j < shiftTermTimes.size(); ++j)
560 shiftTermTimes[j] = dc.yearFraction(
asof,
asof +
data.shiftTerms[j]);
564 for (Size j = 0; j < shiftExpiryTimes.size(); ++j) {
565 for (Size k = 0; k < shiftTermTimes.size(); ++k) {
568 pair<Period, Period> key(
data.shiftExpiries[j],
data.shiftTerms[k]);
569 if (shifts.size() == 0)
570 shift =
data.parallelShiftSize;
572 QL_REQUIRE(shifts.find(key) != shifts.end(),
"swaption vol shift not found for expiry "
573 <<
data.shiftExpiries[j] <<
" and term "
574 <<
data.shiftTerms[k]);
577 applyShift(j, k, shift,
true, shiftType, shiftExpiryTimes, shiftTermTimes, volExpiryTimes, volTermTimes,
578 volData, shiftedVolData, j == 0 && k == 0);
583 for (Size jj = 0; jj < n_swvol_exp; ++jj) {
584 for (Size kk = 0; kk < n_swvol_term; ++kk) {
585 Size idx = jj * n_swvol_term + kk;
588 scenario->add(rfkey, shiftedVolData[jj][kk] - volData[jj][kk]);
590 scenario->add(rfkey, shiftedVolData[jj][kk]);
595 DLOG(
"Swaption vol scenarios done");
599 QuantLib::ext::shared_ptr<Scenario>& scenario) {
603 std::string key = d.first;
604 TLOG(
"Apply stress scenario to cap/floor vol structure " << key);
606 vector<Real> volStrikes =
simMarketData_->capFloorVolStrikes(key);
608 if (volStrikes.empty()) {
611 Size n_cfvol_strikes = volStrikes.size();
613 Size n_cfvol_exp =
simMarketData_->capFloorVolExpiries(key).size();
614 vector<vector<Real>> volData(n_cfvol_exp, vector<Real>(n_cfvol_strikes, 0.0));
615 vector<Real> volExpiryTimes(n_cfvol_exp, 0.0);
616 vector<vector<Real>> shiftedVolData(n_cfvol_exp, vector<Real>(n_cfvol_strikes, 0.0));
622 vector<Real> shiftExpiryTimes(
data.shiftExpiries.size(), 0.0);
623 vector<Real> shiftStrikes =
data.shiftStrikes.empty() ? volStrikes :
data.shiftStrikes;
625 vector<vector<Real>> shifts;
626 for (
size_t i = 0; i <
data.shiftExpiries.size(); ++i) {
627 const auto tenor =
data.shiftExpiries[i];
628 if (
data.shiftStrikes.empty()){
629 const double shift =
data.shifts[tenor].front();
630 shifts.push_back(std::vector<Real>(volStrikes.size(), shift));
632 shifts.push_back(
data.shifts[tenor]);
639 dc = s->capFloorVol(key)->dayCounter();
641 QL_FAIL(
"Internal error: could not lock simMarket. Contact dev.");
645 for (Size j = 0; j < n_cfvol_exp; ++j) {
647 volExpiryTimes[j] = dc.yearFraction(
asof, expiry);
649 for (Size j = 0; j < n_cfvol_exp; ++j) {
650 for (Size k = 0; k < n_cfvol_strikes; ++k) {
651 Size idx = j * n_cfvol_strikes + k;
658 for (Size j = 0; j < shiftExpiryTimes.size(); ++j)
659 shiftExpiryTimes[j] = dc.yearFraction(
asof,
asof +
data.shiftExpiries[j]);
663 for (Size j = 0; j < shiftExpiryTimes.size(); ++j) {
664 for (Size k = 0; k < shiftStrikes.size(); ++k) {
665 applyShift(j, k, shifts[j][k],
true, shiftType, shiftExpiryTimes, shiftStrikes, volExpiryTimes, volStrikes,
666 volData, shiftedVolData, j == 0 && k == 0);
671 for (Size jj = 0; jj < n_cfvol_exp; ++jj) {
672 for (Size kk = 0; kk < n_cfvol_strikes; ++kk) {
673 Size idx = jj * n_cfvol_strikes + kk;
676 scenario->add(rfkey, shiftedVolData[jj][kk] - volData[jj][kk]);
678 scenario->add(rfkey, shiftedVolData[jj][kk]);
683 DLOG(
"Optionlet vol scenarios done");
687 QuantLib::ext::shared_ptr<Scenario>& scenario) {
689 string bond = d.first;
690 TLOG(
"Apply stress scenario to security spread " << bond);
699 Real newSpread = relShift ? base_spread * (1.0 +
size) : (base_spread +
size);
701 stressData_->useSpreadedTermStructures() ? newSpread - base_spread : newSpread);
703 DLOG(
"Security spread scenarios done");
707 QuantLib::ext::shared_ptr<Scenario>& scenario) {
709 string isin = d.first;
710 TLOG(
"Apply stress scenario to recovery rate " << isin);
718 Real new_recoveryRate = relShift ? base_recoveryRate * (1.0 +
size) : (base_recoveryRate +
size);
720 stressData_->useSpreadedTermStructures() ? new_recoveryRate - base_recoveryRate
723 DLOG(
"Recovery rate scenarios done");
Data types stored in the scenario class.
Shift Scenario Generator.
std::vector< QuantLib::ext::shared_ptr< Scenario > > scenarios_
const QuantLib::ext::shared_ptr< Scenario > baseScenario_
void applyShift(Size j, Real shiftSize, bool up, ShiftType type, const vector< Time > &shiftTimes, const vector< Real > &values, const vector< Time > ×, vector< Real > &shiftedValues, bool initialise)
Apply 1d triangular shift to 1d data such as yield curves, public to allow test suite access.
const QuantLib::ext::weak_ptr< ScenarioSimMarket > simMarket_
const QuantLib::ext::shared_ptr< ScenarioSimMarketParameters > simMarketData_
void addCapFloorVolShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
void addFxVolShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
void addSwaptionVolShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
StressScenarioGenerator(const QuantLib::ext::shared_ptr< StressTestScenarioData > &stressData, const QuantLib::ext::shared_ptr< Scenario > &baseScenario, const QuantLib::ext::shared_ptr< ScenarioSimMarketParameters > &simMarketData, const QuantLib::ext::shared_ptr< ScenarioSimMarket > &simMarket, const QuantLib::ext::shared_ptr< ScenarioFactory > &stressScenarioFactory, const QuantLib::ext::shared_ptr< Scenario > &baseScenarioAbsolute=nullptr)
Constructor.
void addDiscountCurveShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
QuantLib::ext::shared_ptr< Scenario > baseScenarioAbsolute_
void addIndexCurveShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
void addYieldCurveShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
void addEquityShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
void addEquityVolShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
void addSecuritySpreadShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
QuantLib::ext::shared_ptr< ScenarioFactory > stressScenarioFactory_
void addRecoveryRateShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
void addSurvivalProbabilityShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
QuantLib::ext::shared_ptr< StressTestScenarioData > stressData_
void addFxShifts(StressTestScenarioData::StressTestData &data, QuantLib::ext::shared_ptr< Scenario > &scenario)
RandomVariable exp(RandomVariable x)
Size size(const ValueType &v)
Stress scenario generation.
map< string, CurveShiftData > discountCurveShifts
map< string, CurveShiftData > yieldCurveShifts
map< string, VolShiftData > fxVolShifts
map< string, SpotShiftData > fxShifts
map< string, CurveShiftData > survivalProbabilityShifts
map< string, SpotShiftData > recoveryRateShifts
map< string, SwaptionVolShiftData > swaptionVolShifts
map< string, SpotShiftData > equityShifts
map< string, SpotShiftData > securitySpreadShifts
map< string, CapFloorVolShiftData > capVolShifts
map< string, CurveShiftData > indexCurveShifts
map< string, VolShiftData > equityVolShifts