Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
inflation.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19#include <ql/cashflows/cpicoupon.hpp>
20#include <ql/math/solvers1d/brent.hpp>
22#include <ql/termstructures/inflation/interpolatedzeroinflationcurve.hpp>
24
25using QuantLib::Date;
26using QuantLib::DayCounter;
27using QuantLib::Days;
28using QuantLib::Frequency;
29using QuantLib::Handle;
30using QuantLib::InflationIndex;
31using QuantLib::InflationTermStructure;
32using QuantLib::inflationYearFraction;
33using QuantLib::Period;
34using QuantLib::Rate;
35using QuantLib::Real;
36using QuantLib::Time;
37using QuantLib::ZeroInflationTermStructure;
38using std::pow;
39
40namespace {
41
42// Throws an error if index doesn't have a historical fixing for fixingDate
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);
46}
47
48// Throws an error if any fixing required to compute a cpi fixing is missing
49void checkIfFixingAvailable(const Date& maturity, const Period obsLag, bool interpolated,
50 const QuantLib::ZeroInflationIndex& index) {
51
52 auto fixingPeriod = inflationPeriod(maturity - obsLag, index.frequency());
53 throwExceptionIfHistoricalFixingMissing(fixingPeriod.first, index);
54 if (interpolated)
55 throwExceptionIfHistoricalFixingMissing(fixingPeriod.second + 1 * Days, index);
56}
57}
58
59namespace QuantExt {
60
61Time inflationTime(const Date& date,
62 const QuantLib::ext::shared_ptr<InflationTermStructure>& inflationTs,
63 bool indexIsInterpolated,
64 const DayCounter& dayCounter) {
65
66 DayCounter dc = inflationTs->dayCounter();
67 if (dayCounter != DayCounter())
68 dc = dayCounter;
69
70 return inflationYearFraction(inflationTs->frequency(), indexIsInterpolated,
71 dc, inflationTs->baseDate(), date);
72}
73
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);
77}
78
79Real inflationGrowth(const Handle<ZeroInflationTermStructure>& ts, Time t, bool indexIsInterpolated) {
80 return inflationGrowth(ts, t, ts->dayCounter(), indexIsInterpolated);
81}
82
83Real inflationLinkedBondQuoteFactor(const QuantLib::ext::shared_ptr<QuantLib::Bond>& bond) {
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;
92 //Date curveBaseDate = inflationIndex->zeroInflationTermStructure()->baseDate();
93 Real todaysCPI = inflationIndex->fixing(curveBaseDate);
94 if (inflCpn->observationInterpolation() == QuantLib::CPI::Linear) {
95
96 std::pair<Date, Date> observationPeriod = inflationPeriod(curveBaseDate, inflationIndex->frequency());
97
98 Real indexStart = inflationIndex->fixing(observationPeriod.first);
99 Real indexEnd = inflationIndex->fixing(observationPeriod.second + 1 * QuantLib::Days);
100
101 todaysCPI = indexStart + (settlementDate - currentInflationPeriod.first) *
102 (indexEnd - indexStart) /
103 (Real)(currentInflationPeriod.second - currentInflationPeriod.first);
104 }
105 QuantLib::Rate baseCPI = inflCpn->baseCPI();
106 if (baseCPI == QuantLib::Null<QuantLib::Rate>()) {
107 baseCPI =
108 QuantLib::CPI::laggedFixing(inflCpn->cpiIndex(), inflCpn->baseDate() + inflCpn->observationLag(),
109 inflCpn->observationLag(),
110 inflCpn->observationInterpolation());
111 }
112 inflFactor = todaysCPI / baseCPI;
113 break;
114 }
115 }
116 return inflFactor;
117}
118
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;
130 }
131 }
132};
133
134std::map<std::tuple<std::string, QuantLib::CPI::InterpolationType, QuantLib::Frequency, QuantLib::Period>,
135 QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>>
136extractAllInflationUnderlyingFromBond(const QuantLib::ext::shared_ptr<QuantLib::Bond>& bond) {
137
138 std::map<std::tuple<std::string, QuantLib::CPI::InterpolationType, Frequency, Period>,
139 QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>>
140 inflationIndices;
141 if (bond != nullptr) {
142 for (const auto& cf : bond->cashflows()) {
143 if (auto cp = QuantLib::ext::dynamic_pointer_cast<QuantLib::CPICoupon>(cf)) {
144 addInflationIndexToMap(inflationIndices, cp->index(), cp->observationInterpolation(),
145 cp->index()->frequency(), cp->observationLag());
146 } else if (auto cp = QuantLib::ext::dynamic_pointer_cast<QuantLib::CPICashFlow>(cf)) {
147 addInflationIndexToMap(inflationIndices, cp->index(), cp->interpolation(), cp->frequency(),
148 cp->observationLag());
149 }
150 }
151 }
152 return inflationIndices;
153}
154namespace ZeroInflation {
155
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;
160 } else {
161 // The fixing for the inflation period before the availability lag should be there
162 return inflationPeriod(availbilityLagFixingDate - 1 * QuantLib::Days, index.frequency()).first;
163 }
164}
165
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);
170}
171
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");
177 return lastAvailableFixing(*index, refDate);
178 } else {
179 return QuantLib::inflationPeriod(refDate - obsLagCurve, curveFreq).first;
180 }
181}
182
183QuantLib::Date fixingDate(const QuantLib::Date& d, const QuantLib::Period obsLag, const QuantLib::Frequency freq,
184 bool interpolated) {
185 Date obsDate = d - obsLag;
186 if (!interpolated)
187 obsDate = QuantLib::inflationPeriod(obsDate, freq).first;
188 return obsDate;
189}
190
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;
200
201 QL_REQUIRE(seasonality == nullptr || multiplicativeSeasonality,
202 "Only multiplicative seasonality supported at the moment");
203
204 Date swapBaseDate = ZeroInflation::fixingDate(swapStart, swapObsLag, index->frequency(), interpolated);
205
206 Date curveBaseDate =
207 ZeroInflation::curveBaseDate(baseDateLastKnownFixing, asof, curveObsLag, index->frequency(), index);
208
209 // If the baseDate in Curve is today - obsLag then the quoted zeroRate should be used
210 if (!baseDateLastKnownFixing && swapBaseDate == curveBaseDate)
211 return zeroCouponRate;
212
213 QL_REQUIRE(index, "can not compute base cpi of the zero coupon swap");
214 // Otherwise we need to take into account the accrued inflation between rate helper base date and curve base date
215 // Check if historical fixings available
216 try {
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());
220 }
221
222 Date swapMaturity = swapStart + swapTenor;
223 Date swapObservationDate = ZeroInflation::fixingDate(swapMaturity, swapObsLag, index->frequency(), interpolated);
224
225 // TODO check historical fixings available
226 Rate instrumentBaseCPI = cpiFixing(index, swapStart, swapObsLag, interpolated);
227 Time timeFromSwapBase =
228 inflationYearFraction(index->frequency(), interpolated, swapZCLegDayCounter, swapBaseDate, swapObservationDate);
229
230 auto fwdCPI = instrumentBaseCPI * std::pow(1 + zeroCouponRate, timeFromSwapBase);
231
232 double curveBaseFixing = index->fixing(curveBaseDate);
233
234 if (!interpolated) {
235 Time timeFromCurveBase = inflationYearFraction(index->frequency(), interpolated, curveDayCounter, curveBaseDate,
236 swapObservationDate);
237 double rateWithSeasonality = std::pow(fwdCPI / curveBaseFixing, 1.0 / timeFromCurveBase) - 1.0;
238
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;
244 } else {
245 return rateWithSeasonality;
246 }
247 } else {
248 // Compute the interpolated fixing of the ZCIIS at maturity
249 auto fixingPeriod = inflationPeriod(swapObservationDate, index->frequency());
250 auto paymentPeriod = inflationPeriod(swapMaturity, index->frequency());
251 // Fixing times from curve base date
252 Time timeToFixing1 =
253 inflationYearFraction(index->frequency(), false, curveDayCounter, curveBaseDate, fixingPeriod.first);
254 Time timeToFixing2 = inflationYearFraction(index->frequency(), false, curveDayCounter, curveBaseDate,
255 fixingPeriod.second + 1 * Days);
256
257 // Time interpolation
258 Time timeToPayment =
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);
266
267 // Root search for a constant rate that the interpolation of both cpi matches the forward cpi
268 Real target = fwdCPI / curveBaseFixing;
269
270 Real seasonalityFactor1 = 1.0;
271 Real seasonalityFactor2 = 1.0;
272
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;
279 }
280
281
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);
288 };
289
290 Rate guess = std::pow(fwdCPI / curveBaseFixing, 1.0 / timeToFixing2) - 1.0;
291 Rate r = guess;
292 try {
293 QuantLib::Brent solver;
294 r = solver.solve(objectiveFunction, 1e-8, guess, -0.1, 0.2);
295 } catch (...) {
296 r = guess;
297 }
298 return r;
299 }
300}
301
302bool isCPIVolSurfaceLogNormal(const QuantLib::ext::shared_ptr<QuantLib::CPIVolatilitySurface>& surface) {
303 if (auto qvs = QuantLib::ext::dynamic_pointer_cast<QuantExt::CPIVolatilitySurface>(surface)) {
304 return qvs->isLogNormal();
305 } else {
306 // QuantLib::CPIVolatilitySurface doesn't support volType so assume it's log normal
307 return true;
308 }
309}
310
311} // namespace ZeroInflation
312
313} // namespace QuantExt
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)
Definition: inflation.cpp:183
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...
Definition: inflation.cpp:156
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.
Definition: inflation.cpp:166
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
Definition: inflation.cpp:172
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)
Definition: inflation.cpp:191
bool isCPIVolSurfaceLogNormal(const QuantLib::ext::shared_ptr< QuantLib::CPIVolatilitySurface > &surface)
Definition: inflation.cpp:302
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)
Definition: inflation.cpp:119
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)
Definition: inflation.cpp:136
CompiledFormula pow(CompiledFormula x, const CompiledFormula &y)
Real inflationLinkedBondQuoteFactor(const QuantLib::ext::shared_ptr< QuantLib::Bond > &bond)
Definition: inflation.cpp:83
Time inflationTime(const Date &date, const QuantLib::ext::shared_ptr< InflationTermStructure > &inflationTs, bool indexIsInterpolated, const DayCounter &dayCounter)
Definition: inflation.cpp:61
Real inflationGrowth(const QuantLib::ext::shared_ptr< CrossAssetModel > &model, Size index, Time S, Time T, Real irState, Real rrState, bool indexIsInterpolated)