From 8cf851830f2a724c6d14583e86f0bff338f51f80 Mon Sep 17 00:00:00 2001 From: Luigi Ballabio Date: Fri, 20 Sep 2024 10:06:32 +0200 Subject: [PATCH] More interpolation passed around --- .../inflation/yoycapfloortermpricesurface.cpp | 21 ++++- .../inflation/yoycapfloortermpricesurface.hpp | 76 +++++++++++++++++-- ql/indexes/inflationindex.cpp | 19 +++++ ql/indexes/inflationindex.hpp | 28 ++++--- .../inflation/inflationhelpers.cpp | 68 +++++++++-------- .../inflation/inflationhelpers.hpp | 15 ++++ test-suite/inflation.cpp | 4 +- test-suite/inflationcapfloor.cpp | 23 +++--- test-suite/inflationcapflooredcoupon.cpp | 35 ++++----- test-suite/inflationvolatility.cpp | 3 +- 10 files changed, 210 insertions(+), 82 deletions(-) diff --git a/ql/experimental/inflation/yoycapfloortermpricesurface.cpp b/ql/experimental/inflation/yoycapfloortermpricesurface.cpp index d9364a5627e..47f36437bb8 100644 --- a/ql/experimental/inflation/yoycapfloortermpricesurface.cpp +++ b/ql/experimental/inflation/yoycapfloortermpricesurface.cpp @@ -26,7 +26,7 @@ namespace QuantLib { Natural fixingDays, const Period& lag, const ext::shared_ptr& yii, - Rate baseRate, + CPI::InterpolationType interpolation, Handle nominal, const DayCounter& dc, const Calendar& cal, @@ -39,7 +39,7 @@ namespace QuantLib { : TermStructure(0, cal, dc), fixingDays_(fixingDays), bdc_(bdc), yoyIndex_(yii), observationLag_(lag), nominalTS_(std::move(nominal)), cStrikes_(cStrikes), fStrikes_(fStrikes), cfMaturities_(cfMaturities), cPrice_(cPrice), - fPrice_(fPrice), indexIsInterpolated_(yii->interpolated()) { + fPrice_(fPrice), indexIsInterpolated_(detail::CPI::isInterpolated(interpolation, yoyIndex_)) { // data consistency checking, enough data? QL_REQUIRE(fStrikes_.size() > 1, "not enough floor strikes"); @@ -100,6 +100,23 @@ namespace QuantLib { "cfStrikes not increasing"); } + YoYCapFloorTermPriceSurface::YoYCapFloorTermPriceSurface( + Natural fixingDays, + const Period& yyLag, + const ext::shared_ptr& yii, + Rate baseRate, + Handle nominal, + const DayCounter& dc, + const Calendar& cal, + const BusinessDayConvention& bdc, + const std::vector& cStrikes, + const std::vector& fStrikes, + const std::vector& cfMaturities, + const Matrix& cPrice, + const Matrix& fPrice) + : YoYCapFloorTermPriceSurface(fixingDays, yyLag, yii, CPI::AsIndex, nominal, dc, cal, bdc, + cStrikes, fStrikes, cfMaturities, cPrice, fPrice) {} + Date YoYCapFloorTermPriceSurface::yoyOptionDateFromTenor(const Period& p) const { return referenceDate() + p; diff --git a/ql/experimental/inflation/yoycapfloortermpricesurface.hpp b/ql/experimental/inflation/yoycapfloortermpricesurface.hpp index 8b749cc51ab..92aa0a5f66c 100644 --- a/ql/experimental/inflation/yoycapfloortermpricesurface.hpp +++ b/ql/experimental/inflation/yoycapfloortermpricesurface.hpp @@ -41,6 +41,24 @@ namespace QuantLib { */ class YoYCapFloorTermPriceSurface : public TermStructure { public: + YoYCapFloorTermPriceSurface(Natural fixingDays, + const Period& yyLag, + const ext::shared_ptr& yii, + CPI::InterpolationType interpolation, + Handle nominal, + const DayCounter& dc, + const Calendar& cal, + const BusinessDayConvention& bdc, + const std::vector& cStrikes, + const std::vector& fStrikes, + const std::vector& cfMaturities, + const Matrix& cPrice, + const Matrix& fPrice); + + /*! \deprecated Use the overload that passes an interpolation type instead. + Deprecated in version 1.36. + */ + [[deprecated("Use the overload that passes an interpolation type instead")]] YoYCapFloorTermPriceSurface(Natural fixingDays, const Period& yyLag, const ext::shared_ptr& yii, @@ -149,6 +167,27 @@ namespace QuantLib { class InterpolatedYoYCapFloorTermPriceSurface : public YoYCapFloorTermPriceSurface { public: + InterpolatedYoYCapFloorTermPriceSurface( + Natural fixingDays, + const Period &yyLag, // observation lag + const ext::shared_ptr& yii, + CPI::InterpolationType interpolation, + const Handle &nominal, + const DayCounter &dc, + const Calendar &cal, + const BusinessDayConvention &bdc, + const std::vector &cStrikes, + const std::vector &fStrikes, + const std::vector &cfMaturities, + const Matrix &cPrice, + const Matrix &fPrice, + const Interpolator2D &interpolator2d = Interpolator2D(), + const Interpolator1D &interpolator1d = Interpolator1D()); + + /*! \deprecated Use the overload that passes an interpolation type instead. + Deprecated in version 1.36. + */ + [[deprecated("Use the overload that passes an interpolation type instead")]] InterpolatedYoYCapFloorTermPriceSurface( Natural fixingDays, const Period &yyLag, // observation lag @@ -257,7 +296,7 @@ namespace QuantLib { Natural fixingDays, const Period &yyLag, const ext::shared_ptr& yii, - Rate baseRate, + CPI::InterpolationType interpolation, const Handle &nominal, const DayCounter &dc, const Calendar &cal, @@ -270,13 +309,37 @@ namespace QuantLib { const I2D &interpolator2d, const I1D &interpolator1d) : YoYCapFloorTermPriceSurface(fixingDays, yyLag, yii, - baseRate, nominal, dc, cal, bdc, + interpolation, nominal, dc, cal, bdc, cStrikes, fStrikes, cfMaturities, cPrice, fPrice), interpolator2d_(interpolator2d), interpolator1d_(interpolator1d) { performCalculations(); } + template + InterpolatedYoYCapFloorTermPriceSurface:: + InterpolatedYoYCapFloorTermPriceSurface( + Natural fixingDays, + const Period &yyLag, + const ext::shared_ptr& yii, + Rate baseRate, + const Handle &nominal, + const DayCounter &dc, + const Calendar &cal, + const BusinessDayConvention &bdc, + const std::vector &cStrikes, + const std::vector &fStrikes, + const std::vector &cfMaturities, + const Matrix &cPrice, + const Matrix &fPrice, + const I2D &interpolator2d, + const I1D &interpolator1d) + : InterpolatedYoYCapFloorTermPriceSurface(fixingDays, yyLag, yii, CPI::AsIndex, + nominal, dc, cal, bdc, + cStrikes, fStrikes, cfMaturities, + cPrice, fPrice, + interpolator2d, interpolator1d) {} + #endif template @@ -532,12 +595,13 @@ namespace QuantLib { Date maturity = nominalTS_->referenceDate() + Period(i,Years); Handle quote(ext::shared_ptr( new SimpleQuote( atmYoYSwapRate( maturity ) )));//! - ext::shared_ptr > - anInstrument( - new YearOnYearInflationSwapHelper( + auto anInstrument = + ext::make_shared( quote, observationLag(), maturity, calendar(), bdc_, dayCounter(), - yoyIndex(), nominalTS_)); + yoyIndex(), + this->indexIsInterpolated() ? CPI::Linear: CPI::Flat, + nominalTS_); YYhelpers.push_back (anInstrument); } diff --git a/ql/indexes/inflationindex.cpp b/ql/indexes/inflationindex.cpp index a71417cd883..6f54bd5b6fc 100644 --- a/ql/indexes/inflationindex.cpp +++ b/ql/indexes/inflationindex.cpp @@ -377,4 +377,23 @@ namespace QuantLib { } } + CPI::InterpolationType + detail::CPI::effectiveInterpolationType(const QuantLib::CPI::InterpolationType& type, + const ext::shared_ptr& index) { + if (type == QuantLib::CPI::AsIndex) { + return index->interpolated() ? QuantLib::CPI::Linear : QuantLib::CPI::Flat; + } else { + return type; + } + } + + bool detail::CPI::isInterpolated(const QuantLib::CPI::InterpolationType& type) { + return detail::CPI::effectiveInterpolationType(type) == QuantLib::CPI::Linear; + } + + bool detail::CPI::isInterpolated(const QuantLib::CPI::InterpolationType& type, + const ext::shared_ptr& index) { + return detail::CPI::effectiveInterpolationType(type, index) == QuantLib::CPI::Linear; + } + } diff --git a/ql/indexes/inflationindex.hpp b/ql/indexes/inflationindex.hpp index 13316a47f06..114e8274f5a 100644 --- a/ql/indexes/inflationindex.hpp +++ b/ql/indexes/inflationindex.hpp @@ -249,16 +249,24 @@ namespace QuantLib { namespace detail::CPI { - // Returns either CPI::Flat or CPI::Linear depending on the combination of index and - // CPI::InterpolationType. - QuantLib::CPI::InterpolationType effectiveInterpolationType( - const QuantLib::CPI::InterpolationType& type = QuantLib::CPI::AsIndex); + // Returns either CPI::Flat or CPI::Linear depending on the combination of index and + // CPI::InterpolationType. + QuantLib::CPI::InterpolationType + effectiveInterpolationType(const QuantLib::CPI::InterpolationType& type); - // checks whether the combination of index and CPI::InterpolationType results - // effectively in CPI::Linear - bool isInterpolated(const QuantLib::CPI::InterpolationType& type = QuantLib::CPI::AsIndex); - } + QuantLib::CPI::InterpolationType + effectiveInterpolationType(const QuantLib::CPI::InterpolationType& type, + const ext::shared_ptr& index); + + // checks whether the combination of index and CPI::InterpolationType results + // effectively in CPI::Linear + bool isInterpolated(const QuantLib::CPI::InterpolationType& type); + + bool isInterpolated(const QuantLib::CPI::InterpolationType& type, + const ext::shared_ptr& index); + + } // inline @@ -313,10 +321,6 @@ namespace QuantLib { return yoyInflation_; } - inline bool detail::CPI::isInterpolated(const QuantLib::CPI::InterpolationType& type) { - return detail::CPI::effectiveInterpolationType(type) == QuantLib::CPI::Linear; - } - } #endif diff --git a/ql/termstructures/inflation/inflationhelpers.cpp b/ql/termstructures/inflation/inflationhelpers.cpp index 7ea04500768..2bc16d494a5 100644 --- a/ql/termstructures/inflation/inflationhelpers.cpp +++ b/ql/termstructures/inflation/inflationhelpers.cpp @@ -44,24 +44,23 @@ namespace QuantLib { observationInterpolation_(observationInterpolation), nominalTermStructure_(std::move(nominalTermStructure)) { - std::pair limStart = inflationPeriod(maturity_ - swapObsLag_, zii_->frequency()); - std::pair interpolationPeriod = inflationPeriod(maturity, zii_->frequency()); + auto fixingPeriod = inflationPeriod(maturity_ - swapObsLag_, zii_->frequency()); + auto interpolationPeriod = inflationPeriod(maturity, zii_->frequency()); - if ((detail::CPI::effectiveInterpolationType(observationInterpolation_) == CPI::Linear) && - (maturity > interpolationPeriod.first)) { + if (detail::CPI::isInterpolated(observationInterpolation_) && maturity > interpolationPeriod.first) { // if interpolated, we need to cover the end of the interpolation period - earliestDate_ = limStart.first; - latestDate_ = limStart.second + 1; + earliestDate_ = fixingPeriod.first; + latestDate_ = fixingPeriod.second + 1; } else { // if not interpolated, the date of the initial fixing is enough - earliestDate_ = limStart.first; - latestDate_ = limStart.first; + earliestDate_ = fixingPeriod.first; + latestDate_ = fixingPeriod.first; } // check that the observation lag of the swap // is compatible with the availability lag of the index AND // it's interpolation (assuming the start day is spot) - if (detail::CPI::effectiveInterpolationType(observationInterpolation_) == CPI::Linear) { + if (detail::CPI::isInterpolated(observationInterpolation_)) { Period pShift(zii_->frequency()); QL_REQUIRE(swapObsLag_ - pShift >= zii_->availabilityLag(), "inconsistency between swap observation lag " @@ -117,43 +116,53 @@ namespace QuantLib { BusinessDayConvention paymentConvention, DayCounter dayCounter, ext::shared_ptr yii, + CPI::InterpolationType interpolation, Handle nominalTermStructure) : BootstrapHelper(quote), swapObsLag_(swapObsLag), maturity_(maturity), calendar_(std::move(calendar)), paymentConvention_(paymentConvention), - dayCounter_(std::move(dayCounter)), yii_(std::move(yii)), + dayCounter_(std::move(dayCounter)), yii_(std::move(yii)), interpolation_(interpolation), nominalTermStructure_(std::move(nominalTermStructure)) { - if (yii_->interpolated()) { - // if interpolated then simple - earliestDate_ = maturity_ - swapObsLag_; - latestDate_ = maturity_ - swapObsLag_; + auto fixingPeriod = inflationPeriod(maturity_ - swapObsLag_, yii_->frequency()); + auto interpolationPeriod = inflationPeriod(maturity, yii_->frequency()); + + if (detail::CPI::isInterpolated(interpolation_, yii_) && maturity > interpolationPeriod.first) { + // if interpolated, we need to cover the end of the interpolation period + earliestDate_ = fixingPeriod.first; + latestDate_ = fixingPeriod.second + 1; } else { - // but if NOT interpolated then the value is valid - // for every day in an inflation period so you actually - // get an extended validity, however for curve building - // just put the first date because using that convention - // for the base date throughout - std::pair limStart = - inflationPeriod(maturity_ - swapObsLag_, yii_->frequency()); - earliestDate_ = limStart.first; - latestDate_ = limStart.first; + // if not interpolated, the date of the initial fixing is enough + earliestDate_ = fixingPeriod.first; + latestDate_ = fixingPeriod.first; } // check that the observation lag of the swap // is compatible with the availability lag of the index AND - // it's interpolation (assuming the start day is spot) - if (yii_->interpolated()) { + // its interpolation (assuming the start day is spot) + if (detail::CPI::isInterpolated(interpolation_, yii_)) { Period pShift(yii_->frequency()); QL_REQUIRE(swapObsLag_ - pShift >= yii_->availabilityLag(), "inconsistency between swap observation lag " - << swapObsLag_ << ", index period " << pShift << " and index availability " - << yii_->availabilityLag() << ": need (obsLag-index period) >= availLag"); + << swapObsLag_ << ", index period " << pShift << " and index availability " + << yii_->availabilityLag() << ": need (obsLag-index period) >= availLag"); } registerWith(Settings::instance().evaluationDate()); registerWith(nominalTermStructure_); } + YearOnYearInflationSwapHelper::YearOnYearInflationSwapHelper( + const Handle& quote, + const Period& swapObsLag, + const Date& maturity, + Calendar calendar, + BusinessDayConvention paymentConvention, + DayCounter dayCounter, + ext::shared_ptr yii, + Handle nominalTermStructure) + : YearOnYearInflationSwapHelper(quote, swapObsLag, maturity, calendar, paymentConvention, + dayCounter, yii, CPI::AsIndex, nominalTermStructure) {} + Real YearOnYearInflationSwapHelper::impliedQuote() const { yyiis_->deepUpdate(); @@ -194,9 +203,8 @@ namespace QuantLib { Real nominal = 1000000.0; // has to be something but doesn't matter what yyiis_ = ext::make_shared( Swap::Payer, nominal, fixedSchedule, fixedRate, dayCounter_, - yoySchedule, new_yii, swapObsLag_, spread, dayCounter_, - calendar_, // inflation index does not have a calendar - paymentConvention_); + yoySchedule, new_yii, swapObsLag_, interpolation_, + spread, dayCounter_, calendar_, paymentConvention_); // The instrument takes a standard discounting swap engine. // The inflation-related work is done by the coupons. diff --git a/ql/termstructures/inflation/inflationhelpers.hpp b/ql/termstructures/inflation/inflationhelpers.hpp index aa30c86e82e..7797a4f82a8 100644 --- a/ql/termstructures/inflation/inflationhelpers.hpp +++ b/ql/termstructures/inflation/inflationhelpers.hpp @@ -65,6 +65,20 @@ namespace QuantLib { //! Year-on-year inflation-swap bootstrap helper class YearOnYearInflationSwapHelper : public BootstrapHelper { public: + YearOnYearInflationSwapHelper(const Handle& quote, + const Period& swapObsLag_, + const Date& maturity, + Calendar calendar, + BusinessDayConvention paymentConvention, + DayCounter dayCounter, + ext::shared_ptr yii, + CPI::InterpolationType interpolation, + Handle nominalTermStructure); + + /*! \deprecated Use the overload that passes an interpolation type instead. + Deprecated in version 1.36. + */ + [[deprecated("Use the overload that passes an interpolation type instead")]] YearOnYearInflationSwapHelper(const Handle& quote, const Period& swapObsLag_, const Date& maturity, @@ -84,6 +98,7 @@ namespace QuantLib { BusinessDayConvention paymentConvention_; DayCounter dayCounter_; ext::shared_ptr yii_; + CPI::InterpolationType interpolation_; ext::shared_ptr yyiis_; Handle nominalTermStructure_; }; diff --git a/test-suite/inflation.cpp b/test-suite/inflation.cpp index 42e760adb8d..4401bde2222 100644 --- a/test-suite/inflation.cpp +++ b/test-suite/inflation.cpp @@ -1121,7 +1121,7 @@ BOOST_AUTO_TEST_CASE(testYYTermStructure) { // now build the helpers ... auto makeHelper = [&](const Handle& quote, const Date& maturity) { return ext::make_shared( - quote, observationLag, maturity, calendar, bdc, dc, iir, + quote, observationLag, maturity, calendar, bdc, dc, iir, CPI::AsIndex, Handle(nominalTS)); }; auto helpers = makeHelpers(yyData, makeHelper); @@ -1278,7 +1278,7 @@ BOOST_AUTO_TEST_CASE(testYYTermStructureWithLag) { // now build the helpers ... auto makeHelper = [&](const Handle& quote, const Date& maturity) { return ext::make_shared( - quote, observationLag, maturity, calendar, bdc, dc, iir, + quote, observationLag, maturity, calendar, bdc, dc, iir, CPI::AsIndex, Handle(nominalTS)); }; auto helpers = makeHelpers(yyData, makeHelper); diff --git a/test-suite/inflationcapfloor.cpp b/test-suite/inflationcapfloor.cpp index 26f877392a1..20434d196ff 100644 --- a/test-suite/inflationcapfloor.cpp +++ b/test-suite/inflationcapfloor.cpp @@ -61,23 +61,24 @@ struct Datum { Rate rate; }; -template -std::vector > > makeHelpers( +std::vector > > makeHelpers( const std::vector& iiData, - const ext::shared_ptr &ii, const Period &observationLag, + const ext::shared_ptr &ii, + CPI::InterpolationType interpolation, + const Period &observationLag, const Calendar &calendar, const BusinessDayConvention &bdc, const DayCounter &dc, const Handle& discountCurve) { - std::vector > > instruments; + std::vector > > instruments; for (Datum datum : iiData) { Date maturity = datum.date; Handle quote(ext::shared_ptr( new SimpleQuote(datum.rate/100.0))); - ext::shared_ptr > anInstrument(new U( + auto anInstrument = ext::make_shared( quote, observationLag, maturity, - calendar, bdc, dc, ii, discountCurve)); + calendar, bdc, dc, ii, interpolation, discountCurve); instruments.push_back(anInstrument); } @@ -169,11 +170,11 @@ struct CommonVars { // now build the helpers ... std::vector > > helpers = - makeHelpers(yyData, iir, - observationLag, - calendar, convention, dc, - Handle(nominalTS)); + makeHelpers(yyData, iir, + CPI::Flat, + observationLag, + calendar, convention, dc, + Handle(nominalTS)); Date baseDate = rpi->lastFixingDate(); Rate baseYYRate = yyData[0].rate/100.0; diff --git a/test-suite/inflationcapflooredcoupon.cpp b/test-suite/inflationcapflooredcoupon.cpp index 14d185a8524..bcd0a742a21 100644 --- a/test-suite/inflationcapflooredcoupon.cpp +++ b/test-suite/inflationcapflooredcoupon.cpp @@ -62,23 +62,24 @@ struct Datum { Rate rate; }; -template -std::vector > > makeHelpers( - const std::vector& iiData, - const ext::shared_ptr &ii, const Period &observationLag, - const Calendar &calendar, - const BusinessDayConvention &bdc, - const DayCounter &dc, - const Handle& discountCurve) { - - std::vector > > instruments; +std::vector > > +makeHelpers(const std::vector& iiData, + const ext::shared_ptr &ii, + const Period &observationLag, + CPI::InterpolationType interpolation, + const Calendar &calendar, + const BusinessDayConvention &bdc, + const DayCounter &dc, + const Handle& discountCurve) { + + std::vector > > instruments; for (Datum datum : iiData) { Date maturity = datum.date; Handle quote(ext::shared_ptr( new SimpleQuote(datum.rate/100.0))); - ext::shared_ptr > anInstrument(new U( + auto anInstrument = ext::make_shared( quote, observationLag, maturity, - calendar, bdc, dc, ii, discountCurve)); + calendar, bdc, dc, ii, interpolation, discountCurve); instruments.push_back(anInstrument); } @@ -177,11 +178,11 @@ struct CommonVars { // now build the helpers ... std::vector > > helpers = - makeHelpers(yyData, iir, - observationLag, - calendar, convention, dc, - Handle(nominalTS)); + makeHelpers(yyData, iir, + observationLag, + CPI::Flat, + calendar, convention, dc, + Handle(nominalTS)); Date baseDate = rpi->lastFixingDate(); Rate baseYYRate = yyData[0].rate/100.0; diff --git a/test-suite/inflationvolatility.cpp b/test-suite/inflationvolatility.cpp index 2d1b2480b49..52f07f2589a 100644 --- a/test-suite/inflationvolatility.cpp +++ b/test-suite/inflationvolatility.cpp @@ -247,7 +247,6 @@ void setupPriceSurface() { Natural fixingDays = 0; Size lag = 3;// must be 3 because we use an interpolated index (EU) Period yyLag = Period(lag,Months); - Rate baseRate = 1; // not really used DayCounter dc = Actual365Fixed(); TARGET cal; BusinessDayConvention bdc = ModifiedFollowing; @@ -256,7 +255,7 @@ void setupPriceSurface() { ext::shared_ptr > cfEUprices(new InterpolatedYoYCapFloorTermPriceSurface( fixingDays, - yyLag, yoyIndexEU, baseRate, + yyLag, yoyIndexEU, CPI::Linear, n, dc, cal, bdc, cStrikesEU, fStrikesEU, cfMaturitiesEU,