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>
61#include <boost/algorithm/string/case_conv.hpp>
62#include <boost/lexical_cast.hpp>
72using QuantLib::DiscountingSwapEngine;
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),
93 endCriteria_(EndCriteria(1000, 500, 1E-8, 1E-8, 1E-8)) {
97 for (
auto ikv : okv.second) {
98 registerWith(ikv.second);
102 for (
auto const& c : config->correlations())
138 for (
auto ikv : okv.second)
139 if (ikv.second->requiresRecalibration())
156 const Size i)
const {
157 auto mp =
model_->MoveParameter(t, param, index, i);
158 for (Size idx = 0; idx < mp.size(); ++idx) {
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);
171 for (Size i = 0; i < mp0.size(); ++i)
174 for (Size i = 0; i < mp1.size(); ++i)
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) {
187 for (Size idx1 = 0,
count = 0; idx1 < mp1.size(); ++idx1) {
189 model_->setParam(idx1, sourceValues[
count++] * mult);
196 LOG(
"Start building CrossAssetModel");
198 QL_REQUIRE(
market_ != NULL,
"CrossAssetModelBuilder: no market given");
200 DLOG(
"configurations: LgmCalibration "
207 DLOG(
"Calibration of the model is disabled.");
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());
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");
250 QL_FAIL(
"Measure " <<
config_->measure() <<
" not recognized");
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;
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());
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>(
276 auto builder = QuantLib::ext::dynamic_pointer_cast<LgmBuilder>(
subBuilders_[CrossAssetModel::AssetType::IR][i]);
277 lgmBuilder.push_back(builder);
281 if (builder->requiresRecalibration())
282 recalibratedCurrencies.insert(builder->parametrization()->currency().code());
283 auto parametrization = builder->parametrization();
285 QL_REQUIRE(std::find(currencies.begin(), currencies.end(), parametrization->currency().code()) ==
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;
296 bool setCalibrationInfo =
false;
298 if (!buildersAreInitialized) {
299 subBuilders_[CrossAssetModel::AssetType::IR][i] = QuantLib::ext::make_shared<HwBuilder>(
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();
311 QL_REQUIRE(std::find(currencies.begin(), currencies.end(), parametrization->currency().code()) ==
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());
323 QL_REQUIRE(irParametrizations.size() > 0,
"missing IR parametrizations");
325 QuantLib::Currency domesticCcy = irParametrizations[0]->currency();
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];
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());
341 QL_REQUIRE(domCcy == domesticCcy,
"FX parametrization [" << i <<
"]=" << ccy <<
"/" << domCcy
342 <<
" does not match domestic ccy " << domesticCcy);
344 if (!buildersAreInitialized) {
348 auto builder = QuantLib::ext::dynamic_pointer_cast<FxBsBuilder>(
subBuilders_[CrossAssetModel::AssetType::FX][i]);
349 fxBuilder.push_back(builder);
351 QuantLib::ext::shared_ptr<QuantExt::FxBsParametrization> parametrization = builder->parametrization();
354 fxParametrizations.push_back(parametrization);
355 processInfo[CrossAssetModel::AssetType::FX].emplace_back(ccy.code() + domCcy.code(), 1);
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();
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>(
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();
378 eqParametrizations.push_back(parametrization);
379 eqNames.push_back(eqName);
380 processInfo[CrossAssetModel::AssetType::EQ].emplace_back(eqName, 1);
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>(
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) {
403 if (jyData->linkRealRateParamsToNominalRateParams()) {
404 Size ccyIndex = std::distance(currencies.begin(),
405 std::find(currencies.begin(), currencies.end(), jyData->currency()));
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);
422 subBuilders_[CrossAssetModel::AssetType::INF][i] = QuantLib::ext::make_shared<InfJyBuilder>(
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);
429 QL_FAIL(
"CrossAssetModelBuilder expects either DK or JY inflation model data.");
431 infIndices.push_back(imData->index());
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) {
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);
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) {
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);
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();
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>(
486 auto builder = QuantLib::ext::dynamic_pointer_cast<CommoditySchwartzModelBuilder>(
490 csBuilder.push_back(builder);
491 QuantLib::ext::shared_ptr<QuantExt::CommoditySchwartzParametrization> parametrization = builder->parametrization();
493 comParametrizations.push_back(parametrization);
494 comNames.push_back(comName);
495 processInfo[CrossAssetModel::AssetType::COM].emplace_back(comName, 1);
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);
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]);
525 QL_REQUIRE(fxParametrizations.size() == irParametrizations.size() - 1,
"mismatch in IR/FX parametrization sizes");
530 DLOG(
"CrossAssetModelBuilder: adding correlations.");
533 for (
auto it =
config_->correlations().begin(); it !=
config_->correlations().end(); it++) {
534 cmb.
addCorrelation(it->first.first, it->first.second, it->second);
539 TLOG(
"CAM correlation matrix:");
546 model_.linkTo(QuantLib::ext::make_shared<QuantExt::CrossAssetModel>(parametrizations, corrMatrix,
salvaging_, measure,
553 if (!buildersAreInitialized) {
561 for (Size i = 0; i < lgmBuilder.size(); i++) {
562 DLOG(
"IR Calibration " << i);
566 for (Size i = 0; i < hwBuilder.size(); i++) {
567 DLOG(
"IR Calibration " << i);
575 for (Size i = 0; i < irParametrizations.size(); i++) {
576 auto p = irParametrizations[i];
578 DLOG(
"Relinked discounting curve for " << p->currency().code() <<
" for FX calibration");
585 for (Size i = 0; i < fxParametrizations.size(); i++) {
586 QuantLib::ext::shared_ptr<FxBsData> fx =
config_->fxConfigs()[i];
589 DLOG(
"FX Calibration " << i <<
" skipped");
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.");
601 DLOG(
"FX Calibration " << i);
604 QuantLib::ext::shared_ptr<QuantExt::AnalyticCcLgmFxOptionEngine> engine =
605 QuantLib::ext::make_shared<QuantExt::AnalyticCcLgmFxOptionEngine>(*
model_, i);
624 DLOG(
"FX " << fx->foreignCcy() <<
" calibration errors:");
633 std::string exceptionMessage =
"FX BS " + std::to_string(i) +
" calibration error " +
635 " exceeds tolerance " +
636 std::to_string(
config_->bootstrapTolerance());
643 QL_FAIL(exceptionMessage);
647 fxBuilder[i]->setCalibrationDone();
654 for (Size i = 0; i < irParametrizations.size(); i++) {
655 auto p = irParametrizations[i];
657 DLOG(
"Relinked discounting curve for " << p->currency().code() <<
" for EQ calibration");
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");
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.");
678 DLOG(
"EQ Calibration " << i);
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);
698 DLOG(
"EQ " << eq->eqName() <<
" calibration errors:");
707 std::string exceptionMessage =
"EQ BS " + std::to_string(i) +
" calibration error " +
709 " exceeds tolerance " +
710 std::to_string(
config_->bootstrapTolerance());
717 QL_FAIL(exceptionMessage);
721 eqBuilder[i]->setCalibrationDone();
728 for (Size i = 0; i < csBuilder.size(); i++) {
729 DLOG(
"COM Calibration " << i);
737 for (Size i = 0; i < irParametrizations.size(); i++) {
738 auto p = irParametrizations[i];
740 DLOG(
"Relinked discounting curve for " << p->currency().code() <<
" for INF calibration");
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.");
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.");
773 jyBuilder->setCalibrationDone();
775 QL_FAIL(
"CrossAssetModelBuilder expects either DK or JY inflation model data.");
783 for (Size i = 0; i < irParametrizations.size(); i++) {
784 auto p = irParametrizations[i];
786 DLOG(
"Relinked discounting curve for " << p->currency().code() <<
" as final model curves");
789 DLOG(
"Building CrossAssetModel done");
799 const vector<QuantLib::ext::shared_ptr<BlackCalibrationHelper>>& cb,
800 const QuantLib::ext::shared_ptr<InfDkParametrization>& inflationParam)
const {
802 LOG(
"Calibrate DK inflation model for inflation index " <<
data.index());
804 if ((!
data.volatility().calibrate() && !
data.reversion().calibrate()) ||
806 LOG(
"Calibration of DK inflation model for inflation index " <<
data.index() <<
" not requested.");
810 Handle<ZeroInflationIndex> zInfIndex =
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);
820 if (
data.volatility().calibrate() && !
data.reversion().calibrate()) {
822 resetModelParams(CrossAssetModel::AssetType::INF, 0, modelIdx, Null<Size>());
828 }
else if (!
data.volatility().calibrate() &&
data.reversion().calibrate()) {
830 resetModelParams(CrossAssetModel::AssetType::INF, 1, modelIdx, Null<Size>());
840 DLOG(
"INF (DK) " <<
data.index() <<
" calibration errors:");
848 string exceptionMessage =
"INF (DK) " + std::to_string(modelIdx) +
" calibration error " +
850 std::to_string(
config_->bootstrapTolerance());
851 StructuredModelWarningMessage(
"Failed to calibrate INF DK Model", exceptionMessage,
id_).log();
856 QL_FAIL(exceptionMessage);
862 const QuantLib::ext::shared_ptr<InfJyBuilder>& jyBuilder,
863 const QuantLib::ext::shared_ptr<InfJyParameterization>& inflationParam)
const {
865 LOG(
"Calibrate JY inflation model for inflation index " <<
data.index());
867 const auto& rrVol =
data.realRateVolatility();
868 const auto& rrRev =
data.realRateReversion();
869 const auto& idxVol =
data.indexVolatility();
872 if ((!rrVol.calibrate() && !rrRev.calibrate() && !idxVol.calibrate()) ||
874 LOG(
"Calibration of JY inflation model for inflation index " <<
data.index() <<
" not requested.");
878 Handle<ZeroInflationIndex> zInfIndex =
882 auto rrBasket = jyBuilder->realRateBasket();
883 auto idxBasket = jyBuilder->indexBasket();
893 vector<QuantLib::ext::shared_ptr<CalibrationHelper>> allHelpers = rrBasket;
894 allHelpers.insert(allHelpers.end(), idxBasket.begin(), idxBasket.end());
897 const auto& cc =
data.calibrationConfiguration();
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);
911 DLOG(
"Calibration BestFit of JY inflation model for inflation index " <<
data.index() <<
" requested.");
914 map<Size, bool> toCalibrate;
915 toCalibrate[0] = rrVol.calibrate();
916 toCalibrate[1] = rrRev.calibrate();
917 toCalibrate[2] = idxVol.calibrate();
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>());
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.");
935 if ((!rrVol.calibrate() && !rrRev.calibrate()) && idxVol.calibrate()) {
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.");
942 resetModelParams(CrossAssetModel::AssetType::INF, 2, modelIdx, Null<Size>());
945 }
else if (rrVol.calibrate() && !idxVol.calibrate()) {
948 DLOG(
"Bootstrap calibration of JY real rate volatility for index " <<
data.index() <<
".");
950 <<
"Piecewise for a Bootstrap calibration.");
952 resetModelParams(CrossAssetModel::AssetType::INF, 0, modelIdx, Null<Size>());
955 }
else if (rrRev.calibrate() && !idxVol.calibrate()) {
958 DLOG(
"Bootstrap calibration of JY real rate reversion for index " <<
data.index() <<
".");
960 <<
"Piecewise for a Bootstrap calibration.");
962 resetModelParams(CrossAssetModel::AssetType::INF, 1, modelIdx, Null<Size>());
965 }
else if ((rrVol.calibrate() && idxVol.calibrate()) || (rrRev.calibrate() && idxVol.calibrate())) {
967 if (rrVol.calibrate()) {
968 DLOG(
"Bootstrap calibration of JY real rate volatility and index volatility for index " <<
data.index()
971 DLOG(
"Bootstrap calibration of JY real rate reversion and index volatility for index " <<
data.index()
976 Size rrIdx = rrVol.calibrate() ? 0 : 1;
981 resetModelParams(CrossAssetModel::AssetType::INF, 2, modelIdx, Null<Size>());
982 resetModelParams(CrossAssetModel::AssetType::INF, rrIdx, modelIdx, Null<Size>());
985 std::min(cc.rmseTolerance(),
config_->bootstrapTolerance()) &&
986 numIts < cc.maxIterations()) {
993 DLOG(
"Bootstrap calibration of JY model stopped with number of iterations "
994 << numIts <<
" and rmse equal to " << std::scientific << std::setprecision(6)
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() <<
".");
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:");
1020 std::stringstream ss;
1021 ss <<
"INF (JY) " << modelIdx <<
" calibration error " << std::scientific
1023 string exceptionMessage = ss.str();
1024 StructuredModelWarningMessage(
"Failed to calibrate INF JY Model", exceptionMessage,
id_).log();
1029 QL_FAIL(exceptionMessage);
1033 LOG(
"Finished calibrating JY inflation model for inflation index " <<
data.index());
1037 const vector<QuantLib::ext::shared_ptr<CalibrationHelper>>& calibrationBasket,
1038 bool indexIsInterpolated)
const {
1040 DLOG(
"Start setting pricing engines on JY calibration instruments.");
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;
1049 for (
auto& ci : calibrationBasket) {
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);
1055 h->setPricingEngine(cpiCapFloorEngine);
1059 if (QuantLib::ext::shared_ptr<YoYCapFloorHelper> h = QuantLib::ext::dynamic_pointer_cast<YoYCapFloorHelper>(ci)) {
1060 if (!yoyCapFloorEngine) {
1062 QuantLib::ext::make_shared<AnalyticJyYoYCapFloorEngine>(*
model_, modelIdx, indexIsInterpolated);
1064 h->setPricingEngine(yoyCapFloorEngine);
1068 if (QuantLib::ext::shared_ptr<YoYSwapHelper> h = QuantLib::ext::dynamic_pointer_cast<YoYSwapHelper>(ci)) {
1071 if (!yoyCouponPricer) {
1072 yoyCouponPricer = QuantLib::ext::make_shared<JyYoYInflationCouponPricer>(*
model_, modelIdx);
1074 Size irIdx =
model_->ccyIndex(
model_->infjy(modelIdx)->currency());
1075 auto yts =
model_->irlgm1f(irIdx)->termStructure();
1076 yoySwapEngine = QuantLib::ext::make_shared<DiscountingSwapEngine>(yts);
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);
1085 h->setPricingEngine(yoySwapEngine);
1089 QL_FAIL(
"Only CPI cap floors, YoY cap floors and YoY swaps are supported for JY calibration.");
1092 DLOG(
"Finished setting pricing engines on JY calibration instruments.");
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 forceRecalculate() override
void performCalculations() const override
std::vector< Real > comOptionCalibrationErrors_
void setJyPricingEngine(QuantLib::Size modelIdx, const std::vector< QuantLib::ext::shared_ptr< QuantLib::CalibrationHelper > > &calibrationBasket, bool indexIsInterpolated) const
std::vector< Array > optionExpiries_
QuantLib::ext::shared_ptr< QuantExt::MarketObserver > marketObserver_
const std::string configurationComCalibration_
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< Array > fxOptionExpiries_
std::vector< Array > comOptionExpiries_
std::vector< std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > > swaptionBaskets_
std::vector< Array > eqOptionExpiries_
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
const bool continueOnError_
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::string configurationCrCalibration_
const std::vector< Real > & swaptionCalibrationErrors()
std::vector< std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > > eqOptionBaskets_
const std::string configurationInfCalibration_
Handle< QuantExt::CrossAssetModel > model() const
return the model
const std::string configurationFxCalibration_
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()
std::vector< Array > swaptionMaturities_
RelinkableHandle< QuantExt::CrossAssetModel > model_
const std::string configurationFinalModel_
bool requiresRecalibration() const override
const std::string configurationEqCalibration_
const std::vector< Real > & inflationCalibrationErrors()
const QuantLib::ext::shared_ptr< ore::data::Market > market_
std::vector< std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > > fxOptionBaskets_
std::vector< Real > eqOptionCalibrationErrors_
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 std::string referenceCalibrationGrid_
const QuantLib::ext::shared_ptr< CrossAssetModelData > config_
const std::string configurationLgmCalibration_
const bool dontCalibrate_
std::vector< Real > inflationCalibrationErrors_
const std::vector< Real > & comOptionCalibrationErrors()
std::vector< Real > swaptionCalibrationErrors_
std::vector< Real > fxOptionCalibrationErrors_
void log() const
generate Boost log record to pass to corresponding sinks
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 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.
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.
Classes and functions for log message handling.
#define WLOGGERSTREAM(text)
#define LOG(text)
Logging Macro (Level = Notice)
#define DLOG(text)
Logging Macro (Level = Debug)
#define TLOGGERSTREAM(text)
#define TLOG(text)
Logging Macro (Level = Data)
Shared utilities for model building and calibration.
Real getCalibrationError(const std::vector< QuantLib::ext::shared_ptr< Helper > > &basket)
Size size(const ValueType &v)
std::string getCalibrationDetails(LgmCalibrationInfo &info, const std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > &basket, const QuantLib::ext::shared_ptr< IrLgm1fParametrization > ¶metrization)
Serializable Credit Default Swap.
Map text representations to QuantLib/QuantExt types.