• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lballabio / QuantLib / 23016336703

12 Mar 2026 05:56PM UTC coverage: 74.223% (-0.04%) from 74.259%
23016336703

Pull #2368

github

web-flow
Merge 19a597aeb into 0a02cced0
Pull Request #2368: Fx options utils - `BlackVolatilitySurfaceDelta` class

113 of 195 new or added lines in 5 files covered. (57.95%)

220 existing lines in 21 files now uncovered.

57858 of 77952 relevant lines covered (74.22%)

8739234.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.03
/ql/termstructures/inflation/inflationhelpers.cpp
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(
162✔
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)
162✔
45
    : ZeroCouponInflationSwapHelper(
46
        quote, swapObsLag, Date(), maturity, std::move(calendar), paymentConvention,
324✔
47
        dayCounter, zii, observationInterpolation,
48
        Handle<YieldTermStructure>(ext::make_shared<FlatForward>(0, NullCalendar(), 0.0, dayCounter)),
324✔
49
        pillar, customPillarDate) {}
486✔
50

51
    ZeroCouponInflationSwapHelper::ZeroCouponInflationSwapHelper(
2,344✔
52
        const Handle<Quote>& quote,
53
        const Period& swapObsLag,
54
        const Date& startDate,
55
        const Date& endDate,
56
        Calendar calendar,
57
        BusinessDayConvention paymentConvention,
58
        const DayCounter& dayCounter,
59
        const ext::shared_ptr<ZeroInflationIndex>& zii,
60
        CPI::InterpolationType observationInterpolation,
61
        Pillar::Choice pillar,
62
        Date customPillarDate)
2,344✔
63
    : ZeroCouponInflationSwapHelper(
64
        quote, swapObsLag, startDate, endDate, std::move(calendar), paymentConvention,
65
        dayCounter, zii, observationInterpolation,
66
        // any nominal term structure will give the same result;
67
        // when calculating the fair rate, the equal discount factors
68
        // for the payments on the two legs will cancel out.
69
        Handle<YieldTermStructure>(ext::make_shared<FlatForward>(0, NullCalendar(), 0.0, dayCounter)),
4,688✔
70
        pillar, customPillarDate) {}
7,032✔
71

72
    ZeroCouponInflationSwapHelper::ZeroCouponInflationSwapHelper(
14✔
73
        const Handle<Quote>& quote,
74
        const Period& swapObsLag,
75
        const Date& maturity,
76
        Calendar calendar,
77
        BusinessDayConvention paymentConvention,
78
        DayCounter dayCounter,
79
        const ext::shared_ptr<ZeroInflationIndex>& zii,
80
        CPI::InterpolationType observationInterpolation,
81
        Handle<YieldTermStructure> nominalTermStructure)
14✔
82
    : ZeroCouponInflationSwapHelper(
83
        quote, swapObsLag, Date(), maturity, std::move(calendar), paymentConvention,
28✔
84
        std::move(dayCounter), zii, observationInterpolation, std::move(nominalTermStructure),
85
        Pillar::LastRelevantDate, Date()) {}
42✔
86

87
    ZeroCouponInflationSwapHelper::ZeroCouponInflationSwapHelper(
2,520✔
88
        const Handle<Quote>& quote,
89
        const Period& swapObsLag,
90
        const Date& startDate,
91
        const Date& endDate,
92
        Calendar calendar,
93
        BusinessDayConvention paymentConvention,
94
        DayCounter dayCounter,
95
        const ext::shared_ptr<ZeroInflationIndex>& zii,
96
        CPI::InterpolationType observationInterpolation,
97
        Handle<YieldTermStructure> nominalTermStructure,
98
        Pillar::Choice pillar,
99
        Date customPillarDate)
2,520✔
100
    : RelativeDateBootstrapHelper<ZeroInflationTermStructure>(quote, startDate == Date()),
5,040✔
101
      swapObsLag_(swapObsLag), startDate_(startDate), maturity_(endDate),
2,520✔
102
      calendar_(std::move(calendar)), paymentConvention_(paymentConvention),
2,520✔
103
      dayCounter_(std::move(dayCounter)), observationInterpolation_(observationInterpolation),
2,520✔
104
      pillarChoice_(pillar),
2,520✔
105
      nominalTermStructure_(std::move(nominalTermStructure)) {
5,040✔
106
        zii_ = zii->clone(termStructureHandle_);
2,520✔
107
        // We want to be notified of changes of fixings, but we don't
108
        // want notifications from termStructureHandle_ (they would
109
        // interfere with bootstrapping.)
110
        zii_->unregisterWith(termStructureHandle_);
2,520✔
111

112
        auto fixingPeriod = inflationPeriod(maturity_ - swapObsLag_, zii_->frequency());
2,520✔
113

114
        if (detail::CPI::isInterpolated(observationInterpolation_)) {
2,520✔
115
            earliestDate_ = fixingPeriod.first;
1,784✔
116
            latestDate_ = fixingPeriod.second + 1;
1,784✔
117

118
            switch (pillarChoice_) {
1,784✔
119
              case Pillar::MaturityDate:
×
UNCOV
120
                pillarDate_ = latestDate_;
×
UNCOV
121
                break;
×
122
              case Pillar::LastRelevantDate:
1,784✔
123
                // Assign the pillar to the node with the dominant
124
                // interpolation weight.  We use startDate_ (the swap
125
                // effective date) rather than maturity_ so that all
126
                // helpers sharing the same effective date make the same
127
                // LEFT/RIGHT decision.  Using maturity_ would cause
128
                // collisions when consecutive helpers mature in months
129
                // of different length (e.g. February vs March) and the
130
                // day-of-month is near the mid-month threshold.
131
                {
132
                    Date weightDate = startDate_ != Date() ? startDate_ : maturity_;
1,784✔
133
                    auto weightPeriod = inflationPeriod(weightDate, zii_->frequency());
1,784✔
134
                    Real dp = (weightPeriod.second + 1) - weightPeriod.first;
1,784✔
135
                    Real dt = weightDate - weightPeriod.first;
1,784✔
136
                    if (dt / dp <= 0.5)
1,784✔
137
                        pillarDate_ = fixingPeriod.first;
916✔
138
                }
139
                break;
1,784✔
UNCOV
140
              case Pillar::CustomDate:
×
UNCOV
141
                pillarDate_ = customPillarDate;
×
UNCOV
142
                QL_REQUIRE(pillarDate_ >= earliestDate_,
×
143
                           "pillar date (" << pillarDate_ << ") must be later "
144
                           "than or equal to the instrument's earliest date ("
145
                           << earliestDate_ << ")");
UNCOV
146
                QL_REQUIRE(pillarDate_ <= latestDate_,
×
147
                           "pillar date (" << pillarDate_ << ") must be before "
148
                           "or equal to the instrument's latest relevant date ("
149
                           << latestDate_ << ")");
150
                break;
UNCOV
151
              default:
×
UNCOV
152
                QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
×
153
            }
154
        } else {
155
            // Not interpolated: only the left node matters, so
156
            // earliestDate_ == latestDate_ and pillarChoice_ is
157
            // irrelevant (there is only one possible pillar).
158
            earliestDate_ = fixingPeriod.first;
736✔
159
            latestDate_ = fixingPeriod.first;
736✔
160
        }
161

162
        // check that the observation lag of the swap
163
        // is compatible with the availability lag of the index AND
164
        // it's interpolation (assuming the start day is spot)
165
        if (detail::CPI::isInterpolated(observationInterpolation_)) {
2,520✔
166
            Period pShift(zii_->frequency());
1,784✔
167
            QL_REQUIRE(swapObsLag_ - pShift >= zii_->availabilityLag(),
3,568✔
168
                       "inconsistency between swap observation lag "
169
                           << swapObsLag_ << ", index period " << pShift << " and index availability "
170
                           << zii_->availabilityLag() << ": need (obsLag-index period) >= availLag");
171
        }
172

173
        registerWith(zii_);
5,040✔
174
        registerWith(nominalTermStructure_);
2,520✔
175
        ZeroCouponInflationSwapHelper::initializeDates();
2,520✔
176
    }
2,520✔
177

178
    QL_DEPRECATED_ENABLE_WARNING
179

180

181
    Real ZeroCouponInflationSwapHelper::impliedQuote() const {
108,635✔
182
        zciis_->deepUpdate();
108,635✔
183
        return zciis_->fairRate();
108,635✔
184
    }
185

186
    void ZeroCouponInflationSwapHelper::initializeDates() {
2,521✔
187
        zciis_ = ext::make_shared<ZeroCouponInflationSwap>(
2,521✔
188
            Swap::Payer, 1.0, updateDates_ ? evaluationDate_ : startDate_, maturity_, calendar_,
2,521✔
189
            paymentConvention_, dayCounter_, 0.0, zii_, swapObsLag_,
5,042✔
190
            observationInterpolation_);
5,042✔
191
        // The instrument takes a standard discounting swap engine.
192
        // The inflation-related work is done by the coupons.
193
        zciis_->setPricingEngine(
7,563✔
194
            ext::make_shared<DiscountingSwapEngine>(nominalTermStructure_));
2,521✔
195
    }
2,521✔
196

197
    void ZeroCouponInflationSwapHelper::setTermStructure(ZeroInflationTermStructure* z) {
3,155✔
198
        // do not set the relinkable handle as an observer -
199
        // force recalculation when needed
200
        bool observer = false;
201

202
        ext::shared_ptr<ZeroInflationTermStructure> temp(z, null_deleter());
203
        termStructureHandle_.linkTo(std::move(temp), observer);
3,155✔
204

205
        RelativeDateBootstrapHelper<ZeroInflationTermStructure>::setTermStructure(z);
3,155✔
206
    }
3,155✔
207

208

209
    YearOnYearInflationSwapHelper::YearOnYearInflationSwapHelper(
165✔
210
        const Handle<Quote>& quote,
211
        const Period& swapObsLag,
212
        const Date& maturity,
213
        Calendar calendar,
214
        BusinessDayConvention paymentConvention,
215
        DayCounter dayCounter,
216
        const ext::shared_ptr<YoYInflationIndex>& yii,
217
        CPI::InterpolationType interpolation,
218
        Handle<YieldTermStructure> nominalTermStructure,
219
        Pillar::Choice pillar,
220
        Date customPillarDate)
165✔
221
    : YearOnYearInflationSwapHelper(
222
        quote, swapObsLag, Date(), maturity, std::move(calendar), paymentConvention,
330✔
223
        std::move(dayCounter), yii, interpolation, std::move(nominalTermStructure),
224
        pillar, customPillarDate) {}
330✔
225

226
    YearOnYearInflationSwapHelper::YearOnYearInflationSwapHelper(
165✔
227
        const Handle<Quote>& quote,
228
        const Period& swapObsLag,
229
        const Date& startDate,
230
        const Date& endDate,
231
        Calendar calendar,
232
        BusinessDayConvention paymentConvention,
233
        DayCounter dayCounter,
234
        const ext::shared_ptr<YoYInflationIndex>& yii,
235
        CPI::InterpolationType interpolation,
236
        Handle<YieldTermStructure> nominalTermStructure,
237
        Pillar::Choice pillar,
238
        Date customPillarDate)
165✔
239
    : RelativeDateBootstrapHelper<YoYInflationTermStructure>(quote, startDate == Date()),
330✔
240
      swapObsLag_(swapObsLag), startDate_(startDate), maturity_(endDate),
165✔
241
      calendar_(std::move(calendar)), paymentConvention_(paymentConvention),
165✔
242
      dayCounter_(std::move(dayCounter)), interpolation_(interpolation),
165✔
243
      pillarChoice_(pillar),
165✔
244
      nominalTermStructure_(std::move(nominalTermStructure)) {
330✔
245
        yii_ = yii->clone(termStructureHandle_);
165✔
246
        // We want to be notified of changes of fixings, but we don't
247
        // want notifications from termStructureHandle_ (they would
248
        // interfere with bootstrapping.)
249
        yii_->unregisterWith(termStructureHandle_);
165✔
250

251
        auto fixingPeriod = inflationPeriod(maturity_ - swapObsLag_, yii_->frequency());
165✔
252

253
        if (detail::CPI::isInterpolated(interpolation_, yii_)) {
165✔
254
            earliestDate_ = fixingPeriod.first;
60✔
255
            latestDate_ = fixingPeriod.second + 1;
60✔
256

257
            switch (pillarChoice_) {
60✔
UNCOV
258
              case Pillar::MaturityDate:
×
UNCOV
259
                pillarDate_ = latestDate_;
×
UNCOV
260
                break;
×
261
              case Pillar::LastRelevantDate:
60✔
262
                {
263
                    Date weightDate = startDate_ != Date() ? startDate_ : maturity_;
60✔
264
                    auto weightPeriod = inflationPeriod(weightDate, yii_->frequency());
60✔
265
                    Real dp = (weightPeriod.second + 1) - weightPeriod.first;
60✔
266
                    Real dt = weightDate - weightPeriod.first;
60✔
267
                    if (dt / dp <= 0.5)
60✔
UNCOV
268
                        pillarDate_ = fixingPeriod.first;
×
269
                }
270
                break;
60✔
UNCOV
271
              case Pillar::CustomDate:
×
UNCOV
272
                pillarDate_ = customPillarDate;
×
UNCOV
273
                QL_REQUIRE(pillarDate_ >= earliestDate_,
×
274
                           "pillar date (" << pillarDate_ << ") must be later "
275
                           "than or equal to the instrument's earliest date ("
276
                           << earliestDate_ << ")");
UNCOV
277
                QL_REQUIRE(pillarDate_ <= latestDate_,
×
278
                           "pillar date (" << pillarDate_ << ") must be before "
279
                           "or equal to the instrument's latest relevant date ("
280
                           << latestDate_ << ")");
281
                break;
UNCOV
282
              default:
×
UNCOV
283
                QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
×
284
            }
285
        } else {
286
            // Not interpolated: only the left node matters, so
287
            // earliestDate_ == latestDate_ and pillarChoice_ is
288
            // irrelevant (there is only one possible pillar).
289
            earliestDate_ = fixingPeriod.first;
105✔
290
            latestDate_ = fixingPeriod.first;
105✔
291
        }
292

293
        // check that the observation lag of the swap
294
        // is compatible with the availability lag of the index AND
295
        // its interpolation (assuming the start day is spot)
296
        if (detail::CPI::isInterpolated(interpolation_, yii_)) {
165✔
297
            Period pShift(yii_->frequency());
60✔
298
            QL_REQUIRE(swapObsLag_ - pShift >= yii_->availabilityLag(),
120✔
299
                       "inconsistency between swap observation lag "
300
                       << swapObsLag_ << ", index period " << pShift << " and index availability "
301
                       << yii_->availabilityLag() << ": need (obsLag-index period) >= availLag");
302
        }
303

304
        registerWith(yii_);
330✔
305
        registerWith(nominalTermStructure_);
165✔
306
        YearOnYearInflationSwapHelper::initializeDates();
165✔
307
    }
165✔
308

309
    Real YearOnYearInflationSwapHelper::impliedQuote() const {
970✔
310
        yyiis_->deepUpdate();
970✔
311
        return yyiis_->fairRate();
970✔
312
    }
313

314
    void YearOnYearInflationSwapHelper::initializeDates() {
165✔
315
        // always works because tenor is always 1 year so
316
        // no problem with different days-in-month
317
        Schedule fixedSchedule = MakeSchedule()
165✔
318
                                     .from(updateDates_ ? evaluationDate_ : startDate_)
165✔
319
                                     .to(maturity_)
165✔
320
                                     .withTenor(1 * Years)
330✔
321
                                     .withConvention(Unadjusted)
165✔
322
                                     .withCalendar(calendar_) // fixed leg gets cal from sched
165✔
323
                                     .backwards();
165✔
324
        const Schedule& yoySchedule = fixedSchedule;
325

326
        yyiis_ = ext::make_shared<YearOnYearInflationSwap>(
330✔
327
            Swap::Payer, 1.0, fixedSchedule, 0.0, dayCounter_,
330✔
328
            yoySchedule, yii_, swapObsLag_, interpolation_,
165✔
329
            0.0, dayCounter_, calendar_, paymentConvention_);
165✔
330

331
        // The instrument takes a standard discounting swap engine.
332
        // The inflation-related work is done by the coupons.
333
        yyiis_->setPricingEngine(
495✔
334
            ext::make_shared<DiscountingSwapEngine>(nominalTermStructure_));
165✔
335
    }
165✔
336

337
    void YearOnYearInflationSwapHelper::setTermStructure(YoYInflationTermStructure* y) {
165✔
338
        // do not set the relinkable handle as an observer -
339
        // force recalculation when needed
340
        bool observer = false;
341

342
        ext::shared_ptr<YoYInflationTermStructure> temp(y, null_deleter());
343
        termStructureHandle_.linkTo(std::move(temp), observer);
165✔
344

345
        RelativeDateBootstrapHelper<YoYInflationTermStructure>::setTermStructure(y);
165✔
346
    }
165✔
347

348
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc