/src/quantlib/ql/termstructures/inflation/inflationhelpers.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2007, 2009 Chris Kenyon |
5 | | Copyright (C) 2007 StatPro Italia srl |
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 | | <https://www.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 | | |
21 | | #include <ql/cashflows/inflationcouponpricer.hpp> |
22 | | #include <ql/indexes/inflationindex.hpp> |
23 | | #include <ql/pricingengines/swap/discountingswapengine.hpp> |
24 | | #include <ql/shared_ptr.hpp> |
25 | | #include <ql/termstructures/inflation/inflationhelpers.hpp> |
26 | | #include <ql/termstructures/yield/flatforward.hpp> |
27 | | #include <ql/utilities/null_deleter.hpp> |
28 | | #include <utility> |
29 | | |
30 | | namespace QuantLib { |
31 | | |
32 | | QL_DEPRECATED_DISABLE_WARNING |
33 | | |
34 | | ZeroCouponInflationSwapHelper::ZeroCouponInflationSwapHelper( |
35 | | const Handle<Quote>& quote, |
36 | | const Period& swapObsLag, |
37 | | const Date& maturity, |
38 | | Calendar calendar, |
39 | | BusinessDayConvention paymentConvention, |
40 | | const DayCounter& dayCounter, |
41 | | const ext::shared_ptr<ZeroInflationIndex>& zii, |
42 | | CPI::InterpolationType observationInterpolation, |
43 | | Pillar::Choice pillar, |
44 | | Date customPillarDate) |
45 | 0 | : ZeroCouponInflationSwapHelper( |
46 | 0 | quote, swapObsLag, Date(), maturity, std::move(calendar), paymentConvention, |
47 | 0 | dayCounter, zii, observationInterpolation, |
48 | 0 | pillar, customPillarDate) {} |
49 | | |
50 | | ZeroCouponInflationSwapHelper::ZeroCouponInflationSwapHelper( |
51 | | const Handle<Quote>& quote, |
52 | | const Period& swapObsLag, |
53 | | const Date& startDate, |
54 | | const Date& endDate, |
55 | | Calendar calendar, |
56 | | BusinessDayConvention paymentConvention, |
57 | | DayCounter dayCounter, |
58 | | const ext::shared_ptr<ZeroInflationIndex>& zii, |
59 | | CPI::InterpolationType observationInterpolation, |
60 | | Pillar::Choice pillar, |
61 | | Date customPillarDate) |
62 | 0 | : RelativeDateBootstrapHelper<ZeroInflationTermStructure>(quote, startDate == Date()), |
63 | 0 | swapObsLag_(swapObsLag), startDate_(startDate), maturity_(endDate), |
64 | 0 | calendar_(std::move(calendar)), paymentConvention_(paymentConvention), |
65 | 0 | dayCounter_(std::move(dayCounter)), observationInterpolation_(observationInterpolation), |
66 | 0 | pillarChoice_(pillar) { |
67 | 0 | zii_ = zii->clone(termStructureHandle_); |
68 | | // We want to be notified of changes of fixings, but we don't |
69 | | // want notifications from termStructureHandle_ (they would |
70 | | // interfere with bootstrapping.) |
71 | 0 | zii_->unregisterWith(termStructureHandle_); |
72 | |
|
73 | 0 | auto fixingPeriod = inflationPeriod(maturity_ - swapObsLag_, zii_->frequency()); |
74 | |
|
75 | 0 | if (detail::CPI::isInterpolated(observationInterpolation_)) { |
76 | 0 | earliestDate_ = fixingPeriod.first; |
77 | 0 | latestDate_ = fixingPeriod.second + 1; |
78 | |
|
79 | 0 | switch (pillarChoice_) { |
80 | 0 | case Pillar::MaturityDate: |
81 | 0 | pillarDate_ = latestDate_; |
82 | 0 | break; |
83 | 0 | case Pillar::LastRelevantDate: |
84 | | // Assign the pillar to the node with the dominant |
85 | | // interpolation weight. We use startDate_ (the swap |
86 | | // effective date) rather than maturity_ so that all |
87 | | // helpers sharing the same effective date make the same |
88 | | // LEFT/RIGHT decision. Using maturity_ would cause |
89 | | // collisions when consecutive helpers mature in months |
90 | | // of different length (e.g. February vs March) and the |
91 | | // day-of-month is near the mid-month threshold. |
92 | 0 | { |
93 | 0 | Date weightDate = startDate_ != Date() ? startDate_ : maturity_; |
94 | 0 | auto weightPeriod = inflationPeriod(weightDate, zii_->frequency()); |
95 | 0 | Real dp = (weightPeriod.second + 1) - weightPeriod.first; |
96 | 0 | Real dt = weightDate - weightPeriod.first; |
97 | 0 | if (dt / dp <= 0.5) |
98 | 0 | pillarDate_ = fixingPeriod.first; |
99 | 0 | } |
100 | 0 | break; |
101 | 0 | case Pillar::CustomDate: |
102 | 0 | pillarDate_ = customPillarDate; |
103 | 0 | QL_REQUIRE(pillarDate_ >= earliestDate_, |
104 | 0 | "pillar date (" << pillarDate_ << ") must be later " |
105 | 0 | "than or equal to the instrument's earliest date (" |
106 | 0 | << earliestDate_ << ")"); |
107 | 0 | QL_REQUIRE(pillarDate_ <= latestDate_, |
108 | 0 | "pillar date (" << pillarDate_ << ") must be before " |
109 | 0 | "or equal to the instrument's latest relevant date (" |
110 | 0 | << latestDate_ << ")"); |
111 | 0 | break; |
112 | 0 | default: |
113 | 0 | QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")"); |
114 | 0 | } |
115 | 0 | } else { |
116 | | // Not interpolated: only the left node matters, so |
117 | | // earliestDate_ == latestDate_ and pillarChoice_ is |
118 | | // irrelevant (there is only one possible pillar). |
119 | 0 | earliestDate_ = fixingPeriod.first; |
120 | 0 | latestDate_ = fixingPeriod.first; |
121 | 0 | } |
122 | | |
123 | | // check that the observation lag of the swap |
124 | | // is compatible with the availability lag of the index AND |
125 | | // it's interpolation (assuming the start day is spot) |
126 | 0 | if (detail::CPI::isInterpolated(observationInterpolation_)) { |
127 | 0 | Period pShift(zii_->frequency()); |
128 | 0 | QL_REQUIRE(swapObsLag_ - pShift >= zii_->availabilityLag(), |
129 | 0 | "inconsistency between swap observation lag " |
130 | 0 | << swapObsLag_ << ", index period " << pShift << " and index availability " |
131 | 0 | << zii_->availabilityLag() << ": need (obsLag-index period) >= availLag"); |
132 | 0 | } |
133 | | |
134 | 0 | registerWith(zii_); |
135 | 0 | ZeroCouponInflationSwapHelper::initializeDates(); |
136 | 0 | } |
137 | | |
138 | | QL_DEPRECATED_ENABLE_WARNING |
139 | | |
140 | | |
141 | 0 | Real ZeroCouponInflationSwapHelper::impliedQuote() const { |
142 | 0 | zciis_->deepUpdate(); |
143 | 0 | return zciis_->fairRate(); |
144 | 0 | } |
145 | | |
146 | 0 | void ZeroCouponInflationSwapHelper::initializeDates() { |
147 | 0 | zciis_ = ext::make_shared<ZeroCouponInflationSwap>( |
148 | 0 | Swap::Payer, 1.0, updateDates_ ? evaluationDate_ : startDate_, maturity_, calendar_, |
149 | 0 | paymentConvention_, dayCounter_, 0.0, zii_, swapObsLag_, |
150 | 0 | observationInterpolation_); |
151 | | |
152 | | // The instrument takes a standard discounting swap engine. |
153 | | // The inflation-related work is done by the coupons. |
154 | | |
155 | | // Any nominal term structure will give the same result; |
156 | | // when calculating the fair rate, the equal discount factors |
157 | | // for the payments on the two legs will cancel out. |
158 | |
|
159 | 0 | auto null_nominal_curve = |
160 | 0 | Handle<YieldTermStructure>(ext::make_shared<FlatForward>(0, NullCalendar(), 0.0, dayCounter_)); |
161 | |
|
162 | 0 | zciis_->setPricingEngine( |
163 | 0 | ext::make_shared<DiscountingSwapEngine>(null_nominal_curve)); |
164 | 0 | } |
165 | | |
166 | 0 | void ZeroCouponInflationSwapHelper::setTermStructure(ZeroInflationTermStructure* z) { |
167 | | // do not set the relinkable handle as an observer - |
168 | | // force recalculation when needed |
169 | 0 | bool observer = false; |
170 | |
|
171 | 0 | ext::shared_ptr<ZeroInflationTermStructure> temp(z, null_deleter()); |
172 | 0 | termStructureHandle_.linkTo(std::move(temp), observer); |
173 | |
|
174 | 0 | RelativeDateBootstrapHelper<ZeroInflationTermStructure>::setTermStructure(z); |
175 | 0 | } |
176 | | |
177 | | |
178 | | YearOnYearInflationSwapHelper::YearOnYearInflationSwapHelper( |
179 | | const Handle<Quote>& quote, |
180 | | const Period& swapObsLag, |
181 | | const Date& maturity, |
182 | | Calendar calendar, |
183 | | BusinessDayConvention paymentConvention, |
184 | | DayCounter dayCounter, |
185 | | const ext::shared_ptr<YoYInflationIndex>& yii, |
186 | | CPI::InterpolationType interpolation, |
187 | | Handle<YieldTermStructure> nominalTermStructure, |
188 | | Pillar::Choice pillar, |
189 | | Date customPillarDate) |
190 | 0 | : YearOnYearInflationSwapHelper( |
191 | 0 | quote, swapObsLag, Date(), maturity, std::move(calendar), paymentConvention, |
192 | 0 | std::move(dayCounter), yii, interpolation, std::move(nominalTermStructure), |
193 | 0 | pillar, customPillarDate) {} |
194 | | |
195 | | YearOnYearInflationSwapHelper::YearOnYearInflationSwapHelper( |
196 | | const Handle<Quote>& quote, |
197 | | const Period& swapObsLag, |
198 | | const Date& startDate, |
199 | | const Date& endDate, |
200 | | Calendar calendar, |
201 | | BusinessDayConvention paymentConvention, |
202 | | DayCounter dayCounter, |
203 | | const ext::shared_ptr<YoYInflationIndex>& yii, |
204 | | CPI::InterpolationType interpolation, |
205 | | Handle<YieldTermStructure> nominalTermStructure, |
206 | | Pillar::Choice pillar, |
207 | | Date customPillarDate) |
208 | 0 | : RelativeDateBootstrapHelper<YoYInflationTermStructure>(quote, startDate == Date()), |
209 | 0 | swapObsLag_(swapObsLag), startDate_(startDate), maturity_(endDate), |
210 | 0 | calendar_(std::move(calendar)), paymentConvention_(paymentConvention), |
211 | 0 | dayCounter_(std::move(dayCounter)), interpolation_(interpolation), |
212 | 0 | pillarChoice_(pillar), |
213 | 0 | nominalTermStructure_(std::move(nominalTermStructure)) { |
214 | 0 | yii_ = yii->clone(termStructureHandle_); |
215 | | // We want to be notified of changes of fixings, but we don't |
216 | | // want notifications from termStructureHandle_ (they would |
217 | | // interfere with bootstrapping.) |
218 | 0 | yii_->unregisterWith(termStructureHandle_); |
219 | |
|
220 | 0 | auto fixingPeriod = inflationPeriod(maturity_ - swapObsLag_, yii_->frequency()); |
221 | |
|
222 | 0 | if (detail::CPI::isInterpolated(interpolation_, yii_)) { |
223 | 0 | earliestDate_ = fixingPeriod.first; |
224 | 0 | latestDate_ = fixingPeriod.second + 1; |
225 | |
|
226 | 0 | switch (pillarChoice_) { |
227 | 0 | case Pillar::MaturityDate: |
228 | 0 | pillarDate_ = latestDate_; |
229 | 0 | break; |
230 | 0 | case Pillar::LastRelevantDate: |
231 | 0 | { |
232 | 0 | Date weightDate = startDate_ != Date() ? startDate_ : maturity_; |
233 | 0 | auto weightPeriod = inflationPeriod(weightDate, yii_->frequency()); |
234 | 0 | Real dp = (weightPeriod.second + 1) - weightPeriod.first; |
235 | 0 | Real dt = weightDate - weightPeriod.first; |
236 | 0 | if (dt / dp <= 0.5) |
237 | 0 | pillarDate_ = fixingPeriod.first; |
238 | 0 | } |
239 | 0 | break; |
240 | 0 | case Pillar::CustomDate: |
241 | 0 | pillarDate_ = customPillarDate; |
242 | 0 | QL_REQUIRE(pillarDate_ >= earliestDate_, |
243 | 0 | "pillar date (" << pillarDate_ << ") must be later " |
244 | 0 | "than or equal to the instrument's earliest date (" |
245 | 0 | << earliestDate_ << ")"); |
246 | 0 | QL_REQUIRE(pillarDate_ <= latestDate_, |
247 | 0 | "pillar date (" << pillarDate_ << ") must be before " |
248 | 0 | "or equal to the instrument's latest relevant date (" |
249 | 0 | << latestDate_ << ")"); |
250 | 0 | break; |
251 | 0 | default: |
252 | 0 | QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")"); |
253 | 0 | } |
254 | 0 | } else { |
255 | | // Not interpolated: only the left node matters, so |
256 | | // earliestDate_ == latestDate_ and pillarChoice_ is |
257 | | // irrelevant (there is only one possible pillar). |
258 | 0 | earliestDate_ = fixingPeriod.first; |
259 | 0 | latestDate_ = fixingPeriod.first; |
260 | 0 | } |
261 | | |
262 | | // check that the observation lag of the swap |
263 | | // is compatible with the availability lag of the index AND |
264 | | // its interpolation (assuming the start day is spot) |
265 | 0 | if (detail::CPI::isInterpolated(interpolation_, yii_)) { |
266 | 0 | Period pShift(yii_->frequency()); |
267 | 0 | QL_REQUIRE(swapObsLag_ - pShift >= yii_->availabilityLag(), |
268 | 0 | "inconsistency between swap observation lag " |
269 | 0 | << swapObsLag_ << ", index period " << pShift << " and index availability " |
270 | 0 | << yii_->availabilityLag() << ": need (obsLag-index period) >= availLag"); |
271 | 0 | } |
272 | | |
273 | 0 | registerWith(yii_); |
274 | 0 | registerWith(nominalTermStructure_); |
275 | 0 | YearOnYearInflationSwapHelper::initializeDates(); |
276 | 0 | } |
277 | | |
278 | 0 | Real YearOnYearInflationSwapHelper::impliedQuote() const { |
279 | 0 | yyiis_->deepUpdate(); |
280 | 0 | return yyiis_->fairRate(); |
281 | 0 | } |
282 | | |
283 | 0 | void YearOnYearInflationSwapHelper::initializeDates() { |
284 | | // always works because tenor is always 1 year so |
285 | | // no problem with different days-in-month |
286 | 0 | Schedule fixedSchedule = MakeSchedule() |
287 | 0 | .from(updateDates_ ? evaluationDate_ : startDate_) |
288 | 0 | .to(maturity_) |
289 | 0 | .withTenor(1 * Years) |
290 | 0 | .withConvention(Unadjusted) |
291 | 0 | .withCalendar(calendar_) // fixed leg gets cal from sched |
292 | 0 | .backwards(); |
293 | 0 | const Schedule& yoySchedule = fixedSchedule; |
294 | |
|
295 | 0 | yyiis_ = ext::make_shared<YearOnYearInflationSwap>( |
296 | 0 | Swap::Payer, 1.0, fixedSchedule, 0.0, dayCounter_, |
297 | 0 | yoySchedule, yii_, swapObsLag_, interpolation_, |
298 | 0 | 0.0, dayCounter_, calendar_, paymentConvention_); |
299 | | |
300 | | // The instrument takes a standard discounting swap engine. |
301 | | // The inflation-related work is done by the coupons. |
302 | 0 | yyiis_->setPricingEngine( |
303 | 0 | ext::make_shared<DiscountingSwapEngine>(nominalTermStructure_)); |
304 | 0 | } |
305 | | |
306 | 0 | void YearOnYearInflationSwapHelper::setTermStructure(YoYInflationTermStructure* y) { |
307 | | // do not set the relinkable handle as an observer - |
308 | | // force recalculation when needed |
309 | 0 | bool observer = false; |
310 | |
|
311 | 0 | ext::shared_ptr<YoYInflationTermStructure> temp(y, null_deleter()); |
312 | 0 | termStructureHandle_.linkTo(std::move(temp), observer); |
313 | |
|
314 | 0 | RelativeDateBootstrapHelper<YoYInflationTermStructure>::setTermStructure(y); |
315 | 0 | } |
316 | | |
317 | | } |