Skip to content

Commit 03394de

Browse files
pcaspersjenkins
authored andcommitted
QPR-12329 use same start value for calibration
1 parent 84af98e commit 03394de

2 files changed

Lines changed: 57 additions & 16 deletions

File tree

OREData/ored/model/crossassetmodelbuilder.cpp

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222
#include <ored/model/crossassetmodelbuilder.hpp>
2323
#include <ored/model/eqbsbuilder.hpp>
2424
#include <ored/model/fxbsbuilder.hpp>
25+
#include <ored/model/hwbuilder.hpp>
2526
#include <ored/model/inflation/infdkbuilder.hpp>
2627
#include <ored/model/inflation/infjybuilder.hpp>
2728
#include <ored/model/inflation/infjydata.hpp>
28-
#include <ored/model/lgmbuilder.hpp>
2929
#include <ored/model/irhwmodeldata.hpp>
30-
#include <ored/model/hwbuilder.hpp>
30+
#include <ored/model/lgmbuilder.hpp>
3131
#include <ored/model/structuredmodelerror.hpp>
3232
#include <ored/model/utilities.hpp>
3333
#include <ored/utilities/correlationmatrix.hpp>
@@ -150,6 +150,15 @@ void CrossAssetModelBuilder::performCalculations() const {
150150
}
151151
}
152152

153+
void CrossAssetModelBuilder::resetModelParams(const AssetType t, const Size param, const Size index, const Size i) const {
154+
auto mp = model_->MoveParameter(t, param, index, i);
155+
for (Size idx = 0; idx < mp.size(); ++idx) {
156+
if (!mp[idx]) {
157+
model_->setParam(idx, params_[idx]);
158+
}
159+
}
160+
}
161+
153162
void CrossAssetModelBuilder::buildModel() const {
154163

155164
LOG("Start building CrossAssetModel");
@@ -222,7 +231,7 @@ void CrossAssetModelBuilder::buildModel() const {
222231
for (Size i = 0; i < config_->irConfigs().size(); i++) {
223232
auto irConfig = config_->irConfigs()[i];
224233
DLOG("IR Parametrization " << i << " qualifier " << irConfig->qualifier());
225-
234+
226235
if (auto ir = boost::dynamic_pointer_cast<IrLgmData>(irConfig)) {
227236
if (!buildersAreInitialized) {
228237
subBuilders_[CrossAssetModel::AssetType::IR][i] = boost::make_shared<LgmBuilder>(
@@ -244,12 +253,11 @@ void CrossAssetModelBuilder::buildModel() const {
244253
irParametrizations.push_back(parametrization);
245254
irDiscountCurves.push_back(builder->discountCurve());
246255
processInfo[CrossAssetModel::AssetType::IR].emplace_back(ir->ccy(), 1);
247-
}
248-
else if(auto ir = boost::dynamic_pointer_cast<HwModelData>(irConfig)) {
256+
} else if (auto ir = boost::dynamic_pointer_cast<HwModelData>(irConfig)) {
249257
bool evaluateBankAccount = true; // updated in cross asset model for non-base ccys
250258
bool setCalibrationInfo = false;
251259
HwModel::Discretization discr = HwModel::Discretization::Euler;
252-
if(!buildersAreInitialized) {
260+
if (!buildersAreInitialized) {
253261
subBuilders_[CrossAssetModel::AssetType::IR][i] = boost::make_shared<HwBuilder>(
254262
market_, ir, measure, discr, evaluateBankAccount, configurationLgmCalibration_,
255263
config_->bootstrapTolerance(), continueOnError_, referenceCalibrationGrid_, setCalibrationInfo);
@@ -317,9 +325,9 @@ void CrossAssetModelBuilder::buildModel() const {
317325
QuantLib::Currency eqCcy = ore::data::parseCurrency(eq->currency());
318326
QL_REQUIRE(std::find(currencies.begin(), currencies.end(), eqCcy.code()) != currencies.end(),
319327
"Currency (" << eqCcy << ") for equity " << eqName << " not covered by CrossAssetModelData");
320-
if(!buildersAreInitialized) {
328+
if (!buildersAreInitialized) {
321329
subBuilders_[CrossAssetModel::AssetType::EQ][i] = boost::make_shared<EqBsBuilder>(
322-
market_, eq, domesticCcy, configurationEqCalibration_, referenceCalibrationGrid_);
330+
market_, eq, domesticCcy, configurationEqCalibration_, referenceCalibrationGrid_);
323331
}
324332
boost::shared_ptr<EqBsBuilder> builder =
325333
boost::dynamic_pointer_cast<EqBsBuilder>(subBuilders_[CrossAssetModel::AssetType::EQ][i]);
@@ -336,9 +344,9 @@ void CrossAssetModelBuilder::buildModel() const {
336344
boost::shared_ptr<InflationModelData> imData = config_->infConfigs()[i];
337345
DLOG("Inflation parameterisation (" << i << ") for index " << imData->index());
338346
if (auto dkData = boost::dynamic_pointer_cast<InfDkData>(imData)) {
339-
if(!buildersAreInitialized) {
347+
if (!buildersAreInitialized) {
340348
subBuilders_[CrossAssetModel::AssetType::INF][i] = boost::make_shared<InfDkBuilder>(
341-
market_, dkData, configurationInfCalibration_, referenceCalibrationGrid_, dontCalibrate_);
349+
market_, dkData, configurationInfCalibration_, referenceCalibrationGrid_, dontCalibrate_);
342350
}
343351
boost::shared_ptr<InfDkBuilder> builder =
344352
boost::dynamic_pointer_cast<InfDkBuilder>(subBuilders_[CrossAssetModel::AssetType::INF][i]);
@@ -369,8 +377,9 @@ void CrossAssetModelBuilder::buildModel() const {
369377
LOG("CR LGM Parametrization " << i);
370378
boost::shared_ptr<CrLgmData> cr = config_->crLgmConfigs()[i];
371379
string crName = cr->name();
372-
if(!buildersAreInitialized) {
373-
subBuilders_[CrossAssetModel::AssetType::CR][i] = boost::make_shared<CrLgmBuilder>(market_, cr, configurationCrCalibration_);
380+
if (!buildersAreInitialized) {
381+
subBuilders_[CrossAssetModel::AssetType::CR][i] =
382+
boost::make_shared<CrLgmBuilder>(market_, cr, configurationCrCalibration_);
374383
}
375384
auto builder = boost::dynamic_pointer_cast<CrLgmBuilder>(subBuilders_[CrossAssetModel::AssetType::CR][i]);
376385
boost::shared_ptr<QuantExt::CrLgm1fParametrization> parametrization = builder->parametrization();
@@ -472,6 +481,14 @@ void CrossAssetModelBuilder::buildModel() const {
472481
model_.linkTo(boost::make_shared<QuantExt::CrossAssetModel>(parametrizations, corrMatrix, salvaging_, measure,
473482
config_->discretization()));
474483

484+
/* Store initial params to ensure identical start values when recalibrating a component.
485+
This is only used for fx, eq, inf, cr, com, for ir this is handled in LgmBuilder directly.
486+
Therefore it does not matter that the IR parameters are calibrated at this point already. */
487+
488+
if (!buildersAreInitialized) {
489+
params_ = model_->params();
490+
}
491+
475492
/*************************
476493
* Calibrate IR components
477494
*/
@@ -521,6 +538,9 @@ void CrossAssetModelBuilder::buildModel() const {
521538

522539
if (!dontCalibrate_) {
523540

541+
// reset to initial params to ensure identical calibration outcomes for identical baskets
542+
resetModelParams(CrossAssetModel::AssetType::FX, 0, i, Null<Size>());
543+
524544
if (fx->calibrationType() == CalibrationType::Bootstrap && fx->sigmaParamType() == ParamType::Piecewise)
525545
model_->calibrateBsVolatilitiesIterative(CrossAssetModel::AssetType::FX, i, fxOptionBaskets_[i],
526546
*optimizationMethod_, endCriteria_);
@@ -584,6 +604,9 @@ void CrossAssetModelBuilder::buildModel() const {
584604

585605
if (!dontCalibrate_) {
586606

607+
// reset to initial params to ensure identical calibration outcomes for identical baskets
608+
resetModelParams(CrossAssetModel::AssetType::EQ, 0, i, Null<Size>());
609+
587610
if (eq->calibrationType() == CalibrationType::Bootstrap && eq->sigmaParamType() == ParamType::Piecewise)
588611
model_->calibrateBsVolatilitiesIterative(CrossAssetModel::AssetType::EQ, i, eqOptionBaskets_[i],
589612
*optimizationMethod_, endCriteria_);
@@ -623,7 +646,7 @@ void CrossAssetModelBuilder::buildModel() const {
623646
DLOG("COM Calibration " << i);
624647
comOptionCalibrationErrors_[i] = csBuilder[i]->error();
625648
}
626-
649+
627650
/*************************
628651
* Relink LGM discount curves to curves used for INF calibration
629652
*/
@@ -700,12 +723,16 @@ void CrossAssetModelBuilder::calibrateInflation(const InfDkData& data, Size mode
700723
return;
701724

702725
if (data.volatility().calibrate() && !data.reversion().calibrate()) {
726+
// reset to initial params to ensure identical calibration outcomes for identical baskets
727+
resetModelParams(CrossAssetModel::AssetType::INF, 0, modelIdx, Null<Size>());
703728
if (data.calibrationType() == CalibrationType::Bootstrap && data.volatility().type() == ParamType::Piecewise) {
704729
model_->calibrateInfDkVolatilitiesIterative(modelIdx, cb, *optimizationMethod_, endCriteria_);
705730
} else {
706731
model_->calibrateInfDkVolatilitiesGlobal(modelIdx, cb, *optimizationMethod_, endCriteria_);
707732
}
708733
} else if (!data.volatility().calibrate() && data.reversion().calibrate()) {
734+
// reset to initial params to ensure identical calibration outcomes for identical baskets
735+
resetModelParams(CrossAssetModel::AssetType::INF, 1, modelIdx, Null<Size>());
709736
if (data.calibrationType() == CalibrationType::Bootstrap && data.reversion().type() == ParamType::Piecewise) {
710737
model_->calibrateInfDkReversionsIterative(modelIdx, cb, *optimizationMethod_, endCriteria_);
711738
} else {
@@ -804,6 +831,8 @@ void CrossAssetModelBuilder::calibrateInflation(const InfJyData& data, Size mode
804831
DLOG("Bootstrap calibration of JY index volatility for index " << data.index() << ".");
805832
QL_REQUIRE(idxVol.type() == ParamType::Piecewise, "Index volatility parameter should be Piecewise for "
806833
<< "a Bootstrap calibration.");
834+
// reset to initial params to ensure identical calibration outcomes for identical baskets
835+
resetModelParams(CrossAssetModel::AssetType::INF, 2, modelIdx, Null<Size>());
807836
model_->calibrateInfJyIterative(modelIdx, 2, idxBasket, *optimizationMethod_, endCriteria_);
808837

809838
} else if (rrVol.calibrate() && !idxVol.calibrate()) {
@@ -812,6 +841,8 @@ void CrossAssetModelBuilder::calibrateInflation(const InfJyData& data, Size mode
812841
DLOG("Bootstrap calibration of JY real rate volatility for index " << data.index() << ".");
813842
QL_REQUIRE(rrVol.type() == ParamType::Piecewise, "Real rate volatility parameter should be "
814843
<< "Piecewise for a Bootstrap calibration.");
844+
// reset to initial params to ensure identical calibration outcomes for identical baskets
845+
resetModelParams(CrossAssetModel::AssetType::INF, 0, modelIdx, Null<Size>());
815846
model_->calibrateInfJyIterative(modelIdx, 0, rrBasket, *optimizationMethod_, endCriteria_);
816847

817848
} else if (rrRev.calibrate() && !idxVol.calibrate()) {
@@ -820,6 +851,8 @@ void CrossAssetModelBuilder::calibrateInflation(const InfJyData& data, Size mode
820851
DLOG("Bootstrap calibration of JY real rate reversion for index " << data.index() << ".");
821852
QL_REQUIRE(rrRev.type() == ParamType::Piecewise, "Real rate reversion parameter should be "
822853
<< "Piecewise for a Bootstrap calibration.");
854+
// reset to initial params to ensure identical calibration outcomes for identical baskets
855+
resetModelParams(CrossAssetModel::AssetType::INF, 1, modelIdx, Null<Size>());
823856
model_->calibrateInfJyIterative(modelIdx, 1, rrBasket, *optimizationMethod_, endCriteria_);
824857

825858
} else if ((rrVol.calibrate() && idxVol.calibrate()) || (rrRev.calibrate() && idxVol.calibrate())) {
@@ -837,6 +870,10 @@ void CrossAssetModelBuilder::calibrateInflation(const InfJyData& data, Size mode
837870
Size numIts = 0;
838871
inflationCalibrationErrors_[modelIdx] = getCalibrationError(allHelpers);
839872

873+
// reset to initial params to ensure identical calibration outcomes for identical baskets
874+
resetModelParams(CrossAssetModel::AssetType::INF, 2, modelIdx, Null<Size>());
875+
resetModelParams(CrossAssetModel::AssetType::INF, 2, rrIdx, Null<Size>());
876+
840877
while (inflationCalibrationErrors_[modelIdx] > cc.rmseTolerance() && numIts < cc.maxIterations()) {
841878
model_->calibrateInfJyIterative(modelIdx, 2, idxBasket, *optimizationMethod_, endCriteria_);
842879
model_->calibrateInfJyIterative(modelIdx, rrIdx, rrBasket, *optimizationMethod_, endCriteria_);
@@ -880,8 +917,9 @@ void CrossAssetModelBuilder::calibrateInflation(const InfJyData& data, Size mode
880917
LOG("Finished calibrating JY inflation model for inflation index " << data.index());
881918
}
882919

883-
void CrossAssetModelBuilder::setJyPricingEngine(
884-
Size modelIdx, const vector<boost::shared_ptr<CalibrationHelper>>& calibrationBasket, bool indexIsInterpolated) const {
920+
void CrossAssetModelBuilder::setJyPricingEngine(Size modelIdx,
921+
const vector<boost::shared_ptr<CalibrationHelper>>& calibrationBasket,
922+
bool indexIsInterpolated) const {
885923

886924
DLOG("Start setting pricing engines on JY calibration instruments.");
887925

@@ -904,7 +942,8 @@ void CrossAssetModelBuilder::setJyPricingEngine(
904942

905943
if (boost::shared_ptr<YoYCapFloorHelper> h = boost::dynamic_pointer_cast<YoYCapFloorHelper>(ci)) {
906944
if (!yoyCapFloorEngine) {
907-
yoyCapFloorEngine = boost::make_shared<AnalyticJyYoYCapFloorEngine>(*model_, modelIdx, indexIsInterpolated);
945+
yoyCapFloorEngine =
946+
boost::make_shared<AnalyticJyYoYCapFloorEngine>(*model_, modelIdx, indexIsInterpolated);
908947
}
909948
h->setPricingEngine(yoyCapFloorEngine);
910949
continue;

OREData/ored/model/crossassetmodelbuilder.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ class CrossAssetModelBuilder : public QuantExt::ModelBuilder {
110110
private:
111111
void performCalculations() const override;
112112
void buildModel() const;
113+
void resetModelParams(const AssetType t, const Size param, const Size index, const Size i) const;
113114

114115
mutable std::vector<std::vector<boost::shared_ptr<BlackCalibrationHelper>>> swaptionBaskets_;
115116
mutable std::vector<std::vector<boost::shared_ptr<BlackCalibrationHelper>>> fxOptionBaskets_;
@@ -130,6 +131,7 @@ class CrossAssetModelBuilder : public QuantExt::ModelBuilder {
130131
mutable std::map<QuantExt::CrossAssetModel::AssetType,
131132
std::map<QuantLib::Size, boost::shared_ptr<QuantExt::ModelBuilder>>>
132133
subBuilders_;
134+
mutable Array params_;
133135

134136
const boost::shared_ptr<ore::data::Market> market_;
135137
const boost::shared_ptr<CrossAssetModelData> config_;

0 commit comments

Comments
 (0)