19#include <ql/math/optimization/levenbergmarquardt.hpp>
20#include <ql/quotes/simplequote.hpp>
45 const std::string& configuration,
const std::string& referenceCalibrationGrid,
46 const bool dontCalibrate)
47 : market_(market), configuration_(configuration), data_(
data), referenceCalibrationGrid_(referenceCalibrationGrid),
48 dontCalibrate_(dontCalibrate) {
50 LOG(
"DkBuilder for " <<
data_->index());
52 if (!
data_->calibrationBaskets().empty()) {
53 QL_REQUIRE(
data_->calibrationBaskets().size() == 1,
"If there is a calibration basket, expect exactly 1.");
55 QL_REQUIRE(!cb.
empty(),
"If there is a calibration basket, expect it to be non-empty.");
76 alwaysForwardNotifications();
84 Array aTimes(volatility.
times().begin(), volatility.
times().end());
85 Array hTimes(reversion.
times().begin(), reversion.
times().end());
86 Array alpha(volatility.
values().begin(), volatility.
values().end());
87 Array h(reversion.
values().begin(), reversion.
values().end());
90 QL_REQUIRE(volatility.
times().size() == 0,
"empty alpha times expected");
91 QL_REQUIRE(volatility.
values().size() == 1,
"initial alpha array should have size 1");
94 if (volatility.
times().size() > 0) {
95 DLOG(
"overriding alpha time grid with option expiries");
99 alpha = Array(aTimes.size() + 1, volatility.
values()[0]);
101 QL_REQUIRE(alpha.size() == aTimes.size() + 1,
"alpha grids do not match");
104 QL_FAIL(
"Expected DK model data volatility parameter to be Constant or Piecewise.");
108 QL_REQUIRE(reversion.
values().size() == 1,
"reversion grid size 1 expected");
109 QL_REQUIRE(reversion.
times().size() == 0,
"empty reversion time grid expected");
112 if (reversion.
times().size() > 0) {
113 DLOG(
"overriding H time grid with option expiries");
117 h = Array(hTimes.size() + 1, reversion.
values()[0]);
119 QL_REQUIRE(h.size() == hTimes.size() + 1,
"H grids do not match");
122 QL_FAIL(
"Expected DK model data reversion parameter to be Constant or Piecewise.");
125 DLOG(
"before calibration: alpha times = " << aTimes <<
" values = " << alpha);
126 DLOG(
"before calibration: h times = " << hTimes <<
" values = " << h)
130 DLOG(
"INF parametrization: InfDkPiecewiseConstantHullWhiteAdaptor");
131 parametrization_ = QuantLib::ext::make_shared<InfDkPiecewiseConstantHullWhiteAdaptor>(
135 DLOG(
"INF parametrization for " <<
data_->index() <<
": InfDkPiecewiseConstant");
136 parametrization_ = QuantLib::ext::make_shared<InfDkPiecewiseConstantParametrization>(
140 parametrization_ = QuantLib::ext::make_shared<InfDkPiecewiseLinearParametrization>(
143 DLOG(
"INF parametrization for " <<
data_->index() <<
": InfDkPiecewiseLinear");
146 DLOG(
"alpha times size: " << aTimes.size());
147 DLOG(
"lambda times size: " << hTimes.size());
149 DLOG(
"Apply shift horizon and scale");
150 Time horizon =
data_->reversionTransformation().horizon();
151 Real scaling =
data_->reversionTransformation().scaling();
152 QL_REQUIRE(horizon >= 0.0,
"shift horizon must be non negative");
153 QL_REQUIRE(scaling > 0.0,
"scaling must be positive");
156 DLOG(
"Apply shift horizon " << horizon <<
" to the " <<
data_->index() <<
" DK model");
160 if (scaling != 1.0) {
161 DLOG(
"Apply scaling " << scaling <<
" to the " <<
data_->index() <<
" DK model");
177 return (
data_->volatility().calibrate() ||
data_->reversion().calibrate()) &&
196 Date today = Settings::instance().evaluationDate();
197 const auto& ci =
data_->calibrationBaskets()[0].instruments();
198 QL_REQUIRE(j < ci.size(),
"InfDkBuilder::optionMaturityDate(" << j <<
"): out of bounds, got " << ci.size()
199 <<
" calibration instruments");
200 auto cf = QuantLib::ext::dynamic_pointer_cast<CpiCapFloor>(ci.at(j));
201 QL_REQUIRE(cf,
"InfDkBuilder::optionMaturityDate("
202 << j <<
"): expected CpiCapFloor calibration instruments, could not cast");
204 QL_REQUIRE(res > today,
"expired calibration option expiry " << io::iso_date(res));
209 const auto& ci =
data_->calibrationBaskets()[0].instruments();
210 QL_REQUIRE(j < ci.size(),
"InfDkBuilder::optionMaturityDate(" << j <<
"): out of bounds, got " << ci.size()
211 <<
" calibration instruments");
212 auto cf = QuantLib::ext::dynamic_pointer_cast<CpiCapFloor>(ci.at(j));
214 "InfDkBuilder::optionStrike(" << j <<
"): expected CpiCapFloor calibration instruments, could not cast");
219 bool hasUpdated =
false;
223 QuantLib::ext::shared_ptr<QuantExt::CPICapFloorEngine> engine;
226 if (isLogNormalVol) {
227 engine = QuantLib::ext::make_shared<QuantExt::CPIBlackCapFloorEngine>(
rateCurve_,
infVol_);
229 engine = QuantLib::ext::make_shared<QuantExt::CPIBachelierCapFloorEngine>(
rateCurve_,
infVol_);
233 BusinessDayConvention bdc =
infVol_->businessDayConvention();
234 Date baseDate =
inflationIndex_->zeroInflationTermStructure()->baseDate();
236 Period lag =
infVol_->observationLag();
243 const auto& ci =
data_->calibrationBaskets()[0].instruments();
246 Size optionCounter = 0;
247 Date today = Settings::instance().evaluationDate();
248 for (Size j = 0; j < ci.size(); j++) {
253 auto cpiCapFloor = QuantLib::ext::dynamic_pointer_cast<CpiCapFloor>(ci[j]);
254 QL_REQUIRE(cpiCapFloor,
"Expected CpiCapFloor calibration instruments in DK inflation model data.");
259 Option::Type capfloor = cpiCapFloor->type() == CapFloor::Cap ? Option::Call : Option::Put;
261 QuantLib::ext::shared_ptr<CPICapFloor> h =
262 QuantLib::ext::make_shared<CPICapFloor>(capfloor, nominal, today, baseCPI, expiryDate, fixCalendar, bdc,
264 h->setPricingEngine(engine);
265 Real price = h->NPV();
279 QL_REQUIRE(!
data_->calibrationBaskets().empty(),
"No calibration basket provided in inflation DK model data.");
283 QL_REQUIRE(ci.size() ==
optionActive_.size(),
"Expected the option active vector size to equal the "
284 <<
"number of calibration instruments");
288 Date lastRefCalDate = Date::minDate();
289 std::vector<Date> referenceCalibrationDates;
293 QuantLib::ext::shared_ptr<QuantExt::CPICapFloorEngine> engine;
296 if (isLogNormalVol) {
297 engine = QuantLib::ext::make_shared<QuantExt::CPIBlackCapFloorEngine>(
rateCurve_,
infVol_);
299 engine = QuantLib::ext::make_shared<QuantExt::CPIBachelierCapFloorEngine>(
rateCurve_,
infVol_);
303 Date baseDate =
inflationIndex_->zeroInflationTermStructure()->baseDate();
305 BusinessDayConvention bdc =
infVol_->businessDayConvention();
306 Period lag =
infVol_->observationLag();
308 Date startDate = Settings::instance().evaluationDate();
309 bool useInterpolatedCPIFixings =
infVol_->indexIsInterpolated();
311 vector<Time> expiryTimes;
313 for (Size j = 0; j < ci.size(); j++) {
315 auto cpiCapFloor = QuantLib::ext::dynamic_pointer_cast<CpiCapFloor>(ci[j]);
316 QL_REQUIRE(cpiCapFloor,
"Expected CpiCapFloor calibration instruments in DK inflation model data.");
322 std::lower_bound(referenceCalibrationDates.begin(), referenceCalibrationDates.end(), expiryDate);
323 if (refCalDate == referenceCalibrationDates.end() || *refCalDate > lastRefCalDate) {
325 Option::Type capfloor = cpiCapFloor->type() == CapFloor::Cap ? Option::Call : Option::Put;
326 QuantLib::ext::shared_ptr<CPICapFloor> cf =
327 QuantLib::ext::make_shared<CPICapFloor>(capfloor, nominal, startDate, baseCPI, expiryDate, fixCalendar, bdc,
329 cf->setPricingEngine(engine);
330 Real tte = inflationYearFraction(
inflationIndex_->frequency(), useInterpolatedCPIFixings,
334 Real tteFromBase =
infVol_->timeFromBase(expiryDate);
339 else if (tte <= 0 || tteFromBase <= 0)
342 marketPrem = cf->NPV();
344 QuantLib::ext::shared_ptr<QuantExt::CpiCapFloorHelper>
helper =
345 QuantLib::ext::make_shared<QuantExt::CpiCapFloorHelper>(capfloor, baseCPI, expiryDate, fixCalendar, bdc,
346 fixCalendar, bdc, strikeValue, hIndex, lag,
350 if (marketPrem > 0.0 && tte > 0 && tteFromBase > 0 &&
351 std::find_if(expiryTimes.begin(), expiryTimes.end(),
352 [tte](Real x) { return QuantLib::close_enough(x, tte); }) == expiryTimes.end()) {
354 helper->performCalculations();
355 expiryTimes.push_back(tte);
356 DLOG(
"Added InflationOptionHelper index="
357 <<
data_->index() <<
", type=" << (capfloor == Option::Type::Call ?
"Cap" :
"Floor") <<
", expiry="
358 << QuantLib::io::iso_date(expiryDate) <<
", baseCPI=" << baseCPI <<
", strike=" << strikeValue
359 <<
", lag=" << lag <<
", marketPremium=" << marketPrem <<
", tte=" << tte);
361 if (refCalDate != referenceCalibrationDates.end())
362 lastRefCalDate = *refCalDate;
364 if (
data_->ignoreDuplicateCalibrationExpiryTimes()) {
365 DLOG(
"Skipped InflationOptionHelper index="
366 <<
data_->index() <<
", type=" << (capfloor == Option::Type::Call ?
"Cap" :
"Floor")
367 <<
", expiry=" << QuantLib::io::iso_date(expiryDate) <<
", baseCPI=" << baseCPI
368 <<
", strike=" << strikeValue <<
", lag=" << lag <<
", marketPremium=" << marketPrem
369 <<
", tte=" << tte <<
" since we already have a helper with the same expiry time.");
371 QL_FAIL(
"InfDkBuilder: a CPI cap floor calibration instrument with the expiry time, "
372 << tte <<
", was already added.");
378 std::sort(expiryTimes.begin(), expiryTimes.end());
379 auto itExpiryTime = unique(expiryTimes.begin(), expiryTimes.end());
380 expiryTimes.resize(distance(expiryTimes.begin(), itExpiryTime));
383 for (Size j = 0; j < expiryTimes.size(); j++)
virtual void forceRecalculate()
bool empty() const
Returns true if the calibration basket is empty.
const std::vector< QuantLib::ext::shared_ptr< CalibrationInstrument > > & instruments() const
const std::vector< QuantLib::Date > & dates() const
QuantLib::ext::shared_ptr< QuantExt::InfDkParametrization > parametrization() const
QuantLib::ext::shared_ptr< QuantExt::InfDkParametrization > parametrization_
const std::string configuration_
void forceRecalculate() override
void performCalculations() const override
InfDkBuilder(const QuantLib::ext::shared_ptr< ore::data::Market > &market, const QuantLib::ext::shared_ptr< InfDkData > &data, const std::string &configuration=Market::defaultConfiguration, const std::string &referenceCalibrationGrid="", const bool dontCalibrate=false)
bool volSurfaceChanged(const bool updateCache) const
const QuantLib::ext::shared_ptr< InfDkData > data_
QuantLib::ext::shared_ptr< QuantExt::MarketObserver > marketObserver_
QuantLib::Array optionExpiries_
std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > optionBasket() const
std::vector< QuantLib::Real > infPriceCache_
std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > optionBasket_
void setCalibrationDone() const
bool requiresRecalibration() const override
QuantLib::Handle< QuantLib::CPIVolatilitySurface > infVol_
Handle< YieldTermStructure > rateCurve_
const QuantLib::ext::shared_ptr< ore::data::Market > market_
void buildCapFloorBasket() const
const std::string referenceCalibrationGrid_
std::vector< bool > optionActive_
Real optionStrikeValue(const Size j) const
QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > inflationIndex_
Date optionMaturityDate(const Size j) const
@ HullWhite
Parametrize volatility as HullWhite sigma(t)
const std::vector< QuantLib::Time > & times() const
const std::vector< QuantLib::Real > & values() const
LgmData::ReversionType reversionType() const
const boost::optional< LgmData::VolatilityType > & volatilityType() const
Builder for a Dodgson-Kainth inflation model component.
Classes and functions for log message handling.
#define LOG(text)
Logging Macro (Level = Notice)
#define DLOG(text)
Logging Macro (Level = Debug)
class for holding details of a zero coupon CPI cap floor calibration instrument.
Shared utilities for model building and calibration.
bool isCPIVolSurfaceLogNormal(const boost::shared_ptr< QuantLib::CPIVolatilitySurface > &surface)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Date optionMaturity(const boost::variant< Date, Period > &maturity, const QuantLib::Calendar &calendar, const QuantLib::Date &referenceDate)
Real cpiCapFloorStrikeValue(const QuantLib::ext::shared_ptr< BaseStrike > &strike, const QuantLib::ext::shared_ptr< ZeroInflationTermStructure > &curve, const QuantLib::Date &optionMaturityDate)
Return a cpi cap/floor strike value, the input strike can be of type absolute or atm forward.
Serializable Credit Default Swap.
Map text representations to QuantLib/QuantExt types.
QuantLib::BootstrapHelper< QuantLib::OptionletVolatilityStructure > helper