Skip to content

Commit e44b69e

Browse files
pcaspersjenkins
authored andcommitted
QPR-12306 store calibration discount curve separately from model discount curve
the cam model builder relinks the model discount curve, so on recalibration of the lgm component a wrong discount curve is used if we don't separate the calibration discount curve from the model discount curve
1 parent cb254b3 commit e44b69e

2 files changed

Lines changed: 27 additions & 23 deletions

File tree

OREData/ored/model/lgmbuilder.cpp

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,15 @@ LgmBuilder::LgmBuilder(const boost::shared_ptr<ore::data::Market>& market, const
196196
swapIndex_ = market_->swapIndex(market_->swapIndexBase(data_->qualifier(), configuration_), configuration_);
197197
// see the comment for dinscountCurve() in the interface
198198
modelDiscountCurve_ = RelinkableHandle<YieldTermStructure>(*swapIndex_->discountingTermStructure());
199+
calibrationDiscountCurve_ = Handle<YieldTermStructure>(*swapIndex_->discountingTermStructure());
199200
} catch (const std::exception& e) {
200201
StructuredModelErrorMessage(
201202
"Error when retrieving swap index base for qualifier '" + data_->qualifier() +
202203
"'. Use market discount curve instead of swap index discount curve as a fallback.",
203204
e.what(), id_)
204205
.log();
205206
modelDiscountCurve_ = RelinkableHandle<YieldTermStructure>(*market_->discountCurve(currency_, configuration_));
207+
calibrationDiscountCurve_ = Handle<YieldTermStructure>(*swapIndex_->discountingTermStructure());
206208
}
207209

208210
if (requiresCalibration_) {
@@ -212,6 +214,7 @@ LgmBuilder::LgmBuilder(const boost::shared_ptr<ore::data::Market>& market, const
212214
marketObserver_->addObservable(shortSwapIndex_->discountingTermStructure());
213215
}
214216
marketObserver_->addObservable(modelDiscountCurve_);
217+
marketObserver_->addObservable(calibrationDiscountCurve_);
215218
registerWith(marketObserver_);
216219
// notify observers of all market data changes, not only when not calculated
217220
alwaysForwardNotifications();
@@ -322,16 +325,18 @@ bool LgmBuilder::requiresRecalibration() const {
322325
void LgmBuilder::performCalculations() const {
323326

324327
DLOG("Recalibrate LGM model for qualifier " << data_->qualifier() << " currency " << currency_);
328+
std::cout << "reaclibrate model ... " << std::flush;
325329

326330
if (!requiresRecalibration()) {
327331
DLOG("Skipping calibration as nothing has changed");
332+
std::cout << " skipped." << std::endl;
328333
return;
329334
}
330335

331336
// reset lgm observer's updated flag
332337
marketObserver_->hasUpdated(true);
333338

334-
if (swaptionBasketRefDate_ != modelDiscountCurve_->referenceDate()) {
339+
if (swaptionBasketRefDate_ != calibrationDiscountCurve_->referenceDate()) {
335340
// build swaption basket if required, i.e. if reference date has changed since last build
336341
buildSwaptionBasket();
337342
volSurfaceChanged(true);
@@ -343,8 +348,7 @@ void LgmBuilder::performCalculations() const {
343348
}
344349

345350
for (Size j = 0; j < swaptionBasket_.size(); j++) {
346-
auto engine =
347-
boost::make_shared<QuantExt::AnalyticLgmSwaptionEngine>(model_, modelDiscountCurve_);
351+
auto engine = boost::make_shared<QuantExt::AnalyticLgmSwaptionEngine>(model_, calibrationDiscountCurve_);
348352
engine->enableCache(!data_->calibrateH(), !data_->calibrateA());
349353
swaptionBasket_[j]->setPricingEngine(engine);
350354
// necessary if notifications are disabled (observation mode = Disable)
@@ -459,6 +463,9 @@ void LgmBuilder::performCalculations() const {
459463
DLOG("Apply scaling " << data_->scaling() << " to the " << data_->qualifier() << " LGM model");
460464
parametrization_->scaling() = data_->scaling();
461465
}
466+
467+
std::cout << " done." << std::endl;
468+
462469
} // performCalculations()
463470

464471
void LgmBuilder::getExpiryAndTerm(const Size j, Period& expiryPb, Period& termPb, Date& expiryDb, Date& termDb,
@@ -607,32 +614,28 @@ void LgmBuilder::buildSwaptionBasket() const {
607614

608615
if (expiryDateBased && termDateBased) {
609616
Real shift = svts_->volatilityType() == ShiftedLognormal ? svts_->shift(expiryDb, termT) : 0.0;
610-
std::tie(helper, updatedStrike) =
611-
createSwaptionHelper(expiryDb, termDb, svts_, vol, iborIndex, fixedLegTenor, fixedDayCounter,
612-
floatDayCounter, modelDiscountCurve_, calibrationErrorType_,
613-
strikeValue, shift, settlementDays, averagingMethod);
617+
std::tie(helper, updatedStrike) = createSwaptionHelper(
618+
expiryDb, termDb, svts_, vol, iborIndex, fixedLegTenor, fixedDayCounter, floatDayCounter,
619+
calibrationDiscountCurve_, calibrationErrorType_, strikeValue, shift, settlementDays, averagingMethod);
614620
}
615621
if (expiryDateBased && !termDateBased) {
616622
Real shift = svts_->volatilityType() == ShiftedLognormal ? svts_->shift(expiryDb, termPb) : 0.0;
617-
std::tie(helper, updatedStrike) =
618-
createSwaptionHelper(expiryDb, termPb, svts_, vol, iborIndex, fixedLegTenor, fixedDayCounter,
619-
floatDayCounter, modelDiscountCurve_, calibrationErrorType_,
620-
strikeValue, shift, settlementDays, averagingMethod);
623+
std::tie(helper, updatedStrike) = createSwaptionHelper(
624+
expiryDb, termPb, svts_, vol, iborIndex, fixedLegTenor, fixedDayCounter, floatDayCounter,
625+
calibrationDiscountCurve_, calibrationErrorType_, strikeValue, shift, settlementDays, averagingMethod);
621626
}
622627
if (!expiryDateBased && termDateBased) {
623628
Date expiry = svts_->optionDateFromTenor(expiryPb);
624629
Real shift = svts_->volatilityType() == ShiftedLognormal ? svts_->shift(expiryPb, termT) : 0.0;
625-
std::tie(helper, updatedStrike) =
626-
createSwaptionHelper(expiry, termDb, svts_, vol, iborIndex, fixedLegTenor, fixedDayCounter,
627-
floatDayCounter, modelDiscountCurve_, calibrationErrorType_,
628-
strikeValue, shift, settlementDays, averagingMethod);
630+
std::tie(helper, updatedStrike) = createSwaptionHelper(
631+
expiry, termDb, svts_, vol, iborIndex, fixedLegTenor, fixedDayCounter, floatDayCounter,
632+
calibrationDiscountCurve_, calibrationErrorType_, strikeValue, shift, settlementDays, averagingMethod);
629633
}
630634
if (!expiryDateBased && !termDateBased) {
631635
Real shift = svts_->volatilityType() == ShiftedLognormal ? svts_->shift(expiryPb, termPb) : 0.0;
632-
std::tie(helper, updatedStrike) =
633-
createSwaptionHelper(expiryPb, termPb, svts_, vol, iborIndex, fixedLegTenor, fixedDayCounter,
634-
floatDayCounter, modelDiscountCurve_, calibrationErrorType_,
635-
strikeValue, shift, settlementDays, averagingMethod);
636+
std::tie(helper, updatedStrike) = createSwaptionHelper(
637+
expiryPb, termPb, svts_, vol, iborIndex, fixedLegTenor, fixedDayCounter, floatDayCounter,
638+
calibrationDiscountCurve_, calibrationErrorType_, strikeValue, shift, settlementDays, averagingMethod);
636639
}
637640

638641
// check if we want to keep the helper when a reference calibration grid is given
@@ -644,10 +647,10 @@ void LgmBuilder::buildSwaptionBasket() const {
644647
swaptionBasketVols_.push_back(volQuote);
645648
swaptionBasket_.push_back(helper);
646649
swaptionStrike_.push_back(updatedStrike);
647-
expiryTimes.push_back(modelDiscountCurve_->timeFromReference(expiryDate));
650+
expiryTimes.push_back(calibrationDiscountCurve_->timeFromReference(expiryDate));
648651
Date matDate = helper->underlyingSwap() ? helper->underlyingSwap()->maturityDate()
649652
: helper->underlyingOvernightIndexedSwap()->maturityDate();
650-
maturityTimes.push_back(modelDiscountCurve_->timeFromReference(matDate));
653+
maturityTimes.push_back(calibrationDiscountCurve_->timeFromReference(matDate));
651654
if (refCalDate != referenceCalibrationDates.end())
652655
lastRefCalDate = *refCalDate;
653656
}
@@ -669,7 +672,7 @@ void LgmBuilder::buildSwaptionBasket() const {
669672
for (Size j = 0; j < maturityTimes.size(); j++)
670673
swaptionMaturities_[j] = maturityTimes[j];
671674

672-
swaptionBasketRefDate_ = modelDiscountCurve_->referenceDate();
675+
swaptionBasketRefDate_ = calibrationDiscountCurve_->referenceDate();
673676
}
674677

675678
std::string LgmBuilder::getBasketDetails(LgmCalibrationInfo& info) const {
@@ -680,7 +683,7 @@ std::string LgmBuilder::getBasketDetails(LgmCalibrationInfo& info) const {
680683
info.swaptionData.clear();
681684
for (Size j = 0; j < swaptionBasket_.size(); ++j) {
682685
auto swp = boost::static_pointer_cast<SwaptionHelper>(swaptionBasket_[j])->swaption();
683-
auto sd = swaptionData(swp, modelDiscountCurve_, svts_);
686+
auto sd = swaptionData(swp, calibrationDiscountCurve_, svts_);
684687
log << std::right << std::setw(3) << j << std::setw(16) << sd.timeToExpiry << std::setw(16) << sd.swapLength
685688
<< std::setw(16) << sd.strike << std::setw(16) << sd.atmForward << std::setw(16) << sd.annuity
686689
<< std::setw(16) << sd.vega << std::setw(16) << std::setw(16) << sd.stdDev / std::sqrt(sd.timeToExpiry)

OREData/ored/model/lgmbuilder.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ class LgmBuilder : public QuantExt::ModelBuilder {
117117
Handle<QuantLib::SwaptionVolatilityStructure> svts_;
118118
Handle<SwapIndex> swapIndex_, shortSwapIndex_;
119119
RelinkableHandle<YieldTermStructure> modelDiscountCurve_;
120+
Handle<YieldTermStructure> calibrationDiscountCurve_;
120121

121122
// TODO: Move CalibrationErrorType, optimizer and end criteria parameters to data
122123
boost::shared_ptr<OptimizationMethod> optimizationMethod_;

0 commit comments

Comments
 (0)