Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
crossassetmodelscenariogenerator.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
22
24
25using namespace QuantLib;
26using namespace QuantExt;
27using namespace std;
28
29namespace ore {
30namespace analytics {
31
33 QuantLib::ext::shared_ptr<QuantExt::CrossAssetModel> model,
34 QuantLib::ext::shared_ptr<QuantExt::MultiPathGeneratorBase> pathGenerator,
35 QuantLib::ext::shared_ptr<ScenarioFactory> scenarioFactory, QuantLib::ext::shared_ptr<ScenarioSimMarketParameters> simMarketConfig,
36 Date today, QuantLib::ext::shared_ptr<DateGrid> grid, QuantLib::ext::shared_ptr<ore::data::Market> initMarket,
37 const std::string& configuration)
38 : ScenarioPathGenerator(today, grid->dates(), grid->timeGrid()), model_(model), pathGenerator_(pathGenerator),
39 scenarioFactory_(scenarioFactory), simMarketConfig_(simMarketConfig), initMarket_(initMarket),
40 configuration_(configuration) {
41
42 LOG("CrossAssetModelScenarioGenerator ctor called");
43
44 QL_REQUIRE(initMarket != NULL, "CrossAssetScenarioGenerator: initMarket is null");
45 QL_REQUIRE(timeGrid_.size() == dates_.size() + 1, "date/time grid size mismatch");
46
47 // TODO, curve tenors might be overwritten by dates in simMarketConfig_, here we just take the tenors
48
49 DayCounter dc = model_->irModel(0)->termStructure()->dayCounter();
50 n_ccy_ = model_->components(CrossAssetModel::AssetType::IR);
51 n_eq_ = model_->components(CrossAssetModel::AssetType::EQ);
52 n_inf_ = model_->components(CrossAssetModel::AssetType::INF);
53 n_cr_ = model_->components(CrossAssetModel::AssetType::CR);
54 n_com_ = model_->components(CrossAssetModel::AssetType::COM);
55 n_crstates_ = model_->components(CrossAssetModel::AssetType::CrState);
56 n_survivalweights_ = simMarketConfig_->additionalScenarioDataSurvivalWeights().size();
57 n_indices_ = simMarketConfig_->indices().size();
58 n_curves_ = simMarketConfig_->yieldCurveNames().size();
59
60 // Cache yield curve keys
61 discountCurveKeys_.reserve(n_ccy_ * simMarketConfig_->yieldCurveTenors("").size());
62 for (Size j = 0; j < model_->components(CrossAssetModel::AssetType::IR); j++) {
63 std::string ccy = model_->parametrizations()[j]->currency().code();
64 ten_dsc_.push_back(simMarketConfig_->yieldCurveTenors(ccy));
65 Size n_ten = ten_dsc_.back().size();
66 for (Size k = 0; k < n_ten; k++)
67 discountCurveKeys_.emplace_back(RiskFactorKey::KeyType::DiscountCurve, ccy, k); // j * n_ten + k
68 }
69
70 // Cache index curve keys
71 indexCurveKeys_.reserve(n_indices_ * simMarketConfig_->yieldCurveTenors("").size());
72 for (Size j = 0; j < n_indices_; ++j) {
73 ten_idx_.push_back(simMarketConfig_->yieldCurveTenors(simMarketConfig_->indices()[j]));
74 Size n_ten = ten_idx_.back().size();
75 for (Size k = 0; k < n_ten; ++k) {
77 }
78 }
79
80 // Cache yield curve keys
81 Size n_curves = simMarketConfig_->yieldCurveNames().size();
82 yieldCurveKeys_.reserve(n_curves * simMarketConfig_->yieldCurveTenors("").size());
83 for (Size j = 0; j < n_curves; ++j) {
84 ten_yc_.push_back(simMarketConfig_->yieldCurveTenors(simMarketConfig_->yieldCurveNames()[j]));
85 Size n_ten = ten_yc_.back().size();
86 for (Size k = 0; k < n_ten; ++k) {
87 yieldCurveKeys_.emplace_back(RiskFactorKey::KeyType::YieldCurve, simMarketConfig_->yieldCurveNames()[j], k);
88 }
89 }
90
91 // Cache commodity curve keys
92 if (n_com_ > 0) {
93 commodityCurveKeys_.reserve(n_com_ * simMarketConfig_->commodityCurveTenors("").size());
94 for (Size j = 0; j < n_com_; j++) {
95 std::string name = simMarketConfig_->commodityNames()[j];
96 ten_com_.push_back(simMarketConfig_->commodityCurveTenors(name));
97 Size n_ten = ten_com_.back().size();
98 for (Size k = 0; k < n_ten; k++)
99 commodityCurveKeys_.emplace_back(RiskFactorKey::KeyType::CommodityCurve, name, k); // j * n_ten + k
100 }
101 }
102
103 // Cache FX rate keys
104 fxKeys_.reserve(n_ccy_ - 1);
105 for (Size k = 0; k < n_ccy_ - 1; k++) {
106 const string& foreign = model_->parametrizations()[k + 1]->currency().code();
107 const string& domestic = model_->parametrizations()[0]->currency().code();
108 fxKeys_.emplace_back(RiskFactorKey::KeyType::FXSpot, foreign + domestic); // k
109 }
110
111 // set up CrossAssetModelImpliedFxVolTermStructures
112 if (simMarketConfig_->simulateFXVols()) {
113 DLOG("CrossAssetModel is simulating FX vols");
114 QL_REQUIRE(model_->modelType(CrossAssetModel::AssetType::IR, 0) == CrossAssetModel::ModelType::LGM1F,
115 "Simulation of FX vols is only supported for LGM1F ir model type.");
116 for (Size k = 0; k < simMarketConfig_->fxVolCcyPairs().size(); k++) {
117 // Calculating the index is messy
118 const string pair = simMarketConfig_->fxVolCcyPairs()[k];
119 DLOG("Set up CrossAssetModelImpliedFxVolTermStructures for " << pair);
120 QL_REQUIRE(pair.size() == 6, "Invalid ccypair " << pair);
121 const string& domestic = pair.substr(0, 3);
122 const string& foreign = pair.substr(3);
123 QL_REQUIRE(domestic == model_->parametrizations()[0]->currency().code(), "Only DOM-FOR fx vols supported");
124 Size index = model->ccyIndex(parseCurrency(foreign)); // will throw if foreign not there
125 QL_REQUIRE(index > 0, "Invalid index for ccy " << foreign << " should be > 0");
126 // fxVols_ are indexed by ccyPairs
127 DLOG("Pair " << pair << " index " << index);
128 // index - 1 to convert "IR" index into an "FX" index
129 fxVols_.push_back(QuantLib::ext::make_shared<CrossAssetModelImpliedFxVolTermStructure>(model_, index - 1));
130 DLOG("Set up CrossAssetModelImpliedFxVolTermStructures for " << pair << " done");
131 }
132 }
133
134 // Cache EQ rate keys
135 Size n_eq = model_->components(CrossAssetModel::AssetType::EQ);
136 eqKeys_.reserve(n_eq);
137 for (Size k = 0; k < n_eq; k++) {
138 const string& eqName = model_->eqbs(k)->name();
139 eqKeys_.emplace_back(RiskFactorKey::KeyType::EquitySpot, eqName);
140 }
141
142 // equity vols
143 if (simMarketConfig_->equityVolNames().size() > 0 && simMarketConfig_->simulateEquityVols()) {
144 DLOG("CrossAssetModel is simulating EQ vols");
145 QL_REQUIRE(model_->modelType(CrossAssetModel::AssetType::IR, 0) == CrossAssetModel::ModelType::LGM1F,
146 "Simulation of EQ vols is only supported for LGM1F ir model type.");
147 for (Size k = 0; k < simMarketConfig_->equityVolNames().size(); k++) {
148 // Calculating the index is messy
149 const string equityName = simMarketConfig_->equityVolNames()[k];
150 DLOG("Set up CrossAssetModelImpliedEqVolTermStructures for " << equityName);
151 Size eqIndex = model->eqIndex(equityName);
152 // eqVols_ are indexed by ccyPairs
153 DLOG("EQ Vol Name = " << equityName << ", index = " << eqIndex);
154 // index - 1 to convert "IR" index into an "FX" index
155 eqVols_.push_back(QuantLib::ext::make_shared<CrossAssetModelImpliedEqVolTermStructure>(model_, eqIndex));
156 DLOG("Set up CrossAssetModelImpliedEqVolTermStructures for " << equityName << " done");
157 }
158 }
159
160 // Cache INF rate keys
161 Size n_inf = model_->components(CrossAssetModel::AssetType::INF);
162 if (n_inf > 0) {
163 cpiKeys_.reserve(n_inf);
164 for (Size j = 0; j < n_inf; ++j) {
165 cpiKeys_.emplace_back(RiskFactorKey::KeyType::CPIIndex, model->inf(j)->name());
166 }
167
168 Size n_zeroinf = simMarketConfig_->zeroInflationIndices().size();
169 if (n_zeroinf > 0) {
170 zeroInflationKeys_.reserve(n_zeroinf * simMarketConfig_->zeroInflationTenors("").size());
171 for (Size j = 0; j < n_zeroinf; ++j) {
172 ten_zinf_.push_back(simMarketConfig_->zeroInflationTenors(simMarketConfig_->zeroInflationIndices()[j]));
173 Size n_ten = ten_zinf_.back().size();
174 for (Size k = 0; k < n_ten; ++k) {
176 simMarketConfig_->zeroInflationIndices()[j], k);
177 }
178 }
179 }
180
181 Size n_yoyinf = simMarketConfig_->yoyInflationIndices().size();
182 if (n_yoyinf > 0) {
183 yoyInflationKeys_.reserve(n_yoyinf * simMarketConfig_->yoyInflationTenors("").size());
184 for (Size j = 0; j < n_yoyinf; ++j) {
185 ten_yinf_.push_back(simMarketConfig_->yoyInflationTenors(simMarketConfig_->yoyInflationIndices()[j]));
186 Size n_ten = ten_yinf_.back().size();
187 for (Size k = 0; k < n_ten; ++k) {
189 simMarketConfig_->yoyInflationIndices()[j], k);
190 }
191 }
192 }
193 }
194
195 // Cache default curve keys
196 Size n_cr = model_->components(CrossAssetModel::AssetType::CR);
197 defaultCurveKeys_.reserve(n_cr * simMarketConfig_->defaultTenors("").size());
198 for (Size j = 0; j < model_->components(CrossAssetModel::AssetType::CR); j++) {
199 std::string cr_name = model_->cr(j)->name();
200 ten_dfc_.push_back(simMarketConfig_->defaultTenors(cr_name));
201 Size n_ten = ten_dfc_.back().size();
202 for (Size k = 0; k < n_ten; k++) {
203 defaultCurveKeys_.emplace_back(RiskFactorKey::KeyType::SurvivalProbability, cr_name, k); // j * n_ten + k
204 }
205 }
206
207 // Cache CrState keys
208 crStateKeys_.reserve(n_crstates_);
209 for (Size j = 0; j < n_crstates_; ++j) {
210 ostringstream numStr;
211 numStr << j;
212 crStateKeys_.emplace_back(RiskFactorKey::KeyType::CreditState, numStr.str());
213 }
214
216 for (Size j = 0; j < n_survivalweights_; ++j) {
217 string name = simMarketConfig_->additionalScenarioDataSurvivalWeights()[j];
221 }
222
223 // cache curves
224
225 for (Size j = 0; j < n_ccy_; ++j) {
226 curves_.push_back(QuantLib::ext::make_shared<QuantExt::ModelImpliedYieldTermStructure>(model_->irModel(j), dc, true));
227 }
228
229 for (Size j = 0; j < n_indices_; ++j) {
230 std::string indexName = simMarketConfig_->indices()[j];
231 QuantLib::ext::shared_ptr<IborIndex> index = *initMarket_->iborIndex(indexName, configuration_);
232 Handle<YieldTermStructure> fts = index->forwardingTermStructure();
233 auto impliedFwdCurve = QuantLib::ext::make_shared<ModelImpliedYtsFwdFwdCorrected>(
234 model_->irModel(model_->ccyIndex(index->currency())), fts, dc, false);
235 fwdCurves_.push_back(impliedFwdCurve);
236 indices_.push_back(index->clone(Handle<YieldTermStructure>(impliedFwdCurve)));
237 }
238
239 for (Size j = 0; j < n_curves_; ++j) {
240 std::string curveName = simMarketConfig_->yieldCurveNames()[j];
241 Currency ccy = ore::data::parseCurrency(simMarketConfig_->yieldCurveCurrencies().at(curveName));
242 Handle<YieldTermStructure> yts = initMarket_->yieldCurve(curveName, configuration_);
243 auto impliedYieldCurve =
244 QuantLib::ext::make_shared<ModelImpliedYtsFwdFwdCorrected>(model_->irModel(model_->ccyIndex(ccy)), yts, dc, false);
245 yieldCurves_.push_back(impliedYieldCurve);
246 yieldCurveCurrency_.push_back(ccy);
247 }
248
249 for (Size j = 0; j < n_com_; ++j) {
250 QuantLib::ext::shared_ptr<CommodityModel> cm = model_->comModel(j);
251 auto pts = QuantLib::ext::make_shared<QuantExt::ModelImpliedPriceTermStructure>(model_->comModel(j), dc, true);
252 comCurves_.push_back(pts);
253 }
254
255 // Cache data regarding the zero inflation curves to avoid repeating in date loop below.
256 // This vector needs to follow the order of the simMarketConfig_->zeroInflationIndices().
257 // 1st element in tuple is the index of the inflation component in the CAM.
258 // 2nd element in tuple is the index of the inflation component's currency in the CAM.
259 // 3rd element in tuple is the type of inflation model in the CAM.
260 // 4th element in tuple is the CAM implied inflation term structure.
261 for (const auto& name : simMarketConfig_->zeroInflationIndices()) {
262 auto idx = model_->infIndex(name);
263 auto ccyIdx = model_->ccyIndex(model_->inf(idx)->currency());
264 auto mt = model_->modelType(CrossAssetModel::AssetType::INF, idx);
265 QL_REQUIRE(mt == CrossAssetModel::ModelType::DK || mt == CrossAssetModel::ModelType::JY,
266 "CrossAssetModelScenarioGenerator: expected inflation model to be JY or DK.");
267 QuantLib::ext::shared_ptr<ZeroInflationModelTermStructure> ts;
268
269
270 if (mt == CrossAssetModel::ModelType::DK) {
271 ts = QuantLib::ext::make_shared<DkImpliedZeroInflationTermStructure>(
272 model_, idx);
273 } else {
274 ts = QuantLib::ext::make_shared<JyImpliedZeroInflationTermStructure>(model_, idx);
275 QL_REQUIRE(model_->modelType(CrossAssetModel::AssetType::IR, 0) == CrossAssetModel::ModelType::LGM1F,
276 "Simulation of INF JY model is only supported for LGM1F ir model type.");
277 }
278 zeroInfCurves_.emplace_back(idx, ccyIdx, mt, ts);
279 }
280
281 // Same logic here as for the zeroInfCurves above.
282 for (const auto& name : simMarketConfig_->yoyInflationIndices()) {
283 auto idx = model_->infIndex(name);
284 auto ccyIdx = model_->ccyIndex(model_->inf(idx)->currency());
285 auto mt = model_->modelType(CrossAssetModel::AssetType::INF, idx);
286 QL_REQUIRE(mt == CrossAssetModel::ModelType::DK || mt == CrossAssetModel::ModelType::JY,
287 "CrossAssetModelScenarioGenerator: expected inflation model to be JY or DK.");
288 QuantLib::ext::shared_ptr<YoYInflationModelTermStructure> ts;
289 if (mt == CrossAssetModel::ModelType::DK) {
290 ts = QuantLib::ext::make_shared<DkImpliedYoYInflationTermStructure>(
291 model_, idx, false);
292 } else {
293 ts = QuantLib::ext::make_shared<JyImpliedYoYInflationTermStructure>(
294 model_, idx, false);
295 }
296 QL_REQUIRE(model_->modelType(CrossAssetModel::AssetType::IR, 0) == CrossAssetModel::ModelType::LGM1F,
297 "Simulation of INF DK or JY model for YoY curves is only supported for LGM1F ir model type.");
298 yoyInfCurves_.emplace_back(idx, ccyIdx, mt, ts);
299 }
300
301 for (Size j = 0; j < n_cr_; ++j) {
302 if (model_->modelType(CrossAssetModel::AssetType::CR, j) == CrossAssetModel::ModelType::LGM1F) {
303 lgmDefaultCurves_.push_back(QuantLib::ext::make_shared<QuantExt::LgmImpliedDefaultTermStructure>(
304 model_, j, model_->ccyIndex(model_->crlgm1f(j)->currency())));
305 cirppDefaultCurves_.push_back(QuantLib::ext::shared_ptr<QuantExt::CirppImpliedDefaultTermStructure>());
306 } else if (model_->modelType(CrossAssetModel::AssetType::CR, j) == CrossAssetModel::ModelType::CIRPP) {
307 lgmDefaultCurves_.push_back(QuantLib::ext::shared_ptr<QuantExt::LgmImpliedDefaultTermStructure>());
308 cirppDefaultCurves_.push_back(
309 QuantLib::ext::make_shared<QuantExt::CirppImpliedDefaultTermStructure>(model_->crcirppModel(j), j));
310 }
311 }
312
313 LOG("CrossAssetModelScenarioGenerator ctor done");
314}
315
316namespace {
317void copyPathToArray(const MultiPath& p, Size t, Size a, Array& target) {
318 for (Size k = 0; k < target.size(); ++k)
319 target[k] = p[a + k][t];
320}
321} // namespace
322
323std::vector<QuantLib::ext::shared_ptr<Scenario>> CrossAssetModelScenarioGenerator::nextPath() {
324 std::vector<QuantLib::ext::shared_ptr<Scenario>> scenarios(dates_.size());
325 QL_REQUIRE(pathGenerator_ != nullptr, "CrossAssetModelScenarioGenerator::nextPath(): pathGenerator is null");
326 Sample<MultiPath> sample = pathGenerator_->next();
327 DayCounter dc = model_->irModel(0)->termStructure()->dayCounter();
328
329 std::vector<Array> ir_state(n_ccy_);
330 for (Size j = 0; j < n_ccy_; ++j)
331 ir_state[j] = Array(model_->irModel(j)->n());
332
333 Array ir_state_aux(model_->irModel(0)->n_aux());
334
335 std::vector<Size> indexCcyIdx(n_indices_);
336 for (Size j = 0; j < n_indices_; ++j)
337 indexCcyIdx[j] = model_->ccyIndex(indices_[j]->currency());
338
339 std::vector<Size> yieldCurveCcyIdx(n_curves_);
340 for (Size j = 0; j < n_curves_; ++j)
341 yieldCurveCcyIdx[j] = model_->ccyIndex(yieldCurveCurrency_[j]);
342
343 for (Size i = 0; i < dates_.size(); i++) {
344 Real t = timeGrid_[i + 1]; // recall: time grid has inserted t=0
345
346 scenarios[i] = scenarioFactory_->buildScenario(dates_[i], true);
347
348 // populate IR states
349 copyPathToArray(sample.value, i + 1, model_->pIdx(CrossAssetModel::AssetType::IR, 0), ir_state[0]);
350 copyPathToArray(sample.value, i + 1, model_->pIdx(CrossAssetModel::AssetType::IR, 0) + ir_state[0].size(),
351 ir_state_aux);
352 for (Size j = 1; j < n_ccy_; ++j)
353 copyPathToArray(sample.value, i + 1, model_->pIdx(CrossAssetModel::AssetType::IR, j), ir_state[j]);
354
355 // Set numeraire from domestic ir process
356 scenarios[i]->setNumeraire(model_->numeraire(0, t, ir_state[0], Handle<YieldTermStructure>(), ir_state_aux));
357
358 // Discount curves
359 for (Size j = 0; j < n_ccy_; j++) {
360 curves_[j]->move(t, ir_state[j]);
361 for (Size k = 0; k < ten_dsc_[j].size(); k++) {
362 Date d = dates_[i] + ten_dsc_[j][k];
363 Time T = dc.yearFraction(dates_[i], d);
364 Real discount = std::max(curves_[j]->discount(T), 0.00001);
365 scenarios[i]->add(discountCurveKeys_[j * ten_dsc_[j].size() + k], discount);
366 }
367 }
368
369 // Index curves and Index fixings
370 for (Size j = 0; j < n_indices_; ++j) {
371 fwdCurves_[j]->move(dates_[i], ir_state[indexCcyIdx[j]]);
372 for (Size k = 0; k < ten_idx_[j].size(); ++k) {
373 Date d = dates_[i] + ten_idx_[j][k];
374 Time T = dc.yearFraction(dates_[i], d);
375 Real discount = std::max(fwdCurves_[j]->discount(T), 0.00001);
376 scenarios[i]->add(indexCurveKeys_[j * ten_idx_[j].size() + k], discount);
377 }
378 }
379
380 // Yield curves
381 for (Size j = 0; j < n_curves_; ++j) {
382 yieldCurves_[j]->move(dates_[i], ir_state[yieldCurveCcyIdx[j]]);
383 for (Size k = 0; k < ten_yc_[j].size(); ++k) {
384 Date d = dates_[i] + ten_yc_[j][k];
385 Time T = dc.yearFraction(dates_[i], d);
386 Real discount = std::max(yieldCurves_[j]->discount(T), 0.00001);
387 scenarios[i]->add(yieldCurveKeys_[j * ten_yc_[j].size() + k], discount);
388 }
389 }
390
391 // FX rates
392 for (Size k = 0; k < n_ccy_ - 1; k++) {
393 Real fx = std::exp(sample.value[model_->pIdx(CrossAssetModel::AssetType::FX, k)][i + 1]);
394 scenarios[i]->add(fxKeys_[k], fx);
395 }
396
397 // FX vols
398 if (simMarketConfig_->simulateFXVols()) {
399 for (Size k = 0; k < simMarketConfig_->fxVolCcyPairs().size(); k++) {
400 const string ccyPair = simMarketConfig_->fxVolCcyPairs()[k];
401 const vector<Period>& expires = simMarketConfig_->fxVolExpiries(ccyPair);
402
403 Size fxIndex = fxVols_[k]->fxIndex();
404 Real zFor = sample.value[fxIndex + 1][i + 1];
405 Real logFx = sample.value[n_ccy_ + fxIndex][i + 1]; // multiplies USD amount to get EUR
406 fxVols_[k]->move(dates_[i], ir_state[0][0], zFor, logFx);
407
408 for (Size j = 0; j < expires.size(); j++) {
409 Real vol = fxVols_[k]->blackVol(dates_[i] + expires[j], Null<Real>(), true);
410 scenarios[i]->add(RiskFactorKey(RiskFactorKey::KeyType::FXVolatility, ccyPair, j), vol);
411 }
412 }
413 }
414
415 // Equity spots
416 for (Size k = 0; k < n_eq_; k++) {
417 Real eqSpot = std::exp(sample.value[model_->pIdx(CrossAssetModel::AssetType::EQ, k)][i + 1]);
418 scenarios[i]->add(eqKeys_[k], eqSpot);
419 }
420
421 // Equity vols
422 if (simMarketConfig_->simulateEquityVols()) {
423 for (Size k = 0; k < simMarketConfig_->equityVolNames().size(); k++) {
424 const string equityName = simMarketConfig_->equityVolNames()[k];
425
426 const vector<Period>& expiries = simMarketConfig_->equityVolExpiries(equityName);
427
428 Size eqIndex = eqVols_[k]->equityIndex();
429 Size eqCcyIdx = eqVols_[k]->eqCcyIndex();
430 Real z_eqIr = sample.value[eqCcyIdx][i + 1];
431 Real logEq = sample.value[eqIndex][i + 1];
432 eqVols_[k]->move(dates_[i], z_eqIr, logEq);
433
434 for (Size j = 0; j < expiries.size(); j++) {
435 Real vol = eqVols_[k]->blackVol(dates_[i] + expiries[j], Null<Real>(), true);
436 scenarios[i]->add(RiskFactorKey(RiskFactorKey::KeyType::EquityVolatility, equityName, j), vol);
437 }
438 }
439 }
440
441 // Inflation index values
442 for (Size j = 0; j < n_inf_; j++) {
443
444 // Depending on type of model, i.e. DK or JY, z and y mean different things.
445 Real z = sample.value[model_->pIdx(CrossAssetModel::AssetType::INF, j, 0)][i + 1];
446 Real y = sample.value[model_->pIdx(CrossAssetModel::AssetType::INF, j, 1)][i + 1];
447
448 // Could possibly cache the model type outside the loop to improve performance.
449 Real cpi = 0.0;
450 if (model_->modelType(CrossAssetModel::AssetType::INF, j) == CrossAssetModel::ModelType::JY) {
451 cpi = std::exp(sample.value[model_->pIdx(CrossAssetModel::AssetType::INF, j, 1)][i + 1]);
452 } else if (model_->modelType(CrossAssetModel::AssetType::INF, j) == CrossAssetModel::ModelType::DK) {
453 auto index = *initMarket_->zeroInflationIndex(model_->inf(j)->name());
454 Date baseDate = index->zeroInflationTermStructure()->baseDate();
455 auto zts = index->zeroInflationTermStructure();
456 Time relativeTime = inflationYearFraction(zts->frequency(), false, zts->dayCounter(),
457 baseDate, dates_[i] - zts->observationLag());
458 std::tie(cpi, std::ignore) = model_->infdkI(j, relativeTime, relativeTime, z, y);
459 cpi *= index->fixing(baseDate);
460 } else {
461 QL_FAIL("CrossAssetModelScenarioGenerator: expected inflation model to be JY or DK.");
462 }
463
464 scenarios[i]->add(cpiKeys_[j], cpi);
465 }
466
467 // Zero inflation curves
468 for (Size j = 0; j < zeroInfCurves_.size(); ++j) {
469
470 auto tup = zeroInfCurves_[j];
471
472 // State variables needed depends on model, 3 for JY and 2 for DK.
473 auto idx = std::get<0>(tup);
474 Array state(3);
475 state[0] = sample.value[model_->pIdx(CrossAssetModel::AssetType::INF, idx, 0)][i + 1];
476 state[1] = sample.value[model_->pIdx(CrossAssetModel::AssetType::INF, idx, 1)][i + 1];
477 if (std::get<2>(tup) == CrossAssetModel::ModelType::DK) {
478 state.resize(2);
479 } else {
480 state[2] = ir_state[std::get<1>(tup)][0];
481 }
482
483 // Update the term structure's date and state.
484 auto ts = std::get<3>(tup);
485 ts->move(dates_[i], state);
486
487 // Populate the zero inflation scenario values based on the current date and state.
488 for (Size k = 0; k < ten_zinf_[j].size(); k++) {
489 Time T = dc.yearFraction(dates_[i], dates_[i] + ten_zinf_[j][k]);
490 scenarios[i]->add(zeroInflationKeys_[j * ten_zinf_[j].size() + k], ts->zeroRate(T));
491 }
492 }
493
494 // YoY inflation curves
495 for (Size j = 0; j < yoyInfCurves_.size(); ++j) {
496
497 auto tup = yoyInfCurves_[j];
498
499 // For YoY model implied term structure, JY and DK both need 3 state variables.
500 auto idx = std::get<0>(tup);
501 Array state(3);
502 state[0] = sample.value[model_->pIdx(CrossAssetModel::AssetType::INF, idx, 0)][i + 1];
503 state[1] = sample.value[model_->pIdx(CrossAssetModel::AssetType::INF, idx, 1)][i + 1];
504 state[2] = ir_state[std::get<1>(tup)][0];
505
506 // Update the term structure's date and state.
507 auto ts = std::get<3>(tup);
508 ts->move(dates_[i], state);
509
510 // Create the YoY pillar dates from the tenors.
511 vector<Date> pillarDates(ten_yinf_[j].size());
512 for (Size k = 0; k < pillarDates.size(); ++k)
513 pillarDates[k] = dates_[i] + ten_yinf_[j][k];
514
515 // Use the YoY term structure's YoY rates to populate the scenarios.
516 auto yoyRates = ts->yoyRates(pillarDates);
517 for (Size k = 0; k < pillarDates.size(); ++k) {
518 scenarios[i]->add(yoyInflationKeys_[j * ten_yinf_[j].size() + k], yoyRates.at(pillarDates[k]));
519 }
520 }
521
522 // Credit curves
523 for (Size j = 0; j < n_cr_; ++j) {
524 if (model_->modelType(CrossAssetModel::AssetType::CR, j) == CrossAssetModel::ModelType::LGM1F) {
525 Real z = sample.value[model_->pIdx(CrossAssetModel::AssetType::CR, j, 0)][i + 1];
526 Real y = sample.value[model_->pIdx(CrossAssetModel::AssetType::CR, j, 1)][i + 1];
527 lgmDefaultCurves_[j]->move(dates_[i], z, y);
528 for (Size k = 0; k < ten_dfc_[j].size(); k++) {
529 Date d = dates_[i] + ten_dfc_[j][k];
530 Time T = dc.yearFraction(dates_[i], d);
531 Real survProb = std::max(lgmDefaultCurves_[j]->survivalProbability(T), 0.00001);
532 scenarios[i]->add(defaultCurveKeys_[j * ten_dfc_[j].size() + k], survProb);
533 }
534 } else if (model_->modelType(CrossAssetModel::AssetType::CR, j) == CrossAssetModel::ModelType::CIRPP) {
535 Real y = sample.value[model_->pIdx(CrossAssetModel::AssetType::CR, j, 0)][i + 1];
536 cirppDefaultCurves_[j]->move(dates_[i], y);
537 for (Size k = 0; k < ten_dfc_[j].size(); k++) {
538 Date d = dates_[i] + ten_dfc_[j][k];
539 Time T = dc.yearFraction(dates_[i], d);
540 Real survProb = std::max(cirppDefaultCurves_[j]->survivalProbability(T), 0.00001);
541 scenarios[i]->add(defaultCurveKeys_[j * ten_dfc_[j].size() + k], survProb);
542 }
543 }
544 }
545
546 // Commodity curves
547 Array comState(1, 0.0); // FIXME: single-factor for now
548 for (Size j = 0; j < n_com_; j++) {
549 comState[0] = sample.value[model_->pIdx(CrossAssetModel::AssetType::COM, j)][i + 1];
550 comCurves_[j]->move(t, comState);
551 for (Size k = 0; k < ten_com_[j].size(); k++) {
552 Date d = dates_[i] + ten_com_[j][k];
553 Time T = dc.yearFraction(dates_[i], d);
554 Real price = std::max(comCurves_[j]->price(T), 0.00001);
555 scenarios[i]->add(commodityCurveKeys_[j * ten_com_[j].size() + k], price);
556 }
557 }
558
559 // Credit States
560 for (Size k = 0; k < n_crstates_; ++k) {
561 Real z = sample.value[model_->pIdx(CrossAssetModel::AssetType::CrState, k)][i + 1];
562 scenarios[i]->add(crStateKeys_[k], z);
563 }
564
565 // Survival Weights, stochastic cumulative survival probability, Recovery Rates
566 for (Size k = 0; k < n_survivalweights_; ++k) {
567 string name = simMarketConfig_->additionalScenarioDataSurvivalWeights()[k];
568 Real rr = survivalWeightsDefaultCurves_[k]->recovery().empty()
569 ? 0.0
570 : survivalWeightsDefaultCurves_[k]->recovery()->value();
571 scenarios[i]->add(survivalWeightKeys_[k],
572 survivalWeightsDefaultCurves_[k]->curve()->survivalProbability(dates_[i]));
573 scenarios[i]->add(recoveryRateKeys_[k], rr);
574 }
575 }
576 return scenarios;
577}
578} // namespace analytics
579} // namespace ore
QuantLib::ext::shared_ptr< QuantExt::CrossAssetModel > model_
std::vector< QuantLib::ext::shared_ptr< Scenario > > nextPath() override
QuantLib::ext::shared_ptr< ScenarioSimMarketParameters > simMarketConfig_
vector< QuantLib::ext::shared_ptr< QuantExt::ModelImpliedYieldTermStructure > > yieldCurves_
vector< QuantLib::ext::shared_ptr< QuantExt::ModelImpliedYieldTermStructure > > fwdCurves_
vector< tuple< Size, Size, CrossAssetModel::ModelType, QuantLib::ext::shared_ptr< ZeroInflationModelTermStructure > > > zeroInfCurves_
vector< QuantLib::ext::shared_ptr< QuantExt::ModelImpliedPriceTermStructure > > comCurves_
vector< QuantLib::ext::shared_ptr< QuantExt::LgmImpliedDefaultTermStructure > > lgmDefaultCurves_
CrossAssetModelScenarioGenerator(QuantLib::ext::shared_ptr< QuantExt::CrossAssetModel > model, QuantLib::ext::shared_ptr< QuantExt::MultiPathGeneratorBase > multiPathGenerator, QuantLib::ext::shared_ptr< ScenarioFactory > scenarioFactory, QuantLib::ext::shared_ptr< ScenarioSimMarketParameters > simMarketConfig, QuantLib::Date today, QuantLib::ext::shared_ptr< DateGrid > grid, QuantLib::ext::shared_ptr< ore::data::Market > initMarket, const std::string &configuration=Market::defaultConfiguration)
Constructor.
std::vector< QuantLib::ext::shared_ptr< QuantExt::CrossAssetModelImpliedEqVolTermStructure > > eqVols_
vector< tuple< Size, Size, CrossAssetModel::ModelType, QuantLib::ext::shared_ptr< YoYInflationModelTermStructure > > > yoyInfCurves_
QuantLib::ext::shared_ptr< QuantExt::MultiPathGeneratorBase > pathGenerator_
vector< QuantLib::ext::shared_ptr< QuantExt::ModelImpliedYieldTermStructure > > curves_
vector< QuantLib::ext::shared_ptr< QuantExt::CirppImpliedDefaultTermStructure > > cirppDefaultCurves_
vector< QuantLib::ext::shared_ptr< QuantExt::CreditCurve > > survivalWeightsDefaultCurves_
std::vector< QuantLib::ext::shared_ptr< QuantExt::CrossAssetModelImpliedFxVolTermStructure > > fxVols_
QuantLib::ext::shared_ptr< ore::data::Market > initMarket_
QuantLib::ext::shared_ptr< ScenarioFactory > scenarioFactory_
vector< QuantLib::ext::shared_ptr< IborIndex > > indices_
Data types stored in the scenario class.
Definition: scenario.hpp:48
Scenario generator that generates an entire path.
const QuantLib::ext::shared_ptr< ModelCG > model_
Scenario generation using cross asset model paths.
Currency parseCurrency(const string &s)
#define LOG(text)
#define DLOG(text)
Size size(const ValueType &v)
string name