Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
crossassetmodelbuilder.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
36
54
55#include <ql/math/optimization/levenbergmarquardt.hpp>
56#include <ql/models/shortrate/calibrationhelpers/swaptionhelper.hpp>
57#include <ql/pricingengines/swap/discountingswapengine.hpp>
58#include <ql/quotes/simplequote.hpp>
59#include <ql/utilities/dataformatters.hpp>
60
61#include <boost/algorithm/string/case_conv.hpp>
62#include <boost/lexical_cast.hpp>
63
72using QuantLib::DiscountingSwapEngine;
73using std::vector;
74
75namespace ore {
76namespace data {
77
79 const QuantLib::ext::shared_ptr<ore::data::Market>& market, const QuantLib::ext::shared_ptr<CrossAssetModelData>& config,
80 const std::string& configurationLgmCalibration, const std::string& configurationFxCalibration,
81 const std::string& configurationEqCalibration, const std::string& configurationInfCalibration,
82 const std::string& configurationCrCalibration, const std::string& configurationFinalModel, const bool dontCalibrate,
83 const bool continueOnError, const std::string& referenceCalibrationGrid, const SalvagingAlgorithm::Type salvaging,
84 const std::string& id)
85 : market_(market), config_(config), configurationLgmCalibration_(configurationLgmCalibration),
86 configurationFxCalibration_(configurationFxCalibration), configurationEqCalibration_(configurationEqCalibration),
87 configurationInfCalibration_(configurationInfCalibration),
88 configurationCrCalibration_(configurationCrCalibration),
89 configurationComCalibration_(Market::defaultConfiguration), configurationFinalModel_(configurationFinalModel),
90 dontCalibrate_(dontCalibrate), continueOnError_(continueOnError),
91 referenceCalibrationGrid_(referenceCalibrationGrid), salvaging_(salvaging), id_(id),
92 optimizationMethod_(QuantLib::ext::shared_ptr<OptimizationMethod>(new LevenbergMarquardt(1E-8, 1E-8, 1E-8))),
93 endCriteria_(EndCriteria(1000, 500, 1E-8, 1E-8, 1E-8)) {
94 buildModel();
95 // register with sub builders
96 for (auto okv : subBuilders_)
97 for (auto ikv : okv.second) {
98 registerWith(ikv.second);
99 }
100 // register market observer with correlations
101 marketObserver_ = QuantLib::ext::make_shared<MarketObserver>();
102 for (auto const& c : config->correlations())
103 marketObserver_->addObservable(c.second);
104 // reset market observer's updated flag
105 marketObserver_->hasUpdated(true);
106 // register with market observer
107 registerWith(marketObserver_);
108}
109
110Handle<QuantExt::CrossAssetModel> CrossAssetModelBuilder::model() const {
111 calculate();
112 return model_;
113}
114
116 calculate();
118}
120 calculate();
122}
124 calculate();
126}
128 calculate();
130}
132 calculate();
134}
135
137 for (auto okv : subBuilders_)
138 for (auto ikv : okv.second)
139 if (ikv.second->requiresRecalibration())
140 return true;
141
142 return marketObserver_->hasUpdated(false);
143}
144
146 // if any of the sub models requires a recalibration, we rebuilt the model
147 // TODO we could do this more selectively
149 // reset market observer update flag
150 marketObserver_->hasUpdated(true);
151 buildModel();
152 }
153}
154
155void CrossAssetModelBuilder::resetModelParams(const CrossAssetModel::AssetType t, const Size param, const Size index,
156 const Size i) const {
157 auto mp = model_->MoveParameter(t, param, index, i);
158 for (Size idx = 0; idx < mp.size(); ++idx) {
159 if (!mp[idx]) {
160 model_->setParam(idx, params_[idx]);
161 }
162 }
163}
164
165void CrossAssetModelBuilder::copyModelParams(const CrossAssetModel::AssetType t0, const Size param0, const Size index0,
166 const Size i0, const CrossAssetModel::AssetType t1, const Size param1,
167 const Size index1, const Size i1, const Real mult) const {
168 auto mp0 = model_->MoveParameter(t0, param0, index0, i0);
169 auto mp1 = model_->MoveParameter(t1, param1, index1, i1);
170 auto s0 = 0, s1 = 0;
171 for (Size i = 0; i < mp0.size(); ++i)
172 if (!mp0[i])
173 s0++;
174 for (Size i = 0; i < mp1.size(); ++i)
175 if (!mp1[i])
176 s1++;
177 QL_REQUIRE(s0 == s1, "CrossAssetModelBuilder::copyModelParams(): source range size ("
178 << s0 << ") does not match target range size (" << s1 << ") when copying (" << t0 << ","
179 << param0 << "," << index0 << "," << i0 << ") -> (" << t1 << "," << param1 << "," << index1
180 << "," << i1 << ")");
181 std::vector<Real> sourceValues(s0);
182 for (Size idx0 = 0, count = 0; idx0 < mp0.size(); ++idx0) {
183 if (!mp0[idx0]) {
184 sourceValues[count++] = params_[idx0];
185 }
186 }
187 for (Size idx1 = 0, count = 0; idx1 < mp1.size(); ++idx1) {
188 if (!mp1[idx1]) {
189 model_->setParam(idx1, sourceValues[count++] * mult);
190 }
191 }
192}
193
195
196 LOG("Start building CrossAssetModel");
197
198 QL_REQUIRE(market_ != NULL, "CrossAssetModelBuilder: no market given");
199
200 DLOG("configurations: LgmCalibration "
201 << configurationLgmCalibration_ << ", FxCalibration " << configurationFxCalibration_ << ", EqCalibration "
202 << configurationEqCalibration_ << ", InfCalibration " << configurationInfCalibration_ << ", CrCalibration"
203 << configurationCrCalibration_ << ", ComCalibration" << configurationComCalibration_ << ", FinalModel "
205
206 if (dontCalibrate_) {
207 DLOG("Calibration of the model is disabled.");
208 }
209
210 bool buildersAreInitialized = !subBuilders_.empty();
211
212 if (!buildersAreInitialized) {
213 QL_REQUIRE(config_->irConfigs().size() > 0, "missing IR configurations");
214 QL_REQUIRE(config_->irConfigs().size() == config_->fxConfigs().size() + 1,
215 "FX configuration size " << config_->fxConfigs().size()
216 << " inconsistent with IR configuration size "
217 << config_->irConfigs().size());
218
219 swaptionBaskets_.resize(config_->irConfigs().size());
220 optionExpiries_.resize(config_->irConfigs().size());
221 swaptionMaturities_.resize(config_->irConfigs().size());
222 swaptionCalibrationErrors_.resize(config_->irConfigs().size());
223 fxOptionBaskets_.resize(config_->fxConfigs().size());
224 fxOptionExpiries_.resize(config_->fxConfigs().size());
225 fxOptionCalibrationErrors_.resize(config_->fxConfigs().size());
226 eqOptionBaskets_.resize(config_->eqConfigs().size());
227 eqOptionExpiries_.resize(config_->eqConfigs().size());
228 eqOptionCalibrationErrors_.resize(config_->eqConfigs().size());
229 inflationCalibrationErrors_.resize(config_->infConfigs().size());
230 comOptionBaskets_.resize(config_->comConfigs().size());
231 comOptionExpiries_.resize(config_->comConfigs().size());
232 comOptionCalibrationErrors_.resize(config_->comConfigs().size());
233 }
234
235 // Store information on the number of factors for each process. This is used when requesting a correlation matrix
236 // from the CorrelationMatrixBuilder below.
238
239 // Set the measure
240 IrModel::Measure measure = IrModel::Measure::LGM;
241 if (config_->measure() == "BA") {
242 measure = IrModel::Measure::BA;
243 DLOG("Setting measure to BA");
244 } else if (config_->measure() == "LGM") {
245 measure = IrModel::Measure::LGM;
246 DLOG("Setting measure to LGM");
247 } else if (config_->measure() == "") {
248 DLOG("Defaulting to LGM measure");
249 } else {
250 QL_FAIL("Measure " << config_->measure() << " not recognized");
251 }
252
253 /*******************************************************
254 * Build the IR parametrizations and calibration baskets
255 */
256 std::vector<QuantLib::ext::shared_ptr<QuantExt::Parametrization>> irParametrizations;
257 std::vector<RelinkableHandle<YieldTermStructure>> irDiscountCurves;
258 std::vector<std::string> currencies, regions, crNames, eqNames, infIndices, comNames;
259 std::vector<QuantLib::ext::shared_ptr<LgmBuilder>> lgmBuilder;
260 std::vector<QuantLib::ext::shared_ptr<HwBuilder>> hwBuilder;
261 std::vector<QuantLib::ext::shared_ptr<FxBsBuilder>> fxBuilder;
262 std::vector<QuantLib::ext::shared_ptr<EqBsBuilder>> eqBuilder;
263 std::vector<QuantLib::ext::shared_ptr<CommoditySchwartzModelBuilder>> csBuilder;
264
265 std::set<std::string> recalibratedCurrencies;
266 for (Size i = 0; i < config_->irConfigs().size(); i++) {
267 auto irConfig = config_->irConfigs()[i];
268 DLOG("IR Parametrization " << i << " qualifier " << irConfig->qualifier());
269
270 if (auto ir = QuantLib::ext::dynamic_pointer_cast<IrLgmData>(irConfig)) {
271 if (!buildersAreInitialized) {
272 subBuilders_[CrossAssetModel::AssetType::IR][i] = QuantLib::ext::make_shared<LgmBuilder>(
275 }
276 auto builder = QuantLib::ext::dynamic_pointer_cast<LgmBuilder>(subBuilders_[CrossAssetModel::AssetType::IR][i]);
277 lgmBuilder.push_back(builder);
278 if (dontCalibrate_) {
279 builder->freeze();
280 }
281 if (builder->requiresRecalibration())
282 recalibratedCurrencies.insert(builder->parametrization()->currency().code());
283 auto parametrization = builder->parametrization();
284 swaptionBaskets_[i] = builder->swaptionBasket();
285 QL_REQUIRE(std::find(currencies.begin(), currencies.end(), parametrization->currency().code()) ==
286 currencies.end(),
287 "Duplicate IR parameterization for currency "
288 << parametrization->currency().code()
289 << " - are there maybe two indices with the same currency in CrossAssetModelData?");
290 currencies.push_back(parametrization->currency().code());
291 irParametrizations.push_back(parametrization);
292 irDiscountCurves.push_back(builder->discountCurve());
293 processInfo[CrossAssetModel::AssetType::IR].emplace_back(ir->ccy(), 1);
294 } else if (auto ir = QuantLib::ext::dynamic_pointer_cast<HwModelData>(irConfig)) {
295 bool evaluateBankAccount = true; // updated in cross asset model for non-base ccys
296 bool setCalibrationInfo = false;
297 HwModel::Discretization discr = HwModel::Discretization::Euler;
298 if (!buildersAreInitialized) {
299 subBuilders_[CrossAssetModel::AssetType::IR][i] = QuantLib::ext::make_shared<HwBuilder>(
300 market_, ir, measure, discr, evaluateBankAccount, configurationLgmCalibration_,
301 config_->bootstrapTolerance(), continueOnError_, referenceCalibrationGrid_, setCalibrationInfo);
302 }
303 auto builder = QuantLib::ext::dynamic_pointer_cast<HwBuilder>(subBuilders_[CrossAssetModel::AssetType::IR][i]);
304 hwBuilder.push_back(builder);
305 if (builder->requiresRecalibration())
306 recalibratedCurrencies.insert(builder->parametrization()->currency().code());
307 auto parametrization = builder->parametrization();
308 if (dontCalibrate_)
309 builder->freeze();
310 swaptionBaskets_[i] = builder->swaptionBasket();
311 QL_REQUIRE(std::find(currencies.begin(), currencies.end(), parametrization->currency().code()) ==
312 currencies.end(),
313 "Duplicate IR parameterization for currency "
314 << parametrization->currency().code()
315 << " - are there maybe two indices with the same currency in CrossAssetModelData?");
316 currencies.push_back(parametrization->currency().code());
317 irParametrizations.push_back(parametrization);
318 irDiscountCurves.push_back(builder->discountCurve());
319 processInfo[CrossAssetModel::AssetType::IR].emplace_back(ir->ccy(), parametrization->m());
320 }
321 }
322
323 QL_REQUIRE(irParametrizations.size() > 0, "missing IR parametrizations");
324
325 QuantLib::Currency domesticCcy = irParametrizations[0]->currency();
326
327 /*******************************************************
328 * Build the FX parametrizations and calibration baskets
329 */
330 std::vector<QuantLib::ext::shared_ptr<QuantExt::FxBsParametrization>> fxParametrizations;
331 for (Size i = 0; i < config_->fxConfigs().size(); i++) {
332 DLOG("FX Parametrization " << i);
333 QuantLib::ext::shared_ptr<FxBsData> fx = config_->fxConfigs()[i];
334 QuantLib::Currency ccy = ore::data::parseCurrency(fx->foreignCcy());
335 QuantLib::Currency domCcy = ore::data::parseCurrency(fx->domesticCcy());
336
337 QL_REQUIRE(ccy.code() == irParametrizations[i + 1]->currency().code(),
338 "FX parametrization currency[" << i << "]=" << ccy << " does not match IR currency[" << i + 1
339 << "]=" << irParametrizations[i + 1]->currency().code());
340
341 QL_REQUIRE(domCcy == domesticCcy, "FX parametrization [" << i << "]=" << ccy << "/" << domCcy
342 << " does not match domestic ccy " << domesticCcy);
343
344 if (!buildersAreInitialized) {
345 subBuilders_[CrossAssetModel::AssetType::FX][i] =
346 QuantLib::ext::make_shared<FxBsBuilder>(market_, fx, configurationFxCalibration_, referenceCalibrationGrid_);
347 }
348 auto builder = QuantLib::ext::dynamic_pointer_cast<FxBsBuilder>(subBuilders_[CrossAssetModel::AssetType::FX][i]);
349 fxBuilder.push_back(builder);
350
351 QuantLib::ext::shared_ptr<QuantExt::FxBsParametrization> parametrization = builder->parametrization();
352
353 fxOptionBaskets_[i] = builder->optionBasket();
354 fxParametrizations.push_back(parametrization);
355 processInfo[CrossAssetModel::AssetType::FX].emplace_back(ccy.code() + domCcy.code(), 1);
356 }
357
358 /*******************************************************
359 * Build the EQ parametrizations and calibration baskets
360 */
361 std::vector<QuantLib::ext::shared_ptr<QuantExt::EqBsParametrization>> eqParametrizations;
362 for (Size i = 0; i < config_->eqConfigs().size(); i++) {
363 DLOG("EQ Parametrization " << i);
364 QuantLib::ext::shared_ptr<EqBsData> eq = config_->eqConfigs()[i];
365 string eqName = eq->eqName();
366 QuantLib::Currency eqCcy = ore::data::parseCurrency(eq->currency());
367 QL_REQUIRE(std::find(currencies.begin(), currencies.end(), eqCcy.code()) != currencies.end(),
368 "Currency (" << eqCcy << ") for equity " << eqName << " not covered by CrossAssetModelData");
369 if (!buildersAreInitialized) {
370 subBuilders_[CrossAssetModel::AssetType::EQ][i] = QuantLib::ext::make_shared<EqBsBuilder>(
372 }
373 QuantLib::ext::shared_ptr<EqBsBuilder> builder =
374 QuantLib::ext::dynamic_pointer_cast<EqBsBuilder>(subBuilders_[CrossAssetModel::AssetType::EQ][i]);
375 eqBuilder.push_back(builder);
376 QuantLib::ext::shared_ptr<QuantExt::EqBsParametrization> parametrization = builder->parametrization();
377 eqOptionBaskets_[i] = builder->optionBasket();
378 eqParametrizations.push_back(parametrization);
379 eqNames.push_back(eqName);
380 processInfo[CrossAssetModel::AssetType::EQ].emplace_back(eqName, 1);
381 }
382
383 /*******************************************************
384 * Build the INF parametrizations and calibration baskets
385 */
386 vector<QuantLib::ext::shared_ptr<Parametrization>> infParameterizations;
387 for (Size i = 0; i < config_->infConfigs().size(); i++) {
388 QuantLib::ext::shared_ptr<InflationModelData> imData = config_->infConfigs()[i];
389 DLOG("Inflation parameterisation (" << i << ") for index " << imData->index());
390 if (auto dkData = QuantLib::ext::dynamic_pointer_cast<InfDkData>(imData)) {
391 if (!buildersAreInitialized) {
392 subBuilders_[CrossAssetModel::AssetType::INF][i] = QuantLib::ext::make_shared<InfDkBuilder>(
394 }
395 QuantLib::ext::shared_ptr<InfDkBuilder> builder =
396 QuantLib::ext::dynamic_pointer_cast<InfDkBuilder>(subBuilders_[CrossAssetModel::AssetType::INF][i]);
397 infParameterizations.push_back(builder->parametrization());
398 processInfo[CrossAssetModel::AssetType::INF].emplace_back(dkData->index(), 1);
399 } else if (auto jyData = QuantLib::ext::dynamic_pointer_cast<InfJyData>(imData)) {
400 if (!buildersAreInitialized) {
401 // for linked real rate params we have to resize the real rate params here again, because their time grid
402 // might have been overwritten in the ir calibration step
403 if (jyData->linkRealRateParamsToNominalRateParams()) {
404 Size ccyIndex = std::distance(currencies.begin(),
405 std::find(currencies.begin(), currencies.end(), jyData->currency()));
406 VolatilityParameter rrVol = jyData->realRateVolatility();
407 ReversionParameter rrRev = jyData->realRateReversion();
408 rrVol.setCalibrate(false);
409 rrRev.setCalibrate(false);
410 auto volTimes = irParametrizations[ccyIndex]->parameterTimes(0);
411 auto volValues = irParametrizations[ccyIndex]->parameterValues(0);
412 auto revTimes = irParametrizations[ccyIndex]->parameterTimes(1);
413 auto revValues = irParametrizations[ccyIndex]->parameterValues(1);
414 rrVol.setTimes(std::vector<Real>(volTimes.begin(), volTimes.end()));
415 rrRev.setTimes(std::vector<Real>(revTimes.begin(), revTimes.end()));
416 rrVol.setValues(std::vector<Real>(volValues.begin(), volValues.end()));
417 rrRev.setValues(std::vector<Real>(revValues.begin(), revValues.end()));
418 rrVol. mult(jyData->linkedRealRateVolatilityScaling());
419 jyData->setRealRateReversion(rrRev);
420 jyData->setRealRateVolatility(rrVol);
421 }
422 subBuilders_[CrossAssetModel::AssetType::INF][i] = QuantLib::ext::make_shared<InfJyBuilder>(
424 }
425 auto builder = QuantLib::ext::dynamic_pointer_cast<InfJyBuilder>(subBuilders_[CrossAssetModel::AssetType::INF][i]);
426 infParameterizations.push_back(builder->parameterization());
427 processInfo[CrossAssetModel::AssetType::INF].emplace_back(jyData->index(), 2);
428 } else {
429 QL_FAIL("CrossAssetModelBuilder expects either DK or JY inflation model data.");
430 }
431 infIndices.push_back(imData->index());
432 }
433
434 /*******************************************************
435 * Build the CR parametrizations and calibration baskets
436 */
437 // LGM (if any)
438 std::vector<QuantLib::ext::shared_ptr<QuantExt::CrLgm1fParametrization>> crLgmParametrizations;
439 for (Size i = 0; i < config_->crLgmConfigs().size(); ++i) {
440 LOG("CR LGM Parametrization " << i);
441 QuantLib::ext::shared_ptr<CrLgmData> cr = config_->crLgmConfigs()[i];
442 string crName = cr->name();
443 if (!buildersAreInitialized) {
444 subBuilders_[CrossAssetModel::AssetType::CR][i] =
445 QuantLib::ext::make_shared<CrLgmBuilder>(market_, cr, configurationCrCalibration_);
446 }
447 auto builder = QuantLib::ext::dynamic_pointer_cast<CrLgmBuilder>(subBuilders_[CrossAssetModel::AssetType::CR][i]);
448 QuantLib::ext::shared_ptr<QuantExt::CrLgm1fParametrization> parametrization = builder->parametrization();
449 crLgmParametrizations.push_back(parametrization);
450 crNames.push_back(crName);
451 processInfo[CrossAssetModel::AssetType::CR].emplace_back(crName, 1);
452 }
453
454 // CIR (if any)
455 std::vector<QuantLib::ext::shared_ptr<QuantExt::CrCirppParametrization>> crCirParametrizations;
456 for (Size i = 0; i < config_->crCirConfigs().size(); ++i) {
457 LOG("CR CIR Parametrization " << i);
458 QuantLib::ext::shared_ptr<CrCirData> cr = config_->crCirConfigs()[i];
459 string crName = cr->name();
460 if (!buildersAreInitialized) {
461 subBuilders_[CrossAssetModel::AssetType::CR][i] =
462 QuantLib::ext::make_shared<CrCirBuilder>(market_, cr, configurationCrCalibration_);
463 }
464 auto builder = QuantLib::ext::dynamic_pointer_cast<CrCirBuilder>(subBuilders_[CrossAssetModel::AssetType::CR][i]);
465 QuantLib::ext::shared_ptr<QuantExt::CrCirppParametrization> parametrization = builder->parametrization();
466 crCirParametrizations.push_back(parametrization);
467 crNames.push_back(crName);
468 processInfo[CrossAssetModel::AssetType::CR].emplace_back(crName, 1);
469 }
470
471 /*******************************************************
472 * Build the COM parametrizations and calibration baskets
473 */
474 std::vector<QuantLib::ext::shared_ptr<QuantExt::CommoditySchwartzParametrization>> comParametrizations;
475 for (Size i = 0; i < config_->comConfigs().size(); i++) {
476 DLOG("COM Parametrization " << i);
477 QuantLib::ext::shared_ptr<CommoditySchwartzData> com = config_->comConfigs()[i];
478 string comName = com->name();
479 QuantLib::Currency comCcy = ore::data::parseCurrency(com->currency());
480 QL_REQUIRE(std::find(currencies.begin(), currencies.end(), comCcy.code()) != currencies.end(),
481 "Currency (" << comCcy << ") for commodity " << comName << " not covered by CrossAssetModelData");
482 if (!buildersAreInitialized) {
483 subBuilders_[CrossAssetModel::AssetType::COM][i] = QuantLib::ext::make_shared<CommoditySchwartzModelBuilder>(
485 }
486 auto builder = QuantLib::ext::dynamic_pointer_cast<CommoditySchwartzModelBuilder>(
487 subBuilders_[CrossAssetModel::AssetType::COM][i]);
488 if (dontCalibrate_)
489 builder->freeze();
490 csBuilder.push_back(builder);
491 QuantLib::ext::shared_ptr<QuantExt::CommoditySchwartzParametrization> parametrization = builder->parametrization();
492 comOptionBaskets_[i] = builder->optionBasket();
493 comParametrizations.push_back(parametrization);
494 comNames.push_back(comName);
495 processInfo[CrossAssetModel::AssetType::COM].emplace_back(comName, 1);
496 }
497
498 /*******************************************************
499 * Build the CrState parametrizations
500 */
501 std::vector<QuantLib::ext::shared_ptr<QuantExt::CrStateParametrization>> crStateParametrizations;
502 for (Size i = 0; i < config_->numberOfCreditStates(); i++) {
503 DLOG("CrState Parametrization " << i);
504 crStateParametrizations.push_back(QuantLib::ext::make_shared<QuantExt::CrStateParametrization>(i));
505 processInfo[CrossAssetModel::AssetType::CrState].emplace_back(std::to_string(i), 1);
506 }
507
508 std::vector<QuantLib::ext::shared_ptr<QuantExt::Parametrization>> parametrizations;
509 for (Size i = 0; i < irParametrizations.size(); i++)
510 parametrizations.push_back(irParametrizations[i]);
511 for (Size i = 0; i < fxParametrizations.size(); i++)
512 parametrizations.push_back(fxParametrizations[i]);
513 for (Size i = 0; i < eqParametrizations.size(); i++)
514 parametrizations.push_back(eqParametrizations[i]);
515 parametrizations.insert(parametrizations.end(), infParameterizations.begin(), infParameterizations.end());
516 for (Size i = 0; i < crLgmParametrizations.size(); i++)
517 parametrizations.push_back(crLgmParametrizations[i]);
518 for (Size i = 0; i < crCirParametrizations.size(); i++)
519 parametrizations.push_back(crCirParametrizations[i]);
520 for (Size i = 0; i < comParametrizations.size(); i++)
521 parametrizations.push_back(comParametrizations[i]);
522 for (Size i = 0; i < crStateParametrizations.size(); i++)
523 parametrizations.push_back(crStateParametrizations[i]);
524
525 QL_REQUIRE(fxParametrizations.size() == irParametrizations.size() - 1, "mismatch in IR/FX parametrization sizes");
526
527 /******************************
528 * Build the correlation matrix
529 */
530 DLOG("CrossAssetModelBuilder: adding correlations.");
532
533 for (auto it = config_->correlations().begin(); it != config_->correlations().end(); it++) {
534 cmb.addCorrelation(it->first.first, it->first.second, it->second);
535 }
536
537 Matrix corrMatrix = cmb.correlationMatrix(processInfo);
538
539 TLOG("CAM correlation matrix:");
540 TLOGGERSTREAM(corrMatrix);
541
542 /*****************************
543 * Build the cross asset model
544 */
545
546 model_.linkTo(QuantLib::ext::make_shared<QuantExt::CrossAssetModel>(parametrizations, corrMatrix, salvaging_, measure,
547 config_->discretization()));
548
549 /* Store initial params to ensure identical start values when recalibrating a component.
550 This is only used for fx, eq, inf, cr, com, for ir this is handled in LgmBuilder directly.
551 Therefore it does not matter that the IR parameters are calibrated at this point already. */
552
553 if (!buildersAreInitialized) {
554 params_ = model_->params();
555 }
556
557 /*************************
558 * Calibrate IR components
559 */
560
561 for (Size i = 0; i < lgmBuilder.size(); i++) {
562 DLOG("IR Calibration " << i);
563 swaptionCalibrationErrors_[i] = lgmBuilder[i]->error();
564 }
565
566 for (Size i = 0; i < hwBuilder.size(); i++) {
567 DLOG("IR Calibration " << i);
568 swaptionCalibrationErrors_[i] = hwBuilder[i]->error();
569 }
570
571 /*************************
572 * Relink LGM discount curves to curves used for FX calibration
573 */
574
575 for (Size i = 0; i < irParametrizations.size(); i++) {
576 auto p = irParametrizations[i];
577 irDiscountCurves[i].linkTo(*market_->discountCurve(p->currency().code(), configurationFxCalibration_));
578 DLOG("Relinked discounting curve for " << p->currency().code() << " for FX calibration");
579 }
580
581 /*************************
582 * Calibrate FX components
583 */
584
585 for (Size i = 0; i < fxParametrizations.size(); i++) {
586 QuantLib::ext::shared_ptr<FxBsData> fx = config_->fxConfigs()[i];
587
588 if (fx->calibrationType() == CalibrationType::None || !fx->calibrateSigma()) {
589 DLOG("FX Calibration " << i << " skipped");
590 continue;
591 }
592
593 if (!fxBuilder[i]->requiresRecalibration() &&
594 recalibratedCurrencies.find(fx->foreignCcy()) == recalibratedCurrencies.end() &&
595 recalibratedCurrencies.find(fx->domesticCcy()) == recalibratedCurrencies.end()) {
596 DLOG("FX Calibration "
597 << i << " skipped, since neither fx builder nor ir models in dom / for ccy were recalibrated.");
598 continue;
599 }
600
601 DLOG("FX Calibration " << i);
602
603 // attach pricing engines to helpers
604 QuantLib::ext::shared_ptr<QuantExt::AnalyticCcLgmFxOptionEngine> engine =
605 QuantLib::ext::make_shared<QuantExt::AnalyticCcLgmFxOptionEngine>(*model_, i);
606 // enable caching for calibration
607 // TODO: review this
608 engine->cache(true);
609 for (Size j = 0; j < fxOptionBaskets_[i].size(); j++)
610 fxOptionBaskets_[i][j]->setPricingEngine(engine);
611
612 if (!dontCalibrate_) {
613
614 // reset to initial params to ensure identical calibration outcomes for identical baskets
615 resetModelParams(CrossAssetModel::AssetType::FX, 0, i, Null<Size>());
616
617 if (fx->calibrationType() == CalibrationType::Bootstrap && fx->sigmaParamType() == ParamType::Piecewise)
618 model_->calibrateBsVolatilitiesIterative(CrossAssetModel::AssetType::FX, i, fxOptionBaskets_[i],
620 else
621 model_->calibrateBsVolatilitiesGlobal(CrossAssetModel::AssetType::FX, i, fxOptionBaskets_[i],
623
624 DLOG("FX " << fx->foreignCcy() << " calibration errors:");
626 if (fx->calibrationType() == CalibrationType::Bootstrap) {
627 if (fabs(fxOptionCalibrationErrors_[i]) < config_->bootstrapTolerance()) {
628 TLOGGERSTREAM("Calibration details:");
630 getCalibrationDetails(fxOptionBaskets_[i], fxParametrizations[i], irParametrizations[0]));
632 } else {
633 std::string exceptionMessage = "FX BS " + std::to_string(i) + " calibration error " +
634 std::to_string(fxOptionCalibrationErrors_[i]) +
635 " exceeds tolerance " +
636 std::to_string(config_->bootstrapTolerance());
637 StructuredModelWarningMessage("Failed to calibrate FX BS Model", exceptionMessage, id_).log();
638 WLOGGERSTREAM("Calibration details:");
640 getCalibrationDetails(fxOptionBaskets_[i], fxParametrizations[i], irParametrizations[0]));
642 if (!continueOnError_)
643 QL_FAIL(exceptionMessage);
644 }
645 }
646 }
647 fxBuilder[i]->setCalibrationDone();
648 }
649
650 /*************************
651 * Relink LGM discount curves to curves used for EQ calibration
652 */
653
654 for (Size i = 0; i < irParametrizations.size(); i++) {
655 auto p = irParametrizations[i];
656 irDiscountCurves[i].linkTo(*market_->discountCurve(p->currency().code(), configurationEqCalibration_));
657 DLOG("Relinked discounting curve for " << p->currency().code() << " for EQ calibration");
658 }
659
660 /*************************
661 * Calibrate EQ components
662 */
663
664 for (Size i = 0; i < eqParametrizations.size(); i++) {
665 QuantLib::ext::shared_ptr<EqBsData> eq = config_->eqConfigs()[i];
666 if (!eq->calibrateSigma()) {
667 DLOG("EQ Calibration " << i << " skipped");
668 continue;
669 }
670
671 if (!eqBuilder[i]->requiresRecalibration() &&
672 recalibratedCurrencies.find(eq->currency()) == recalibratedCurrencies.end()) {
673 DLOG("EQ Calibration " << i
674 << " skipped, since neither eq builder nor ir model in eq ccy were recalibrated.");
675 continue;
676 }
677
678 DLOG("EQ Calibration " << i);
679 // attach pricing engines to helpers
680 Currency eqCcy = eqParametrizations[i]->currency();
681 Size eqCcyIdx = model_->ccyIndex(eqCcy);
682 QuantLib::ext::shared_ptr<QuantExt::AnalyticXAssetLgmEquityOptionEngine> engine =
683 QuantLib::ext::make_shared<QuantExt::AnalyticXAssetLgmEquityOptionEngine>(*model_, i, eqCcyIdx);
684 for (Size j = 0; j < eqOptionBaskets_[i].size(); j++)
685 eqOptionBaskets_[i][j]->setPricingEngine(engine);
686
687 if (!dontCalibrate_) {
688
689 // reset to initial params to ensure identical calibration outcomes for identical baskets
690 resetModelParams(CrossAssetModel::AssetType::EQ, 0, i, Null<Size>());
691
692 if (eq->calibrationType() == CalibrationType::Bootstrap && eq->sigmaParamType() == ParamType::Piecewise)
693 model_->calibrateBsVolatilitiesIterative(CrossAssetModel::AssetType::EQ, i, eqOptionBaskets_[i],
695 else
696 model_->calibrateBsVolatilitiesGlobal(CrossAssetModel::AssetType::EQ, i, eqOptionBaskets_[i],
698 DLOG("EQ " << eq->eqName() << " calibration errors:");
700 if (eq->calibrationType() == CalibrationType::Bootstrap) {
701 if (fabs(eqOptionCalibrationErrors_[i]) < config_->bootstrapTolerance()) {
702 TLOGGERSTREAM("Calibration details:");
704 getCalibrationDetails(eqOptionBaskets_[i], eqParametrizations[i], irParametrizations[0]));
706 } else {
707 std::string exceptionMessage = "EQ BS " + std::to_string(i) + " calibration error " +
708 std::to_string(eqOptionCalibrationErrors_[i]) +
709 " exceeds tolerance " +
710 std::to_string(config_->bootstrapTolerance());
711 StructuredModelWarningMessage("Failed to calibrate EQ BS Model", exceptionMessage, id_).log();
712 WLOGGERSTREAM("Calibration details:");
714 getCalibrationDetails(eqOptionBaskets_[i], eqParametrizations[i], irParametrizations[0]));
716 if (!continueOnError_)
717 QL_FAIL(exceptionMessage);
718 }
719 }
720 }
721 eqBuilder[i]->setCalibrationDone();
722 }
723
724 /*************************
725 * Calibrate COM components
726 */
727
728 for (Size i = 0; i < csBuilder.size(); i++) {
729 DLOG("COM Calibration " << i);
730 comOptionCalibrationErrors_[i] = csBuilder[i]->error();
731 }
732
733 /*************************
734 * Relink LGM discount curves to curves used for INF calibration
735 */
736
737 for (Size i = 0; i < irParametrizations.size(); i++) {
738 auto p = irParametrizations[i];
739 irDiscountCurves[i].linkTo(*market_->discountCurve(p->currency().code(), configurationInfCalibration_));
740 DLOG("Relinked discounting curve for " << p->currency().code() << " for INF calibration");
741 }
742
743 // Calibrate INF components
744 for (Size i = 0; i < infParameterizations.size(); i++) {
745 QuantLib::ext::shared_ptr<InflationModelData> imData = config_->infConfigs()[i];
746 if (auto dkData = QuantLib::ext::dynamic_pointer_cast<InfDkData>(imData)) {
747 auto dkParam = QuantLib::ext::dynamic_pointer_cast<InfDkParametrization>(infParameterizations[i]);
748 QL_REQUIRE(dkParam, "Expected DK model data to have given a DK parameterisation.");
749 const auto& builder = subBuilders_.at(CrossAssetModel::AssetType::INF).at(i);
750 const auto& dkBuilder = QuantLib::ext::dynamic_pointer_cast<InfDkBuilder>(builder);
751 if (!dkBuilder->requiresRecalibration() &&
752 recalibratedCurrencies.find(infParameterizations[i]->currency().code()) ==
753 recalibratedCurrencies.end()) {
754 DLOG("Skipping inf dk calibration "
755 << i << " since neither inf builder nor ir model in inf ccy were recalibrated.");
756 continue;
757 }
758 calibrateInflation(*dkData, i, dkBuilder->optionBasket(), dkParam);
759 dkBuilder->setCalibrationDone();
760 } else if (auto jyData = QuantLib::ext::dynamic_pointer_cast<InfJyData>(imData)) {
761 auto jyParam = QuantLib::ext::dynamic_pointer_cast<InfJyParameterization>(infParameterizations[i]);
762 QL_REQUIRE(jyParam, "Expected JY model data to have given a JY parameterisation.");
763 const auto& builder = subBuilders_.at(CrossAssetModel::AssetType::INF).at(i);
764 const auto& jyBuilder = QuantLib::ext::dynamic_pointer_cast<InfJyBuilder>(builder);
765 if (!jyBuilder->requiresRecalibration() &&
766 recalibratedCurrencies.find(infParameterizations[i]->currency().code()) ==
767 recalibratedCurrencies.end()) {
768 DLOG("Skipping inf jy calibration "
769 << i << " since neither inf builder nor ir model in inf ccy were recalibrated.");
770 continue;
771 }
772 calibrateInflation(*jyData, i, jyBuilder, jyParam);
773 jyBuilder->setCalibrationDone();
774 } else {
775 QL_FAIL("CrossAssetModelBuilder expects either DK or JY inflation model data.");
776 }
777 }
778
779 /*************************
780 * Relink LGM discount curves to final model curves
781 */
782
783 for (Size i = 0; i < irParametrizations.size(); i++) {
784 auto p = irParametrizations[i];
785 irDiscountCurves[i].linkTo(*market_->discountCurve(p->currency().code(), configurationFinalModel_));
786 DLOG("Relinked discounting curve for " << p->currency().code() << " as final model curves");
787 }
788
789 DLOG("Building CrossAssetModel done");
790}
791
793 forceCalibration_ = true;
795 forceCalibration_ = false;
796}
797
798void CrossAssetModelBuilder::calibrateInflation(const InfDkData& data, Size modelIdx,
799 const vector<QuantLib::ext::shared_ptr<BlackCalibrationHelper>>& cb,
800 const QuantLib::ext::shared_ptr<InfDkParametrization>& inflationParam) const {
801
802 LOG("Calibrate DK inflation model for inflation index " << data.index());
803
804 if ((!data.volatility().calibrate() && !data.reversion().calibrate()) ||
805 (data.calibrationType() == CalibrationType::None)) {
806 LOG("Calibration of DK inflation model for inflation index " << data.index() << " not requested.");
807 return;
808 }
809
810 Handle<ZeroInflationIndex> zInfIndex =
811 market_->zeroInflationIndex(model_->infdk(modelIdx)->name(), configurationInfCalibration_);
812 Real baseCPI = dontCalibrate_ ? 100. : zInfIndex->fixing(zInfIndex->zeroInflationTermStructure()->baseDate());
813 auto engine = QuantLib::ext::make_shared<QuantExt::AnalyticDkCpiCapFloorEngine>(*model_, modelIdx, baseCPI);
814 for (Size j = 0; j < cb.size(); j++)
815 cb[j]->setPricingEngine(engine);
816
817 if (dontCalibrate_)
818 return;
819
820 if (data.volatility().calibrate() && !data.reversion().calibrate()) {
821 // reset to initial params to ensure identical calibration outcomes for identical baskets
822 resetModelParams(CrossAssetModel::AssetType::INF, 0, modelIdx, Null<Size>());
823 if (data.calibrationType() == CalibrationType::Bootstrap && data.volatility().type() == ParamType::Piecewise) {
824 model_->calibrateInfDkVolatilitiesIterative(modelIdx, cb, *optimizationMethod_, endCriteria_);
825 } else {
826 model_->calibrateInfDkVolatilitiesGlobal(modelIdx, cb, *optimizationMethod_, endCriteria_);
827 }
828 } else if (!data.volatility().calibrate() && data.reversion().calibrate()) {
829 // reset to initial params to ensure identical calibration outcomes for identical baskets
830 resetModelParams(CrossAssetModel::AssetType::INF, 1, modelIdx, Null<Size>());
831 if (data.calibrationType() == CalibrationType::Bootstrap && data.reversion().type() == ParamType::Piecewise) {
832 model_->calibrateInfDkReversionsIterative(modelIdx, cb, *optimizationMethod_, endCriteria_);
833 } else {
834 model_->calibrateInfDkReversionsGlobal(modelIdx, cb, *optimizationMethod_, endCriteria_);
835 }
836 } else {
837 model_->calibrate(cb, *optimizationMethod_, endCriteria_);
838 }
839
840 DLOG("INF (DK) " << data.index() << " calibration errors:");
842 if (data.calibrationType() == CalibrationType::Bootstrap) {
843 if (fabs(inflationCalibrationErrors_[modelIdx]) < config_->bootstrapTolerance()) {
844 TLOGGERSTREAM("Calibration details:");
845 TLOGGERSTREAM(getCalibrationDetails(cb, inflationParam, false));
846 TLOGGERSTREAM("rmse = " << inflationCalibrationErrors_[modelIdx]);
847 } else {
848 string exceptionMessage = "INF (DK) " + std::to_string(modelIdx) + " calibration error " +
849 std::to_string(inflationCalibrationErrors_[modelIdx]) + " exceeds tolerance " +
850 std::to_string(config_->bootstrapTolerance());
851 StructuredModelWarningMessage("Failed to calibrate INF DK Model", exceptionMessage, id_).log();
852 WLOGGERSTREAM("Calibration details:");
853 WLOGGERSTREAM(getCalibrationDetails(cb, inflationParam, false));
854 WLOGGERSTREAM("rmse = " << inflationCalibrationErrors_[modelIdx]);
855 if (!continueOnError_)
856 QL_FAIL(exceptionMessage);
857 }
858 }
859}
860
861void CrossAssetModelBuilder::calibrateInflation(const InfJyData& data, Size modelIdx,
862 const QuantLib::ext::shared_ptr<InfJyBuilder>& jyBuilder,
863 const QuantLib::ext::shared_ptr<InfJyParameterization>& inflationParam) const {
864
865 LOG("Calibrate JY inflation model for inflation index " << data.index());
866
867 const auto& rrVol = data.realRateVolatility();
868 const auto& rrRev = data.realRateReversion();
869 const auto& idxVol = data.indexVolatility();
870
871 // Check if calibration is needed at all.
872 if ((!rrVol.calibrate() && !rrRev.calibrate() && !idxVol.calibrate()) ||
873 (data.calibrationType() == CalibrationType::None)) {
874 LOG("Calibration of JY inflation model for inflation index " << data.index() << " not requested.");
875 return;
876 }
877
878 Handle<ZeroInflationIndex> zInfIndex =
879 market_->zeroInflationIndex(model_->infjy(modelIdx)->name(), configurationInfCalibration_);
880
881 // We will need the 2 baskets of helpers
882 auto rrBasket = jyBuilder->realRateBasket();
883 auto idxBasket = jyBuilder->indexBasket();
884
885 // Attach engines to the helpers.
886 setJyPricingEngine(modelIdx, rrBasket, false);
887 setJyPricingEngine(modelIdx, idxBasket, false);
888
889 if (dontCalibrate_)
890 return;
891
892 // Single basket of helpers is useful in various places below.
893 vector<QuantLib::ext::shared_ptr<CalibrationHelper>> allHelpers = rrBasket;
894 allHelpers.insert(allHelpers.end(), idxBasket.begin(), idxBasket.end());
895
896 // Calibration configuration.
897 const auto& cc = data.calibrationConfiguration();
898
899 // if we link the real rate params to the nominal rate params, we copy them over now (ir calibration is done at this point)
900 if(data.linkRealRateParamsToNominalRateParams()) {
901 Size irIdx = model_->ccyIndex(model_->infjy(modelIdx)->currency());
902 copyModelParams(CrossAssetModel::AssetType::IR, 0, irIdx, Null<Size>(), CrossAssetModel::AssetType::INF, 0,
903 modelIdx, Null<Size>(), data.linkedRealRateVolatilityScaling());
904 copyModelParams(CrossAssetModel::AssetType::IR, 1, irIdx, Null<Size>(), CrossAssetModel::AssetType::INF, 1,
905 modelIdx, Null<Size>(), 1.0);
906 }
907
908 if (data.calibrationType() == CalibrationType::BestFit) {
909
910 // If calibration type is BestFit, do a global optimisation on the parameters that need to be calibrated.
911 DLOG("Calibration BestFit of JY inflation model for inflation index " << data.index() << " requested.");
912
913 // Indicate the parameters to calibrate
914 map<Size, bool> toCalibrate;
915 toCalibrate[0] = rrVol.calibrate();
916 toCalibrate[1] = rrRev.calibrate();
917 toCalibrate[2] = idxVol.calibrate();
918
919 // Calibrate the model.
920 resetModelParams(CrossAssetModel::AssetType::INF, 0, modelIdx, Null<Size>());
921 resetModelParams(CrossAssetModel::AssetType::INF, 1, modelIdx, Null<Size>());
922 resetModelParams(CrossAssetModel::AssetType::INF, 2, modelIdx, Null<Size>());
923 model_->calibrateInfJyGlobal(modelIdx, allHelpers, *optimizationMethod_, endCriteria_, toCalibrate);
924
925 } else {
926
927 // Calibration type is now Bootstrap, there are multiple options.
928 QL_REQUIRE(data.calibrationType() == CalibrationType::Bootstrap,
929 "JY inflation calibration expected a "
930 << "calibration type of None, BestFit or Bootstrap.");
931 QL_REQUIRE(!(rrRev.calibrate() && rrVol.calibrate()),
932 "Calibrating both the "
933 << "real rate reversion and real rate volatility using Bootstrap is not supported.");
934
935 if ((!rrVol.calibrate() && !rrRev.calibrate()) && idxVol.calibrate()) {
936
937 // Bootstrap the inflation index volatility only.
938 DLOG("Bootstrap calibration of JY index volatility for index " << data.index() << ".");
939 QL_REQUIRE(idxVol.type() == ParamType::Piecewise, "Index volatility parameter should be Piecewise for "
940 << "a Bootstrap calibration.");
941 // reset to initial params to ensure identical calibration outcomes for identical baskets
942 resetModelParams(CrossAssetModel::AssetType::INF, 2, modelIdx, Null<Size>());
943 model_->calibrateInfJyIterative(modelIdx, 2, idxBasket, *optimizationMethod_, endCriteria_);
944
945 } else if (rrVol.calibrate() && !idxVol.calibrate()) {
946
947 // Bootstrap the real rate volatility only
948 DLOG("Bootstrap calibration of JY real rate volatility for index " << data.index() << ".");
949 QL_REQUIRE(rrVol.type() == ParamType::Piecewise, "Real rate volatility parameter should be "
950 << "Piecewise for a Bootstrap calibration.");
951 // reset to initial params to ensure identical calibration outcomes for identical baskets
952 resetModelParams(CrossAssetModel::AssetType::INF, 0, modelIdx, Null<Size>());
953 model_->calibrateInfJyIterative(modelIdx, 0, rrBasket, *optimizationMethod_, endCriteria_);
954
955 } else if (rrRev.calibrate() && !idxVol.calibrate()) {
956
957 // Bootstrap the real rate reversion only
958 DLOG("Bootstrap calibration of JY real rate reversion for index " << data.index() << ".");
959 QL_REQUIRE(rrRev.type() == ParamType::Piecewise, "Real rate reversion parameter should be "
960 << "Piecewise for a Bootstrap calibration.");
961 // reset to initial params to ensure identical calibration outcomes for identical baskets
962 resetModelParams(CrossAssetModel::AssetType::INF, 1, modelIdx, Null<Size>());
963 model_->calibrateInfJyIterative(modelIdx, 1, rrBasket, *optimizationMethod_, endCriteria_);
964
965 } else if ((rrVol.calibrate() && idxVol.calibrate()) || (rrRev.calibrate() && idxVol.calibrate())) {
966
967 if (rrVol.calibrate()) {
968 DLOG("Bootstrap calibration of JY real rate volatility and index volatility for index " << data.index()
969 << ".");
970 } else {
971 DLOG("Bootstrap calibration of JY real rate reversion and index volatility for index " << data.index()
972 << ".");
973 }
974
975 // Bootstrap the real rate volatility and the index volatility
976 Size rrIdx = rrVol.calibrate() ? 0 : 1;
977 Size numIts = 0;
978 inflationCalibrationErrors_[modelIdx] = getCalibrationError(allHelpers);
979
980 // reset to initial params to ensure identical calibration outcomes for identical baskets
981 resetModelParams(CrossAssetModel::AssetType::INF, 2, modelIdx, Null<Size>());
982 resetModelParams(CrossAssetModel::AssetType::INF, rrIdx, modelIdx, Null<Size>());
983
984 while (inflationCalibrationErrors_[modelIdx] >
985 std::min(cc.rmseTolerance(), config_->bootstrapTolerance()) &&
986 numIts < cc.maxIterations()) {
987 model_->calibrateInfJyIterative(modelIdx, 2, idxBasket, *optimizationMethod_, endCriteria_);
988 model_->calibrateInfJyIterative(modelIdx, rrIdx, rrBasket, *optimizationMethod_, endCriteria_);
989 numIts++;
990 inflationCalibrationErrors_[modelIdx] = getCalibrationError(allHelpers);
991 }
992
993 DLOG("Bootstrap calibration of JY model stopped with number of iterations "
994 << numIts << " and rmse equal to " << std::scientific << std::setprecision(6)
995 << inflationCalibrationErrors_[modelIdx] << ".");
996
997 } else {
998 QL_FAIL("JY inflation bootstrap calibration does not support the combination of real rate volatility = "
999 << std::boolalpha << rrVol.calibrate() << ", real rate reversion = " << rrRev.calibrate() << " and "
1000 << "index volatility = " << idxVol.calibrate() << ".");
1001 }
1002 }
1003
1004 // Log the calibration details.
1005 TLOG("INF (JY) " << data.index() << " model parameters after calibration:");
1006 TLOG("Real rate vol times : " << inflationParam->parameterTimes(0));
1007 TLOG("Real rate vol values : " << inflationParam->parameterValues(0));
1008 TLOG("Real rate rev times : " << inflationParam->parameterTimes(1));
1009 TLOG("Real rate rev values : " << inflationParam->parameterValues(1));
1010 TLOG("R/N conversion times : " << inflationParam->parameterTimes(2));
1011 TLOG("R/N conversion values : " << inflationParam->parameterValues(2));
1012 DLOG("INF (JY) " << data.index() << " calibration errors:");
1013 inflationCalibrationErrors_[modelIdx] = getCalibrationError(allHelpers);
1014 if (data.calibrationType() == CalibrationType::Bootstrap) {
1015 if (fabs(inflationCalibrationErrors_[modelIdx]) < config_->bootstrapTolerance()) {
1016 TLOGGERSTREAM("Calibration details:");
1017 TLOGGERSTREAM(getCalibrationDetails(rrBasket, idxBasket, inflationParam, rrVol.calibrate()));
1018 TLOGGERSTREAM("rmse = " << inflationCalibrationErrors_[modelIdx]);
1019 } else {
1020 std::stringstream ss;
1021 ss << "INF (JY) " << modelIdx << " calibration error " << std::scientific
1022 << inflationCalibrationErrors_[modelIdx] << " exceeds tolerance " << config_->bootstrapTolerance();
1023 string exceptionMessage = ss.str();
1024 StructuredModelWarningMessage("Failed to calibrate INF JY Model", exceptionMessage, id_).log();
1025 WLOGGERSTREAM("Calibration details:");
1026 WLOGGERSTREAM(getCalibrationDetails(rrBasket, idxBasket, inflationParam, rrVol.calibrate()));
1027 WLOGGERSTREAM("rmse = " << inflationCalibrationErrors_[modelIdx]);
1028 if (!continueOnError_)
1029 QL_FAIL(exceptionMessage);
1030 }
1031 }
1032
1033 LOG("Finished calibrating JY inflation model for inflation index " << data.index());
1034}
1035
1037 const vector<QuantLib::ext::shared_ptr<CalibrationHelper>>& calibrationBasket,
1038 bool indexIsInterpolated) const {
1039
1040 DLOG("Start setting pricing engines on JY calibration instruments.");
1041
1042 // JY supports three types of calibration helpers. Generally, all of the calibration instruments in a basket will
1043 // be of the same type but we support all three here.
1044 QuantLib::ext::shared_ptr<PricingEngine> cpiCapFloorEngine;
1045 QuantLib::ext::shared_ptr<PricingEngine> yoyCapFloorEngine;
1046 QuantLib::ext::shared_ptr<PricingEngine> yoySwapEngine;
1047 QuantLib::ext::shared_ptr<InflationCouponPricer> yoyCouponPricer;
1048
1049 for (auto& ci : calibrationBasket) {
1050
1051 if (QuantLib::ext::shared_ptr<CpiCapFloorHelper> h = QuantLib::ext::dynamic_pointer_cast<CpiCapFloorHelper>(ci)) {
1052 if (!cpiCapFloorEngine) {
1053 cpiCapFloorEngine = QuantLib::ext::make_shared<AnalyticJyCpiCapFloorEngine>(*model_, modelIdx);
1054 }
1055 h->setPricingEngine(cpiCapFloorEngine);
1056 continue;
1057 }
1058
1059 if (QuantLib::ext::shared_ptr<YoYCapFloorHelper> h = QuantLib::ext::dynamic_pointer_cast<YoYCapFloorHelper>(ci)) {
1060 if (!yoyCapFloorEngine) {
1061 yoyCapFloorEngine =
1062 QuantLib::ext::make_shared<AnalyticJyYoYCapFloorEngine>(*model_, modelIdx, indexIsInterpolated);
1063 }
1064 h->setPricingEngine(yoyCapFloorEngine);
1065 continue;
1066 }
1067
1068 if (QuantLib::ext::shared_ptr<YoYSwapHelper> h = QuantLib::ext::dynamic_pointer_cast<YoYSwapHelper>(ci)) {
1069 // Here we need to attach the coupon pricer to all the YoY coupons and then the generic discounting swap
1070 // engine to the helper.
1071 if (!yoyCouponPricer) {
1072 yoyCouponPricer = QuantLib::ext::make_shared<JyYoYInflationCouponPricer>(*model_, modelIdx);
1073
1074 Size irIdx = model_->ccyIndex(model_->infjy(modelIdx)->currency());
1075 auto yts = model_->irlgm1f(irIdx)->termStructure();
1076 yoySwapEngine = QuantLib::ext::make_shared<DiscountingSwapEngine>(yts);
1077 }
1078
1079 const auto& yoyLeg = h->yoySwap()->yoyLeg();
1080 for (const auto& cf : yoyLeg) {
1081 if (auto yoyCoupon = QuantLib::ext::dynamic_pointer_cast<YoYInflationCoupon>(cf))
1082 yoyCoupon->setPricer(yoyCouponPricer);
1083 }
1084
1085 h->setPricingEngine(yoySwapEngine);
1086 continue;
1087 }
1088
1089 QL_FAIL("Only CPI cap floors, YoY cap floors and YoY swaps are supported for JY calibration.");
1090 }
1091
1092 DLOG("Finished setting pricing engines on JY calibration instruments.");
1093}
1094
1095} // namespace data
1096} // namespace ore
virtual void forceRecalculate()
QuantLib::Matrix correlationMatrix(const std::vector< std::string > &ccys)
void addCorrelation(const std::string &factor1, const std::string &factor2, QuantLib::Real correlation)
std::map< QuantExt::CrossAssetModel::AssetType, std::vector< std::pair< std::string, QuantLib::Size > > > ProcessInfo
const SalvagingAlgorithm::Type salvaging_
void setJyPricingEngine(QuantLib::Size modelIdx, const std::vector< QuantLib::ext::shared_ptr< QuantLib::CalibrationHelper > > &calibrationBasket, bool indexIsInterpolated) const
QuantLib::ext::shared_ptr< QuantExt::MarketObserver > marketObserver_
void copyModelParams(const CrossAssetModel::AssetType t0, const Size param0, const Size index0, const Size i0, const CrossAssetModel::AssetType t1, const Size param1, const Size index1, const Size i1, const Real mult) const
std::vector< std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > > swaptionBaskets_
void calibrateInflation(const InfDkData &data, QuantLib::Size modelIdx, const std::vector< QuantLib::ext::shared_ptr< QuantLib::BlackCalibrationHelper > > &calibrationBasket, const QuantLib::ext::shared_ptr< QuantExt::InfDkParametrization > &inflationParam) const
CrossAssetModelBuilder(const QuantLib::ext::shared_ptr< Market > &market, const QuantLib::ext::shared_ptr< CrossAssetModelData > &config, const std::string &configurationLgmCalibration=Market::defaultConfiguration, const std::string &configurationFxCalibration=Market::defaultConfiguration, const std::string &configurationEqCalibration=Market::defaultConfiguration, const std::string &configurationInfCalibration=Market::defaultConfiguration, const std::string &configurationCrCalibration=Market::defaultConfiguration, const std::string &configurationFinalModel=Market::defaultConfiguration, const bool dontCalibrate=false, const bool continueOnError=false, const std::string &referenceCalibrationGrid_="", const SalvagingAlgorithm::Type salvaging=SalvagingAlgorithm::None, const std::string &id="unknown")
std::vector< std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > > comOptionBaskets_
const std::vector< Real > & swaptionCalibrationErrors()
std::vector< std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > > eqOptionBaskets_
Handle< QuantExt::CrossAssetModel > model() const
return the model
std::map< QuantExt::CrossAssetModel::AssetType, std::map< QuantLib::Size, QuantLib::ext::shared_ptr< QuantExt::ModelBuilder > > > subBuilders_
Store model builders for each asset under each asset type.
const std::vector< Real > & eqOptionCalibrationErrors()
RelinkableHandle< QuantExt::CrossAssetModel > model_
const std::vector< Real > & inflationCalibrationErrors()
const QuantLib::ext::shared_ptr< ore::data::Market > market_
std::vector< std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > > fxOptionBaskets_
QuantLib::ext::shared_ptr< OptimizationMethod > optimizationMethod_
void resetModelParams(const CrossAssetModel::AssetType t, const Size param, const Size index, const Size i) const
const std::vector< Real > & fxOptionCalibrationErrors()
const QuantLib::ext::shared_ptr< CrossAssetModelData > config_
const std::vector< Real > & comOptionCalibrationErrors()
void log() const
generate Boost log record to pass to corresponding sinks
Definition: log.cpp:491
void setValues(std::vector< Real > values)
void setTimes(std::vector< Real > times)
void setCalibrate(const bool b)
Utility class for Structured Model errors.
Builder for a Lognormal COM model component.
configuration class for building correlation matrices
Build an cir model.
Build a cross asset model.
Builder for a Lognormal EQ model component.
Builder for a Lognormal FX model component.
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:290
Build a hw model.
Builder for a Dodgson-Kainth inflation model component.
Builder for a Jarrow Yildrim inflation model component.
Jarrow Yildirim inflation model component data for the cross asset model.
Hull White model data.
Build an lgm model.
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define WLOGGERSTREAM(text)
Definition: log.hpp:630
#define LOG(text)
Logging Macro (Level = Notice)
Definition: log.hpp:552
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define TLOGGERSTREAM(text)
Definition: log.hpp:633
#define TLOG(text)
Logging Macro (Level = Data)
Definition: log.hpp:556
Shared utilities for model building and calibration.
Real getCalibrationError(const std::vector< QuantLib::ext::shared_ptr< Helper > > &basket)
Definition: utilities.hpp:47
Size size(const ValueType &v)
Definition: value.cpp:145
std::string getCalibrationDetails(LgmCalibrationInfo &info, const std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > &basket, const QuantLib::ext::shared_ptr< IrLgm1fParametrization > &parametrization)
Definition: utilities.cpp:125
Serializable Credit Default Swap.
Definition: namespaces.docs:23
std::size_t count
Map text representations to QuantLib/QuantExt types.