diff --git a/ql/indexes/inflationindex.cpp b/ql/indexes/inflationindex.cpp index 0c69c32be2a..123adc1194c 100644 --- a/ql/indexes/inflationindex.cpp +++ b/ql/indexes/inflationindex.cpp @@ -201,6 +201,11 @@ namespace QuantLib { Real ZeroInflationIndex::fixing(const Date& fixingDate, bool /*forecastTodaysFixing*/) const { + return fixing(fixingDate, false, true); + } + + Real ZeroInflationIndex::fixing(const Date& fixingDate, + bool /*forecastTodaysFixing*/, bool applySeasonality) const { if (!needsForecast(fixingDate)) { const Real I1 = pastFixing(fixingDate); QL_REQUIRE(I1 != Null(), @@ -209,7 +214,7 @@ namespace QuantLib { return I1; } else { - return forecastFixing(fixingDate); + return forecastFixing(fixingDate, applySeasonality); } } @@ -254,8 +259,7 @@ namespace QuantLib { } } - - Real ZeroInflationIndex::forecastFixing(const Date& fixingDate) const { + Real ZeroInflationIndex::forecastFixing(const Date& fixingDate, bool applySeasonality) const { // the term structure is relative to the fixing value at the base date. Date baseDate = zeroInflation_->baseDate(); QL_REQUIRE(!needsForecast(baseDate), @@ -265,7 +269,7 @@ namespace QuantLib { std::pair fixingPeriod = inflationPeriod(fixingDate, frequency_); Date firstDateInPeriod = fixingPeriod.first; - Rate Z1 = zeroInflation_->zeroRate(firstDateInPeriod, Period(0,Days), false); + Rate Z1 = zeroInflation_->zeroRate(firstDateInPeriod, Period(0,Days), false, false, applySeasonality); Time t1 = inflationYearFraction(frequency_, false, zeroInflation_->dayCounter(), baseDate, firstDateInPeriod); return baseFixing * std::pow(1.0 + Z1, t1); diff --git a/ql/indexes/inflationindex.hpp b/ql/indexes/inflationindex.hpp index 148ce6b01fc..c6dd549bc9f 100644 --- a/ql/indexes/inflationindex.hpp +++ b/ql/indexes/inflationindex.hpp @@ -167,7 +167,10 @@ namespace QuantLib { /*! \warning the forecastTodaysFixing parameter (required by the Index interface) is currently ignored. */ - Real fixing(const Date& fixingDate, bool forecastTodaysFixing = false) const override; + Real fixing(const Date& fixingDate, bool forecastTodaysFixing = false) const override; // required + Real fixing(const Date& fixingDate, + bool forecastTodaysFixing, + bool applySeasonality) const; // overload Real pastFixing(const Date& fixingDate) const override; //@} //! \name Other methods @@ -178,7 +181,7 @@ namespace QuantLib { bool needsForecast(const Date& fixingDate) const; //@} private: - Real forecastFixing(const Date& fixingDate) const; + Real forecastFixing(const Date& fixingDate, bool applySeasonality = true) const; Handle zeroInflation_; }; diff --git a/ql/instruments/zerocouponinflationswap.cpp b/ql/instruments/zerocouponinflationswap.cpp index 31bebd2b7f3..3c6ed60cdd8 100644 --- a/ql/instruments/zerocouponinflationswap.cpp +++ b/ql/instruments/zerocouponinflationswap.cpp @@ -93,6 +93,8 @@ namespace QuantLib { inflationYearFraction(infIndex_->frequency(), detail::CPI::isInterpolated(observationInterpolation_), dayCounter_, baseDate_, obsDate_); + // Mimic yearly compounding 1/1 DC with Year (which can return zero). + T = T == 0 ? 1 : T; // N.B. the -1.0 is because swaps only exchange growth, not notionals as well Real fixedAmount = nominal * (std::pow(1.0 + fixedRate, T) - 1.0); @@ -134,7 +136,8 @@ namespace QuantLib { inflationYearFraction(infIndex_->frequency(), detail::CPI::isInterpolated(observationInterpolation_), dayCounter_, baseDate_, obsDate_); - + // Year DC can return zero which is not wanted + T = T == 0 ? 1 : T; return std::pow(growth,1.0/T) - 1.0; // we cannot use this simple definition because diff --git a/ql/termstructures/inflation/seasonality.cpp b/ql/termstructures/inflation/seasonality.cpp index d9865907a2e..ffaec30c1c7 100644 --- a/ql/termstructures/inflation/seasonality.cpp +++ b/ql/termstructures/inflation/seasonality.cpp @@ -248,8 +248,8 @@ namespace QuantLib { Time timeFromCurveBase = dc.yearFraction(curveBaseDate, p.first); f = std::pow(seasonalityAt, 1 / timeFromCurveBase); } else { - Rate factor1Ybefore = this->seasonalityFactor(atDate - Period(1, Years)); - f = factorAt / factor1Ybefore; + Rate factorBefore = this->seasonalityFactor(curveBaseDate); + f = factorAt / factorBefore; } return (rate + 1) / f - 1; diff --git a/ql/termstructures/inflationtermstructure.cpp b/ql/termstructures/inflationtermstructure.cpp index 649738cd51d..194f3fd0654 100644 --- a/ql/termstructures/inflationtermstructure.cpp +++ b/ql/termstructures/inflationtermstructure.cpp @@ -228,7 +228,8 @@ namespace QuantLib { Rate ZeroInflationTermStructure::zeroRate(const Date &d, const Period& instObsLag, bool forceLinearInterpolation, - bool extrapolate) const { + bool extrapolate, + bool applySeasonality) const { Period useLag = instObsLag; if (instObsLag == Period(-1,Days)) { @@ -256,7 +257,7 @@ namespace QuantLib { zeroRate = zeroRateImpl(t); } - if (hasSeasonality()) { + if (hasSeasonality() && applySeasonality) { zeroRate = seasonality()->correctZeroRate(d-useLag, zeroRate, *this); } return zeroRate; diff --git a/ql/termstructures/inflationtermstructure.hpp b/ql/termstructures/inflationtermstructure.hpp index 3f91712f465..42de8978a2d 100644 --- a/ql/termstructures/inflationtermstructure.hpp +++ b/ql/termstructures/inflationtermstructure.hpp @@ -242,7 +242,8 @@ namespace QuantLib { */ Rate zeroRate(const Date& d, const Period& instObsLag = Period(-1,Days), bool forceLinearInterpolation = false, - bool extrapolate = false) const; + bool extrapolate = false, + bool applySeasonality = true) const; //! zero-coupon inflation rate. /*! \warning Since inflation is highly linked to dates (lags, interpolation, months for seasonality, etc) this