19#include <ql/cashflows/cpicoupon.hpp>
20#include <ql/math/solvers1d/brent.hpp>
22#include <ql/termstructures/inflation/interpolatedzeroinflationcurve.hpp>
26using QuantLib::DayCounter;
28using QuantLib::Frequency;
29using QuantLib::Handle;
30using QuantLib::InflationIndex;
31using QuantLib::InflationTermStructure;
32using QuantLib::inflationYearFraction;
33using QuantLib::Period;
37using QuantLib::ZeroInflationTermStructure;
43void throwExceptionIfHistoricalFixingMissing(
const Date& fixingDate,
const QuantLib::ZeroInflationIndex& index) {
44 QL_REQUIRE(index.hasHistoricalFixing(fixingDate),
45 "Historical fixing missing for index " << index.name() <<
" on " << fixingDate);
49void checkIfFixingAvailable(
const Date& maturity,
const Period obsLag,
bool interpolated,
50 const QuantLib::ZeroInflationIndex& index) {
52 auto fixingPeriod = inflationPeriod(maturity - obsLag, index.frequency());
53 throwExceptionIfHistoricalFixingMissing(fixingPeriod.first, index);
55 throwExceptionIfHistoricalFixingMissing(fixingPeriod.second + 1 * Days, index);
62 const QuantLib::ext::shared_ptr<InflationTermStructure>& inflationTs,
63 bool indexIsInterpolated,
64 const DayCounter& dayCounter) {
66 DayCounter dc = inflationTs->dayCounter();
67 if (dayCounter != DayCounter())
70 return inflationYearFraction(inflationTs->frequency(), indexIsInterpolated,
71 dc, inflationTs->baseDate(), date);
74Real
inflationGrowth(
const Handle<ZeroInflationTermStructure>& ts, Time t,
const DayCounter& dc,
bool indexIsInterpolated) {
75 auto lag =
inflationTime(ts->referenceDate(), *ts, indexIsInterpolated, dc);
76 return pow(1.0 + ts->zeroRate(t - lag), t);
79Real
inflationGrowth(
const Handle<ZeroInflationTermStructure>& ts, Time t,
bool indexIsInterpolated) {
84 QuantLib::Real inflFactor = 1;
85 for (
auto& cf : bond->cashflows()) {
86 if (
auto inflCpn = QuantLib::ext::dynamic_pointer_cast<QuantLib::CPICoupon>(cf)) {
87 const auto& inflationIndex = QuantLib::ext::dynamic_pointer_cast<QuantLib::ZeroInflationIndex>(inflCpn->index());
88 Date settlementDate = bond->settlementDate();
89 std::pair<Date, Date> currentInflationPeriod = inflationPeriod(settlementDate, inflationIndex->frequency());
90 std::pair<Date, Date> settlementFixingPeriod = inflationPeriod(settlementDate - inflCpn->observationLag(), inflationIndex->frequency());
91 Date curveBaseDate = settlementFixingPeriod.first;
93 Real todaysCPI = inflationIndex->fixing(curveBaseDate);
94 if (inflCpn->observationInterpolation() == QuantLib::CPI::Linear) {
96 std::pair<Date, Date> observationPeriod = inflationPeriod(curveBaseDate, inflationIndex->frequency());
98 Real indexStart = inflationIndex->fixing(observationPeriod.first);
99 Real indexEnd = inflationIndex->fixing(observationPeriod.second + 1 * QuantLib::Days);
101 todaysCPI = indexStart + (settlementDate - currentInflationPeriod.first) *
102 (indexEnd - indexStart) /
103 (Real)(currentInflationPeriod.second - currentInflationPeriod.first);
105 QuantLib::Rate baseCPI = inflCpn->baseCPI();
106 if (baseCPI == QuantLib::Null<QuantLib::Rate>()) {
108 QuantLib::CPI::laggedFixing(inflCpn->cpiIndex(), inflCpn->baseDate() + inflCpn->observationLag(),
109 inflCpn->observationLag(),
110 inflCpn->observationInterpolation());
112 inflFactor = todaysCPI / baseCPI;
120 std::map<std::tuple<std::string, QuantLib::CPI::InterpolationType, QuantLib::Frequency, QuantLib::Period>,
121 QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>>& inflationIndices,
122 const QuantLib::ext::shared_ptr<QuantLib::Index>& index, QuantLib::CPI::InterpolationType interpolation,
123 Frequency couponFrequency, Period observationLag) {
124 if (index !=
nullptr) {
125 const auto zInfIndex = QuantLib::ext::dynamic_pointer_cast<QuantLib::ZeroInflationIndex>(index);
126 std::string name = index->name();
127 const auto key = std::make_tuple(name, interpolation, couponFrequency, observationLag);
128 if (zInfIndex !=
nullptr && inflationIndices.count(key) == 0) {
129 inflationIndices[key] = zInfIndex;
134std::map<std::tuple<std::string, QuantLib::CPI::InterpolationType, QuantLib::Frequency, QuantLib::Period>,
135 QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>>
138 std::map<std::tuple<std::string, QuantLib::CPI::InterpolationType, Frequency, Period>,
139 QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>>
141 if (bond !=
nullptr) {
142 for (
const auto& cf : bond->cashflows()) {
143 if (
auto cp = QuantLib::ext::dynamic_pointer_cast<QuantLib::CPICoupon>(cf)) {
145 cp->index()->frequency(), cp->observationLag());
146 }
else if (
auto cp = QuantLib::ext::dynamic_pointer_cast<QuantLib::CPICashFlow>(cf)) {
148 cp->observationLag());
152 return inflationIndices;
154namespace ZeroInflation {
156QuantLib::Date
lastAvailableFixing(
const QuantLib::ZeroInflationIndex& index,
const QuantLib::Date& asof) {
157 Date availbilityLagFixingDate = inflationPeriod(asof - index.availabilityLag(), index.frequency()).first;
158 if (index.hasHistoricalFixing(availbilityLagFixingDate)) {
159 return availbilityLagFixingDate;
162 return inflationPeriod(availbilityLagFixingDate - 1 * QuantLib::Days, index.frequency()).first;
166QuantLib::Rate
cpiFixing(
const QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>& index,
const QuantLib::Date& maturity,
167 const QuantLib::Period& obsLag,
bool interpolated) {
168 QuantLib::CPI::InterpolationType interpolation = interpolated ? QuantLib::CPI::Linear : QuantLib::CPI::Flat;
169 return QuantLib::CPI::laggedFixing(index, maturity, obsLag, interpolation);
172QuantLib::Date
curveBaseDate(
const bool baseDateLastKnownFixing,
const QuantLib::Date& refDate,
173 const QuantLib::Period obsLagCurve,
const QuantLib::Frequency curveFreq,
174 const QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>& index) {
175 if (baseDateLastKnownFixing) {
176 QL_REQUIRE(index,
"can not compute curve base date based on the last known index fixing if no index provided");
179 return QuantLib::inflationPeriod(refDate - obsLagCurve, curveFreq).first;
183QuantLib::Date
fixingDate(
const QuantLib::Date& d,
const QuantLib::Period obsLag,
const QuantLib::Frequency freq,
185 Date obsDate = d - obsLag;
187 obsDate = QuantLib::inflationPeriod(obsDate, freq).first;
191QuantLib::Rate
guessCurveBaseRate(
const bool baseDateLastKnownFixing,
const QuantLib::Date& swapStart,
192 const QuantLib::Date& asof,
193 const QuantLib::Period& swapTenor,
const QuantLib::DayCounter& swapZCLegDayCounter,
194 const QuantLib::Period& swapObsLag,
const QuantLib::Rate zeroCouponRate,
195 const QuantLib::Period& curveObsLag,
const QuantLib::DayCounter& curveDayCounter,
196 const QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>& index,
const bool interpolated,
197 const QuantLib::ext::shared_ptr<QuantLib::Seasonality>& seasonality) {
198 QuantLib::ext::shared_ptr<QuantLib::MultiplicativePriceSeasonality> multiplicativeSeasonality =
199 seasonality ? QuantLib::ext::dynamic_pointer_cast<QuantLib::MultiplicativePriceSeasonality>(seasonality) :
nullptr;
201 QL_REQUIRE(seasonality ==
nullptr || multiplicativeSeasonality,
202 "Only multiplicative seasonality supported at the moment");
210 if (!baseDateLastKnownFixing && swapBaseDate ==
curveBaseDate)
211 return zeroCouponRate;
213 QL_REQUIRE(index,
"can not compute base cpi of the zero coupon swap");
217 checkIfFixingAvailable(swapStart, swapObsLag, interpolated, *index);
218 }
catch (
const std::exception& e) {
219 QL_FAIL(
"Can not estimate the curve base date, got " << e.what());
222 Date swapMaturity = swapStart + swapTenor;
226 Rate instrumentBaseCPI =
cpiFixing(index, swapStart, swapObsLag, interpolated);
227 Time timeFromSwapBase =
228 inflationYearFraction(index->frequency(), interpolated, swapZCLegDayCounter, swapBaseDate, swapObservationDate);
230 auto fwdCPI = instrumentBaseCPI * std::pow(1 + zeroCouponRate, timeFromSwapBase);
235 Time timeFromCurveBase = inflationYearFraction(index->frequency(), interpolated, curveDayCounter,
curveBaseDate,
236 swapObservationDate);
237 double rateWithSeasonality = std::pow(fwdCPI / curveBaseFixing, 1.0 / timeFromCurveBase) - 1.0;
239 if (multiplicativeSeasonality) {
240 double factorAt = multiplicativeSeasonality->seasonalityFactor(swapObservationDate);
241 double factorBase = multiplicativeSeasonality->seasonalityFactor(
curveBaseDate);
242 double seasonalityFactor = std::pow(factorAt / factorBase, 1.0 / timeFromCurveBase);
243 return (rateWithSeasonality + 1) / seasonalityFactor - 1;
245 return rateWithSeasonality;
249 auto fixingPeriod = inflationPeriod(swapObservationDate, index->frequency());
250 auto paymentPeriod = inflationPeriod(swapMaturity, index->frequency());
253 inflationYearFraction(index->frequency(),
false, curveDayCounter,
curveBaseDate, fixingPeriod.first);
254 Time timeToFixing2 = inflationYearFraction(index->frequency(),
false, curveDayCounter,
curveBaseDate,
255 fixingPeriod.second + 1 * Days);
259 inflationYearFraction(index->frequency(),
true, curveDayCounter,
curveBaseDate, swapMaturity);
260 Time timeToStartPaymentPeriod =
261 inflationYearFraction(index->frequency(),
false, curveDayCounter,
curveBaseDate, paymentPeriod.first);
262 Time timeToEndPaymenetPeriod = inflationYearFraction(index->frequency(),
false, curveDayCounter,
curveBaseDate,
263 paymentPeriod.second + 1 * Days);
264 Time interpolationFactor =
265 (timeToPayment - timeToStartPaymentPeriod) / (timeToEndPaymenetPeriod - timeToStartPaymentPeriod);
268 Real target = fwdCPI / curveBaseFixing;
270 Real seasonalityFactor1 = 1.0;
271 Real seasonalityFactor2 = 1.0;
273 if (multiplicativeSeasonality) {
274 double factorAt1 = multiplicativeSeasonality->seasonalityFactor(fixingPeriod.first);
275 double factorAt2 = multiplicativeSeasonality->seasonalityFactor(fixingPeriod.second + 1 * Days);
276 double factorBase = multiplicativeSeasonality->seasonalityFactor(
curveBaseDate);
277 seasonalityFactor1 = factorAt1 / factorBase;
278 seasonalityFactor2 = factorAt2 / factorBase;
282 std::function<double(
double)> objectiveFunction = [&timeToFixing1, &timeToFixing2, &interpolationFactor,
283 &target, &seasonalityFactor1, &seasonalityFactor2](Rate r) {
284 return target - (std::pow(1 + r, timeToFixing1) * seasonalityFactor1 +
285 (std::pow(1 + r, timeToFixing2) * seasonalityFactor2 -
286 std::pow(1 + r, timeToFixing1) * seasonalityFactor1) *
287 interpolationFactor);
290 Rate guess = std::pow(fwdCPI / curveBaseFixing, 1.0 / timeToFixing2) - 1.0;
293 QuantLib::Brent solver;
294 r = solver.solve(objectiveFunction, 1e-8, guess, -0.1, 0.2);
303 if (
auto qvs = QuantLib::ext::dynamic_pointer_cast<QuantExt::CPIVolatilitySurface>(surface)) {
304 return qvs->isLogNormal();
interpolated correlation term structure
some inflation related utilities.
QuantLib::Date fixingDate(const QuantLib::Date &d, const QuantLib::Period obsLag, const QuantLib::Frequency freq, bool interpolated)
QuantLib::Date lastAvailableFixing(const QuantLib::ZeroInflationIndex &index, const QuantLib::Date &asof)
Check if today - availabilityLag is already known, otherwise return the fixingDate of the previous fi...
QuantLib::Rate cpiFixing(const QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > &index, const QuantLib::Date &maturity, const QuantLib::Period &obsLag, bool interpolated)
Computes a CPI fixing giving an zeroIndex, with interpolation if needed.
QuantLib::Date curveBaseDate(const bool baseDateLastKnownFixing, const QuantLib::Date &refDate, const QuantLib::Period obsLagCurve, const QuantLib::Frequency curveFreq, const QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > &index)
derives the zero inflation curve base date based on the useLastKnownFixing rule
QuantLib::Rate guessCurveBaseRate(const bool baseDateLastKnownFixing, const QuantLib::Date &swapStart, const QuantLib::Date &asof, const QuantLib::Period &swapTenor, const QuantLib::DayCounter &swapZCLegDayCounter, const QuantLib::Period &swapObsLag, const QuantLib::Rate zeroCouponRate, const QuantLib::Period &curveObsLag, const QuantLib::DayCounter &curveDayCounter, const QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > &index, const bool interpolated, const QuantLib::ext::shared_ptr< QuantLib::Seasonality > &seasonality)
bool isCPIVolSurfaceLogNormal(const QuantLib::ext::shared_ptr< QuantLib::CPIVolatilitySurface > &surface)
void addInflationIndexToMap(std::map< std::tuple< std::string, QuantLib::CPI::InterpolationType, QuantLib::Frequency, QuantLib::Period >, QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > > &inflationIndices, const QuantLib::ext::shared_ptr< QuantLib::Index > &index, QuantLib::CPI::InterpolationType interpolation, Frequency couponFrequency, Period observationLag)
std::map< std::tuple< std::string, QuantLib::CPI::InterpolationType, QuantLib::Frequency, QuantLib::Period >, QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > > extractAllInflationUnderlyingFromBond(const QuantLib::ext::shared_ptr< QuantLib::Bond > &bond)
CompiledFormula pow(CompiledFormula x, const CompiledFormula &y)
Real inflationLinkedBondQuoteFactor(const QuantLib::ext::shared_ptr< QuantLib::Bond > &bond)
Time inflationTime(const Date &date, const QuantLib::ext::shared_ptr< InflationTermStructure > &inflationTs, bool indexIsInterpolated, const DayCounter &dayCounter)
Real inflationGrowth(const QuantLib::ext::shared_ptr< CrossAssetModel > &model, Size index, Time S, Time T, Real irState, Real rrState, bool indexIsInterpolated)