QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.38
Loading...
Searching...
No Matches
inflationindex.cpp
Go to the documentation of this file.
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2007 Chris Kenyon
5 Copyright (C) 2021 Ralf Konrad Eckel
6
7 This file is part of QuantLib, a free-software/open-source library
8 for financial quantitative analysts and developers - http://quantlib.org/
9
10 QuantLib is free software: you can redistribute it and/or modify it
11 under the terms of the QuantLib license. You should have received a
12 copy of the license along with this program; if not, please email
13 <quantlib-dev@lists.sf.net>. The license is also available online at
14 <http://quantlib.org/license.shtml>.
15
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the license for more details.
19*/
20
24#include <utility>
25
26namespace QuantLib {
27
28 Real CPI::laggedFixing(const ext::shared_ptr<ZeroInflationIndex>& index,
29 const Date& date,
30 const Period& observationLag,
31 CPI::InterpolationType interpolationType) {
32
33 switch (interpolationType) {
34 case AsIndex:
35 case Flat: {
36 auto fixingPeriod = inflationPeriod(date - observationLag, index->frequency());
37 return index->fixing(fixingPeriod.first);
38 }
39 case Linear: {
40 auto fixingPeriod = inflationPeriod(date - observationLag, index->frequency());
41 auto interpolationPeriod = inflationPeriod(date, index->frequency());
42
43 auto I0 = index->fixing(fixingPeriod.first);
44
45 if (date == interpolationPeriod.first) {
46 // special case; no interpolation. This avoids asking for
47 // the fixing at the end of the period, which might need a
48 // forecast curve to be set.
49 return I0;
50 }
51
52 static const auto oneDay = Period(1, Days);
53
54 auto I1 = index->fixing(fixingPeriod.second + oneDay);
55
56 return I0 + (I1 - I0) * (date - interpolationPeriod.first) /
57 (Real)((interpolationPeriod.second + oneDay) - interpolationPeriod.first);
58 }
59 default:
60 QL_FAIL("unknown CPI interpolation type: " << int(interpolationType));
61 }
62 }
63
64
65 Real CPI::laggedYoYRate(const ext::shared_ptr<YoYInflationIndex>& index,
66 const Date& date,
67 const Period& observationLag,
68 CPI::InterpolationType interpolationType) {
69
70 switch (interpolationType) {
71 case AsIndex: {
72 return index->fixing(date - observationLag);
73 }
74 case Flat: {
75 auto fixingPeriod = inflationPeriod(date - observationLag, index->frequency());
76 return index->fixing(fixingPeriod.first);
77 }
78 case Linear: {
79 if (index->ratio() && !index->needsForecast(date)) {
80 // in the case of a ratio, the convention seems to be to interpolate
81 // the underlying index fixings first, then take the ratio. This is
82 // not the same as taking the ratios and then interpolate, which is
83 // equivalent to what the else clause does.
84 // However, we can only do this if the fixings we need are in the past,
85 // because forecasts need to be done through the YoY forecast curve,
86 // and not the underlying index.
87
88 auto underlying = index->underlyingIndex();
89 Rate Z1 = CPI::laggedFixing(underlying, date, observationLag, interpolationType);
90 Rate Z0 = CPI::laggedFixing(underlying, date - 1*Years, observationLag, interpolationType);
91
92 return Z1/Z0 - 1.0;
93
94 } else {
95 static const auto oneDay = Period(1, Days);
96
97 auto fixingPeriod = inflationPeriod(date - observationLag, index->frequency());
98 auto interpolationPeriod = inflationPeriod(date, index->frequency());
99
100 auto Y0 = index->fixing(fixingPeriod.first);
101
102 if (date == interpolationPeriod.first) {
103 // special case; no interpolation anyway.
104 return Y0;
105 }
106
107 auto Y1 = index->fixing(fixingPeriod.second + oneDay);
108
109 return Y0 + (Y1 - Y0) * (date - interpolationPeriod.first) /
110 (Real)((interpolationPeriod.second + oneDay) - interpolationPeriod.first);
111 }
112 }
113 default:
114 QL_FAIL("unknown CPI interpolation type: " << int(interpolationType));
115 }
116 }
117
118
119 InflationIndex::InflationIndex(std::string familyName,
120 Region region,
121 bool revised,
122 Frequency frequency,
123 const Period& availabilityLag,
124 Currency currency)
125 : familyName_(std::move(familyName)), region_(std::move(region)), revised_(revised),
126 frequency_(frequency), availabilityLag_(availabilityLag), currency_(std::move(currency)) {
127 name_ = region_.name() + " " + familyName_;
128 registerWith(Settings::instance().evaluationDate());
130 }
131
133 static NullCalendar c;
134 return c;
135 }
136
137 void InflationIndex::addFixing(const Date& fixingDate,
138 Real fixing,
139 bool forceOverwrite) {
140
141 std::pair<Date,Date> lim = inflationPeriod(fixingDate, frequency_);
142 Size n = static_cast<QuantLib::Size>(lim.second - lim.first) + 1;
143 std::vector<Date> dates(n);
144 std::vector<Rate> rates(n);
145 for (Size i=0; i<n; ++i) {
146 dates[i] = lim.first + i;
147 rates[i] = fixing;
148 }
149
150 Index::addFixings(dates.begin(), dates.end(),
151 rates.begin(), forceOverwrite);
152 }
153
154 ZeroInflationIndex::ZeroInflationIndex(const std::string& familyName,
155 const Region& region,
156 bool revised,
157 Frequency frequency,
158 const Period& availabilityLag,
159 const Currency& currency,
161 : InflationIndex(familyName, region, revised, frequency, availabilityLag, currency),
162 zeroInflation_(std::move(zeroInflation)) {
164 }
165
167 bool /*forecastTodaysFixing*/) const {
168 if (!needsForecast(fixingDate)) {
169 const Real I1 = pastFixing(fixingDate);
170 QL_REQUIRE(I1 != Null<Real>(),
171 "Missing " << name() << " fixing for "
172 << inflationPeriod(fixingDate, frequency_).first);
173
174 return I1;
175 } else {
176 return forecastFixing(fixingDate);
177 }
178 }
179
180 Real ZeroInflationIndex::pastFixing(const Date& fixingDate) const {
181 const auto p = inflationPeriod(fixingDate, frequency_);
182 const auto& ts = timeSeries();
183 return ts[p.first];
184 }
185
187 const auto& fixings = timeSeries();
188 QL_REQUIRE(!fixings.empty(), "no fixings stored for " << name());
189 // attribute fixing to first day of the underlying period
190 return inflationPeriod(fixings.lastDate(), frequency_).first;
191 }
192
193 bool ZeroInflationIndex::needsForecast(const Date& fixingDate) const {
194
196
197 auto latestPossibleHistoricalFixingPeriod =
199
200 // Zero-index fixings are always non-interpolated.
201 auto fixingPeriod = inflationPeriod(fixingDate, frequency_);
202 Date latestNeededDate = fixingPeriod.first;
203
204 if (latestNeededDate < latestPossibleHistoricalFixingPeriod.first) {
205 // the fixing date is well before the availability lag, so
206 // we know that fixings must be provided.
207 return false;
208 } else if (latestNeededDate > latestPossibleHistoricalFixingPeriod.second) {
209 // the fixing can't be available yet
210 return true;
211 } else {
212 // we're not sure, but the fixing might be there so we check.
213 Real f = timeSeries()[latestNeededDate];
214 return (f == Null<Real>());
215 }
216 }
217
218
220 // the term structure is relative to the fixing value at the base date.
221 Date baseDate = zeroInflation_->baseDate();
222 QL_REQUIRE(!needsForecast(baseDate),
223 name() << " index fixing at base date " << baseDate << " is not available");
224 Real baseFixing = fixing(baseDate);
225
226 std::pair<Date, Date> fixingPeriod = inflationPeriod(fixingDate, frequency_);
227
228 Date firstDateInPeriod = fixingPeriod.first;
229 Rate Z1 = zeroInflation_->zeroRate(firstDateInPeriod, Period(0,Days), false);
230 Time t1 = inflationYearFraction(frequency_, false, zeroInflation_->dayCounter(),
231 baseDate, firstDateInPeriod);
232 return baseFixing * std::pow(1.0 + Z1, t1);
233 }
234
235
236 ext::shared_ptr<ZeroInflationIndex> ZeroInflationIndex::clone(
237 const Handle<ZeroInflationTermStructure>& h) const {
238 return ext::make_shared<ZeroInflationIndex>(
240 }
241
242
243 YoYInflationIndex::YoYInflationIndex(const ext::shared_ptr<ZeroInflationIndex>& underlyingIndex,
245 : InflationIndex("YYR_" + underlyingIndex->familyName(), underlyingIndex->region(),
246 underlyingIndex->revised(), underlyingIndex->frequency(),
247 underlyingIndex->availabilityLag(), underlyingIndex->currency()),
248 interpolated_(false), ratio_(true), underlyingIndex_(underlyingIndex),
249 yoyInflation_(std::move(yoyInflation)) {
252 }
253
254 YoYInflationIndex::YoYInflationIndex(const ext::shared_ptr<ZeroInflationIndex>& underlyingIndex,
255 bool interpolated,
257 : YoYInflationIndex(underlyingIndex, std::move(yoyInflation)) {
259 }
260
261 YoYInflationIndex::YoYInflationIndex(const std::string& familyName,
262 const Region& region,
263 bool revised,
264 Frequency frequency,
265 const Period& availabilityLag,
266 const Currency& currency,
268 : InflationIndex(familyName, region, revised, frequency, availabilityLag, currency),
269 interpolated_(false), ratio_(false), yoyInflation_(std::move(yoyInflation)) {
271 }
272
273 YoYInflationIndex::YoYInflationIndex(const std::string& familyName,
274 const Region& region,
275 bool revised,
276 bool interpolated,
277 Frequency frequency,
278 const Period& availabilityLag,
279 const Currency& currency,
281 : YoYInflationIndex(familyName, region, revised, frequency, availabilityLag, currency, std::move(yoyInflation)) {
283 }
284
285
287 bool /*forecastTodaysFixing*/) const {
288 if (needsForecast(fixingDate)) {
289 return forecastFixing(fixingDate);
290 } else {
291 return pastFixing(fixingDate);
292 }
293 }
294
296 if (ratio()) {
297 return underlyingIndex_->lastFixingDate();
298 } else {
299 const auto& fixings = timeSeries();
300 QL_REQUIRE(!fixings.empty(), "no fixings stored for " << name());
301 // attribute fixing to first day of the underlying period
302 return inflationPeriod(fixings.lastDate(), frequency_).first;
303 }
304 }
305
306 bool YoYInflationIndex::needsForecast(const Date& fixingDate) const {
308
309 auto fixingPeriod = inflationPeriod(fixingDate, frequency_);
310 Date latestNeededDate;
311 if (!interpolated() || fixingDate == fixingPeriod.first)
312 latestNeededDate = fixingPeriod.first;
313 else
314 latestNeededDate = fixingPeriod.second + 1;
315
316 if (ratio()) {
317 return underlyingIndex_->needsForecast(latestNeededDate);
318 } else {
319 auto latestPossibleHistoricalFixingPeriod =
321
322 if (latestNeededDate < latestPossibleHistoricalFixingPeriod.first) {
323 // the fixing date is well before the availability lag, so
324 // we know that fixings must be provided.
325 return false;
326 } else if (latestNeededDate > latestPossibleHistoricalFixingPeriod.second) {
327 // the fixing can't be available yet
328 return true;
329 } else {
330 // we're not sure, but the fixing might be there so we check.
331 Real f = timeSeries()[latestNeededDate];
332 return (f == Null<Real>());
333 }
334 }
335 }
336
337 Real YoYInflationIndex::pastFixing(const Date& fixingDate) const {
338 if (ratio()) {
339
340 auto interpolationType = interpolated() ? CPI::Linear : CPI::Flat;
341
342 Rate pastFixing = CPI::laggedFixing(underlyingIndex_, fixingDate, Period(0, Months), interpolationType);
343 Rate previousFixing = CPI::laggedFixing(underlyingIndex_, fixingDate - 1*Years, Period(0, Months), interpolationType);
344
345 return pastFixing/previousFixing - 1.0;
346
347 } else { // NOT ratio
348
349 const auto& ts = timeSeries();
350 auto [periodStart, periodEnd] = inflationPeriod(fixingDate, frequency_);
351
352 Rate YY0 = ts[periodStart];
353 QL_REQUIRE(YY0 != Null<Rate>(),
354 "Missing " << name() << " fixing for " << periodStart);
355
356 if (!interpolated() || /* degenerate case */ fixingDate == periodStart) {
357
358 return YY0;
359
360 } else {
361
362 Real dp = periodEnd + 1 - periodStart;
363 Real dl = fixingDate - periodStart;
364 Rate YY1 = ts[periodEnd+1];
365 QL_REQUIRE(YY1 != Null<Rate>(),
366 "Missing " << name() << " fixing for " << periodEnd+1);
367 return YY0 + (YY1 - YY0) * dl / dp;
368
369 }
370 }
371 }
372
373 Real YoYInflationIndex::forecastFixing(const Date& fixingDate) const {
374
375 Date d;
376 if (interpolated()) {
377 d = fixingDate;
378 } else {
379 // if the value is not interpolated use the starting value
380 // by internal convention this will be consistent
381 std::pair<Date,Date> fixingPeriod = inflationPeriod(fixingDate, frequency_);
382 d = fixingPeriod.first;
383 }
384 return yoyInflation_->yoyRate(d,0*Days);
385 }
386
387 ext::shared_ptr<YoYInflationIndex> YoYInflationIndex::clone(
388 const Handle<YoYInflationTermStructure>& h) const {
390 if (ratio_) {
391 // NOLINTNEXTLINE(modernize-make-shared)
392 return ext::shared_ptr<YoYInflationIndex>(
394 } else {
395 // NOLINTNEXTLINE(modernize-make-shared)
396 return ext::shared_ptr<YoYInflationIndex>(
400 }
402 }
403
404
407 if (type == QuantLib::CPI::AsIndex) {
408 return QuantLib::CPI::Flat;
409 } else {
410 return type;
411 }
412 }
413
416 const ext::shared_ptr<YoYInflationIndex>& index) {
417 if (type == QuantLib::CPI::AsIndex) {
418 return index->interpolated() ? QuantLib::CPI::Linear : QuantLib::CPI::Flat;
419 } else {
420 return type;
421 }
422 }
423
426 }
427
429 const ext::shared_ptr<YoYInflationIndex>& index) {
431 }
432
433}
calendar class
Definition: calendar.hpp:61
Currency specification
Definition: currency.hpp:36
Concrete date class.
Definition: date.hpp:125
Shared handle to an observable.
Definition: handle.hpp:41
ext::shared_ptr< Observable > notifier() const
Definition: index.hpp:113
const TimeSeries< Real > & timeSeries() const
returns the fixing TimeSeries
Definition: index.hpp:71
void addFixings(const TimeSeries< Real > &t, bool forceOverwrite=false)
stores historical fixings from a TimeSeries
Definition: index.cpp:31
Base class for inflation-rate indexes,.
InflationIndex(std::string familyName, Region region, bool revised, Frequency frequency, const Period &availabilitiyLag, Currency currency)
Calendar fixingCalendar() const override
std::string name() const override
Returns the name of the index.
void addFixing(const Date &fixingDate, Rate fixing, bool forceOverwrite=false) override
Real fixing(const Date &fixingDate, bool forecastTodaysFixing=false) const override=0
Calendar for reproducing theoretical calculations.
template class providing a null value for a given type.
Definition: null.hpp:59
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:226
Frequency frequency() const
Definition: period.cpp:69
Region class, used for inflation applicability.
Definition: region.hpp:36
const std::string & name() const
Definition: region.hpp:113
DateProxy & evaluationDate()
the date at which pricing is to be performed.
Definition: settings.hpp:147
static Settings & instance()
access to the unique instance
Definition: singleton.hpp:104
Base class for year-on-year inflation indices.
Handle< YoYInflationTermStructure > yoyInflation_
ext::shared_ptr< YoYInflationIndex > clone(const Handle< YoYInflationTermStructure > &h) const
Rate forecastFixing(const Date &fixingDate) const
Rate fixing(const Date &fixingDate, bool forecastTodaysFixing=false) const override
ext::shared_ptr< ZeroInflationIndex > underlyingIndex_
bool needsForecast(const Date &fixingDate) const
YoYInflationIndex(const ext::shared_ptr< ZeroInflationIndex > &underlyingIndex, Handle< YoYInflationTermStructure > ts={})
Constructor for year-on-year indices defined as a ratio.
Real pastFixing(const Date &fixingDate) const override
returns a past fixing at the given date
ext::shared_ptr< ZeroInflationIndex > clone(const Handle< ZeroInflationTermStructure > &h) const
Handle< ZeroInflationTermStructure > zeroInflation_
ZeroInflationIndex(const std::string &familyName, const Region &region, bool revised, Frequency frequency, const Period &availabilityLag, const Currency &currency, Handle< ZeroInflationTermStructure > ts={})
Real forecastFixing(const Date &fixingDate) const
bool needsForecast(const Date &fixingDate) const
Real pastFixing(const Date &fixingDate) const override
returns a past fixing at the given date
Real fixing(const Date &fixingDate, bool forecastTodaysFixing=false) const override
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
#define QL_FAIL(message)
throw an error (possibly with file and line information)
Definition: errors.hpp:92
Date d
Frequency
Frequency of events.
Definition: frequency.hpp:37
Real Time
continuous quantity with 1-year units
Definition: types.hpp:62
QL_REAL Real
real number
Definition: types.hpp:50
Real Rate
interest rates
Definition: types.hpp:70
std::size_t Size
size of a container
Definition: types.hpp:58
base classes for inflation indexes
Base classes for inflation term structures.
bool isInterpolated(const QuantLib::CPI::InterpolationType &type)
QuantLib::CPI::InterpolationType effectiveInterpolationType(const QuantLib::CPI::InterpolationType &type)
Definition: any.hpp:37
std::pair< Date, Date > inflationPeriod(const Date &d, Frequency frequency)
utility function giving the inflation period for a given date
Time inflationYearFraction(Frequency f, bool indexIsInterpolated, const DayCounter &dayCounter, const Date &d1, const Date &d2)
STL namespace.
Calendar for reproducing theoretical calculations.
#define QL_DEPRECATED_DISABLE_WARNING
Definition: qldefines.hpp:216
#define QL_DEPRECATED_ENABLE_WARNING
Definition: qldefines.hpp:217
static Real laggedYoYRate(const ext::shared_ptr< YoYInflationIndex > &index, const Date &date, const Period &observationLag, InterpolationType interpolationType)
interpolated year-on-year inflation rate
static Real laggedFixing(const ext::shared_ptr< ZeroInflationIndex > &index, const Date &date, const Period &observationLag, InterpolationType interpolationType)
interpolated inflation fixing
InterpolationType
when you observe an index, how do you interpolate between fixings?
@ AsIndex
same interpolation as index
@ Linear
linearly between bracketing fixings
@ Flat
flat from previous fixing