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

lballabio / QuantLib / 27546532329

15 Jun 2026 12:33PM UTC coverage: 74.835% (+0.04%) from 74.797%
27546532329

Pull #2619

github

web-flow
Merge d61d3f9fe into c06232d06
Pull Request #2619: Use make_shared in volatility-related code

53 of 71 new or added lines in 22 files covered. (74.65%)

72 existing lines in 3 files now uncovered.

59236 of 79156 relevant lines covered (74.83%)

8626822.15 hits per line

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

76.32
/ql/termstructures/yield/ratehelpers.cpp
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2

3
/*
4
 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
5
 Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 StatPro Italia srl
6
 Copyright (C) 2007, 2008, 2009, 2015 Ferdinando Ametrano
7
 Copyright (C) 2007, 2009 Roland Lichters
8
 Copyright (C) 2015 Maddalena Zanzi
9
 Copyright (C) 2015 Paolo Mazzocchi
10
 Copyright (C) 2018 Matthias Lungwitz
11

12
 This file is part of QuantLib, a free-software/open-source library
13
 for financial quantitative analysts and developers - http://quantlib.org/
14

15
 QuantLib is free software: you can redistribute it and/or modify it
16
 under the terms of the QuantLib license.  You should have received a
17
 copy of the license along with this program; if not, please email
18
 <quantlib-dev@lists.sf.net>. The license is also available online at
19
 <https://www.quantlib.org/license.shtml>.
20

21
 This program is distributed in the hope that it will be useful, but WITHOUT
22
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23
 FOR A PARTICULAR PURPOSE.  See the license for more details.
24
*/
25

26
#include <ql/cashflows/iborcoupon.hpp>
27
#include <ql/currency.hpp>
28
#include <ql/indexes/swapindex.hpp>
29
#include <ql/instruments/makevanillaswap.hpp>
30
#include <ql/instruments/simplifynotificationgraph.hpp>
31
#include <ql/optional.hpp>
32
#include <ql/pricingengines/swap/discountingswapengine.hpp>
33
#include <ql/quote.hpp>
34
#include <ql/termstructures/yield/ratehelpers.hpp>
35
#include <ql/time/asx.hpp>
36
#include <ql/time/calendars/jointcalendar.hpp>
37
#include <ql/time/imm.hpp>
38
#include <ql/utilities/null_deleter.hpp>
39
#include <utility>
40
#include <ql/cashflows/couponpricer.hpp>
41

42
namespace QuantLib {
43

44
    namespace {
45

46
        void CheckDate(const Date& date, const Futures::Type type) {
153✔
47
            switch (type) {
153✔
48
              case Futures::IMM:
78✔
49
                QL_REQUIRE(IMM::isIMMdate(date, false), date << " is not a valid IMM date");
78✔
50
                break;
51
              case Futures::ASX:
72✔
52
                QL_REQUIRE(ASX::isASXdate(date, false), date << " is not a valid ASX date");
72✔
53
                break;
54
              case Futures::Custom:
55
                break;
56
              default:
×
UNCOV
57
                QL_FAIL("unknown futures type (" << type << ')');
×
58
            }
59
        }
153✔
60

61
        Time DetermineYearFraction(const Date& earliestDate,
153✔
62
                                   const Date& maturityDate,
63
                                   const DayCounter& dayCounter) {
64
            return dayCounter.yearFraction(earliestDate, maturityDate,
153✔
65
                                           earliestDate, maturityDate);
153✔
66
        }
67

68
    } // namespace
69

70
    FuturesRateHelper::FuturesRateHelper(const std::variant<Real, Handle<Quote>>& price,
1✔
71
                                         const Date& iborStartDate,
72
                                         Natural lengthInMonths,
73
                                         const Calendar& calendar,
74
                                         BusinessDayConvention convention,
75
                                         bool endOfMonth,
76
                                         const DayCounter& dayCounter,
77
                                         const std::variant<Real, Handle<Quote>>& convAdj,
78
                                         Futures::Type type)
1✔
79
    : RateHelper(price), convAdj_(handleFromVariant(convAdj)) {
1✔
80
        CheckDate(iborStartDate, type);
1✔
81

82
        earliestDate_ = iborStartDate;
1✔
83
        maturityDate_ =
84
            calendar.advance(iborStartDate, lengthInMonths * Months, convention, endOfMonth);
1✔
85
        yearFraction_ = DetermineYearFraction(earliestDate_, maturityDate_, dayCounter);
1✔
86
        pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
1✔
87

88
        registerWith(convAdj_);
1✔
89
    }
1✔
90

91
    FuturesRateHelper::FuturesRateHelper(const std::variant<Real, Handle<Quote>>& price,
1✔
92
                                         const Date& iborStartDate,
93
                                         const Date& iborEndDate,
94
                                         const DayCounter& dayCounter,
95
                                         const std::variant<Real, Handle<Quote>>& convAdj,
96
                                         Futures::Type type)
1✔
97
    : RateHelper(price), convAdj_(handleFromVariant(convAdj)) {
1✔
98
        CheckDate(iborStartDate, type);
1✔
99

100
        const auto determineMaturityDate =
101
            [&iborStartDate, &iborEndDate](const auto nextDateCalculator) -> Date {
×
102
                Date maturityDate;
×
UNCOV
103
                if (iborEndDate == Date()) {
×
104
                    // advance 3 months
105
                    maturityDate = nextDateCalculator(iborStartDate);
×
106
                    maturityDate = nextDateCalculator(maturityDate);
×
UNCOV
107
                    maturityDate = nextDateCalculator(maturityDate);
×
108
                } else {
UNCOV
109
                    QL_REQUIRE(iborEndDate > iborStartDate,
×
110
                               "end date (" << iborEndDate << ") must be greater than start date ("
111
                                            << iborStartDate << ')');
UNCOV
112
                    maturityDate = iborEndDate;
×
113
                }
UNCOV
114
                return maturityDate;
×
115
            };
1✔
116

117
        switch (type) {
1✔
118
          case Futures::IMM:
×
119
            maturityDate_ = determineMaturityDate(
×
UNCOV
120
                [](const Date date) -> Date { return IMM::nextDate(date, false); });
×
121
            break;
122
          case Futures::ASX:
×
123
            maturityDate_ = determineMaturityDate(
×
UNCOV
124
                [](const Date date) -> Date { return ASX::nextDate(date, false); });
×
125
            break;
126
          case Futures::Custom:
1✔
127
            maturityDate_ = iborEndDate;
1✔
128
            break;
1✔
129
          default:
×
UNCOV
130
            QL_FAIL("unsupported futures type (" << type << ')');
×
131
        }
132
        earliestDate_ = iborStartDate;
1✔
133
        yearFraction_ = DetermineYearFraction(earliestDate_, maturityDate_, dayCounter);
1✔
134
        pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
1✔
135

136
        registerWith(convAdj_);
1✔
137
    }
1✔
138

139
    FuturesRateHelper::FuturesRateHelper(const std::variant<Real, Handle<Quote>>& price,
151✔
140
                                         const Date& iborStartDate,
141
                                         const ext::shared_ptr<IborIndex>& index,
142
                                         const std::variant<Real, Handle<Quote>>& convAdj,
143
                                         Futures::Type type)
151✔
144
    : RateHelper(price), convAdj_(handleFromVariant(convAdj)) {
151✔
145
        CheckDate(iborStartDate, type);
151✔
146

147
        earliestDate_ = iborStartDate;
151✔
148
        const Calendar& cal = index->fixingCalendar();
151✔
149
        maturityDate_ =
150
            cal.advance(iborStartDate, index->tenor(), index->businessDayConvention());
151✔
151
        yearFraction_ = DetermineYearFraction(earliestDate_, maturityDate_, index->dayCounter());
151✔
152
        pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
151✔
153

154
        registerWith(convAdj_);
302✔
155
    }
151✔
156

157
    Real FuturesRateHelper::impliedQuote() const {
1,428✔
158
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
1,428✔
159
        Rate forwardRate = (termStructure_->discount(earliestDate_) /
1,428✔
160
            termStructure_->discount(maturityDate_) - 1.0) / yearFraction_;
1,428✔
161
        // Convexity, as FRA/futures adjustment, has been used in the
162
        // past to take into account futures margining vs FRA.
163
        // Therefore, there's no requirement for it to be non-negative.
164
        Rate futureRate = forwardRate + convexityAdjustment();
1,428✔
165
        return 100.0 * (1.0 - futureRate);
1,428✔
166
    }
167

168
    Real FuturesRateHelper::convexityAdjustment() const {
1,428✔
169
        return convAdj_.empty() ? 0.0 : convAdj_->value();
1,428✔
170
    }
171

172
    void FuturesRateHelper::accept(AcyclicVisitor& v) {
×
173
        auto* v1 = dynamic_cast<Visitor<FuturesRateHelper>*>(&v);
×
174
        if (v1 != nullptr)
×
UNCOV
175
            v1->visit(*this);
×
176
        else
177
            RateHelper::accept(v);
×
UNCOV
178
    }
×
179

180
    DepositRateHelper::DepositRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
305✔
181
                                         const Period& tenor,
182
                                         Natural fixingDays,
183
                                         const Calendar& calendar,
184
                                         BusinessDayConvention convention,
185
                                         bool endOfMonth,
186
                                         const DayCounter& dayCounter)
305✔
187
    : RelativeDateRateHelper(rate) {
305✔
188
        iborIndex_ = ext::make_shared<IborIndex>("no-fix", // never take fixing into account
610✔
189
                      tenor, fixingDays,
190
                      Currency(), calendar, convention,
305✔
191
                      endOfMonth, dayCounter, termStructureHandle_);
305✔
192
        DepositRateHelper::initializeDates();
305✔
193
    }
305✔
194

195
    DepositRateHelper::DepositRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
144✔
196
                                         const ext::shared_ptr<IborIndex>& i)
144✔
197
    : RelativeDateRateHelper(rate) {
144✔
198
        iborIndex_ = i->clone(termStructureHandle_);
144✔
199
        DepositRateHelper::initializeDates();
144✔
200
    }
144✔
201

202
    DepositRateHelper::DepositRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
6✔
203
                                         Date fixingDate,
204
                                         const ext::shared_ptr<IborIndex>& i)
6✔
205
    : RelativeDateRateHelper(rate, false), fixingDate_(fixingDate) {
6✔
206
        iborIndex_ = i->clone(termStructureHandle_);
6✔
207
        DepositRateHelper::initializeDates();
6✔
208
    }
6✔
209

210
    Real DepositRateHelper::impliedQuote() const {
6,439✔
211
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
6,439✔
212
        // the forecast fixing flag is set to true because
213
        // we do not want to take fixing into account
214
        return iborIndex_->fixing(fixingDate_, true);
6,439✔
215
    }
216

217
    void DepositRateHelper::setTermStructure(YieldTermStructure* t) {
570✔
218
        // do not set the relinkable handle as an observer -
219
        // force recalculation when needed---the index is not lazy
220
        bool observer = false;
221

222
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
223
        termStructureHandle_.linkTo(temp, observer);
570✔
224

225
        RelativeDateRateHelper::setTermStructure(t);
570✔
226
    }
570✔
227

228
    void DepositRateHelper::initializeDates() {
482✔
229
        if (updateDates_) {
482✔
230
            // if the evaluation date is not a business day
231
            // then move to the next business day
232
            Date referenceDate =
233
                iborIndex_->fixingCalendar().adjust(evaluationDate_);
476✔
234
            earliestDate_ = iborIndex_->valueDate(referenceDate);
476✔
235
            fixingDate_ = iborIndex_->fixingDate(earliestDate_);
476✔
236
        } else {
237
            earliestDate_ = iborIndex_->valueDate(fixingDate_);
6✔
238
        }
239
        maturityDate_ = iborIndex_->maturityDate(earliestDate_);
482✔
240
        pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
482✔
241
    }
482✔
242

243
    void DepositRateHelper::accept(AcyclicVisitor& v) {
×
244
        auto* v1 = dynamic_cast<Visitor<DepositRateHelper>*>(&v);
×
245
        if (v1 != nullptr)
×
UNCOV
246
            v1->visit(*this);
×
247
        else
248
            RateHelper::accept(v);
×
UNCOV
249
    }
×
250

251

252
    FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
339✔
253
                                 Natural monthsToStart,
254
                                 Natural monthsToEnd,
255
                                 Natural fixingDays,
256
                                 const Calendar& calendar,
257
                                 BusinessDayConvention convention,
258
                                 bool endOfMonth,
259
                                 const DayCounter& dayCounter,
260
                                 Pillar::Choice pillarChoice,
261
                                 Date customPillarDate,
262
                                 bool useIndexedCoupon)
339✔
263
    : FraRateHelper(rate, monthsToStart*Months, monthsToEnd-monthsToStart, fixingDays, calendar,
264
        convention, endOfMonth, dayCounter, pillarChoice, customPillarDate, useIndexedCoupon) {
339✔
265
        QL_REQUIRE(monthsToEnd>monthsToStart,
339✔
266
                   "monthsToEnd (" << monthsToEnd <<
267
                   ") must be grater than monthsToStart (" << monthsToStart <<
268
                   ")");
269
    }
339✔
270

271
    FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
18✔
272
                                 Natural monthsToStart,
273
                                 const ext::shared_ptr<IborIndex>& i,
274
                                 Pillar::Choice pillarChoice,
275
                                 Date customPillarDate,
276
                                 bool useIndexedCoupon)
18✔
277
    : FraRateHelper(rate, monthsToStart*Months, i, pillarChoice, customPillarDate, useIndexedCoupon)
18✔
278
    {}
18✔
279

280
    FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
339✔
281
                                 Period periodToStart,
282
                                 Natural lengthInMonths,
283
                                 Natural fixingDays,
284
                                 const Calendar& calendar,
285
                                 BusinessDayConvention convention,
286
                                 bool endOfMonth,
287
                                 const DayCounter& dayCounter,
288
                                 Pillar::Choice pillarChoice,
289
                                 Date customPillarDate,
290
                                 bool useIndexedCoupon)
339✔
291
    : RelativeDateRateHelper(rate), periodToStart_(periodToStart),
292
      pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
339✔
293
        // no way to take fixing into account,
294
        // even if we would like to for FRA over today
295
        iborIndex_ = ext::make_shared<IborIndex>("no-fix", // correct family name would be needed
678✔
296
                      lengthInMonths*Months,
678✔
297
                      fixingDays,
298
                      Currency(), calendar, convention,
339✔
299
                      endOfMonth, dayCounter, termStructureHandle_);
339✔
300
        pillarDate_ = customPillarDate;
339✔
301
        FraRateHelper::initializeDates();
339✔
302
    }
339✔
303

304
    FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
52✔
305
                                 Period periodToStart,
306
                                 const ext::shared_ptr<IborIndex>& i,
307
                                 Pillar::Choice pillarChoice,
308
                                 Date customPillarDate,
309
                                 bool useIndexedCoupon)
52✔
310
    : RelativeDateRateHelper(rate), periodToStart_(periodToStart),
311
      pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
52✔
312
        // take fixing into account
313
        iborIndex_ = i->clone(termStructureHandle_);
52✔
314
        // We want to be notified of changes of fixings, but we don't
315
        // want notifications from termStructureHandle_ (they would
316
        // interfere with bootstrapping.)
317
        iborIndex_->unregisterWith(termStructureHandle_);
52✔
318
        registerWith(iborIndex_);
52✔
319
        pillarDate_ = customPillarDate;
52✔
320
        FraRateHelper::initializeDates();
52✔
321
    }
52✔
322

UNCOV
323
    FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
×
324
                                 Natural immOffsetStart,
325
                                 Natural immOffsetEnd,
326
                                 const ext::shared_ptr<IborIndex>& i,
327
                                 Pillar::Choice pillarChoice,
328
                                 Date customPillarDate,
UNCOV
329
                                 bool useIndexedCoupon)
×
330
    : RelativeDateRateHelper(rate), immOffsetStart_(immOffsetStart), immOffsetEnd_(immOffsetEnd),
UNCOV
331
      pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
×
332
        // take fixing into account
UNCOV
333
        iborIndex_ = i->clone(termStructureHandle_);
×
334
        // see above
335
        iborIndex_->unregisterWith(termStructureHandle_);
×
336
        registerWith(iborIndex_);
×
337
        pillarDate_ = customPillarDate;
×
338
        FraRateHelper::initializeDates();
×
UNCOV
339
    }
×
340

341
    FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
5✔
342
                                 Date startDate,
343
                                 Date endDate,
344
                                 const ext::shared_ptr<IborIndex>& i,
345
                                 Pillar::Choice pillarChoice,
346
                                 Date customPillarDate,
347
                                 bool useIndexedCoupon)
5✔
348
    : RelativeDateRateHelper(rate, false), pillarChoice_(pillarChoice),
5✔
349
      useIndexedCoupon_(useIndexedCoupon) {
5✔
350
        // take fixing into account
351
        iborIndex_ = i->clone(termStructureHandle_);
5✔
352
        // see above
353
        iborIndex_->unregisterWith(termStructureHandle_);
5✔
354
        registerWith(iborIndex_);
5✔
355
        earliestDate_ = startDate;
5✔
356
        maturityDate_ = endDate;
5✔
357
        pillarDate_ = customPillarDate;
5✔
358
        FraRateHelper::initializeDates();
5✔
359
    }
5✔
360

361
    Real FraRateHelper::impliedQuote() const {
15,074✔
362
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
15,074✔
363
        if (useIndexedCoupon_)
15,074✔
364
            return iborIndex_->fixing(fixingDate_, true);
14,273✔
365
        else
366
            return (termStructure_->discount(earliestDate_) /
801✔
367
                        termStructure_->discount(maturityDate_) -
801✔
368
                    1.0) /
369
                   spanningTime_;
801✔
370
    }
371

372
    void FraRateHelper::setTermStructure(YieldTermStructure* t) {
401✔
373
        // do not set the relinkable handle as an observer -
374
        // force recalculation when needed---the index is not lazy
375
        bool observer = false;
376

377
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
378
        termStructureHandle_.linkTo(temp, observer);
401✔
379

380
        RelativeDateRateHelper::setTermStructure(t);
401✔
381
    }
401✔
382

383
    namespace {
384
        Date nthImmDate(const Date& asof, const Size n) {
×
385
            Date imm = asof;
×
386
            for (Size i = 0; i < n; ++i) {
×
UNCOV
387
                imm = IMM::nextDate(imm, true);
×
388
            }
UNCOV
389
            return imm;
×
390
        }
391
    }
392

393
    void FraRateHelper::initializeDates() {
396✔
394
        if (updateDates_) {
396✔
395
            // if the evaluation date is not a business day
396
            // then move to the next business day
397
            Date referenceDate =
398
                iborIndex_->fixingCalendar().adjust(evaluationDate_);
391✔
399
            Date spotDate = iborIndex_->fixingCalendar().advance(
782✔
400
                referenceDate, iborIndex_->fixingDays()*Days);
391✔
401
            if (periodToStart_) { // NOLINT(readability-implicit-bool-conversion)
391✔
402
                earliestDate_ = iborIndex_->fixingCalendar().advance(
782✔
403
                    spotDate, *periodToStart_, iborIndex_->businessDayConvention(),
404
                    iborIndex_->endOfMonth());
391✔
405
                // maturity date is calculated from spot date
406
                maturityDate_ = iborIndex_->fixingCalendar().advance(
1,564✔
407
                    spotDate, *periodToStart_ + iborIndex_->tenor(), iborIndex_->businessDayConvention(),
782✔
408
                    iborIndex_->endOfMonth());
391✔
409

410
            } else if ((immOffsetStart_) && (immOffsetEnd_)) { // NOLINT(readability-implicit-bool-conversion)
×
411
                earliestDate_ = iborIndex_->fixingCalendar().adjust(nthImmDate(spotDate, *immOffsetStart_));
×
UNCOV
412
                maturityDate_ = iborIndex_->fixingCalendar().adjust(nthImmDate(spotDate, *immOffsetEnd_));
×
413
            } else {
UNCOV
414
                QL_FAIL("neither periodToStart nor immOffsetStart/End given");
×
415
            }
416
        }
417

418
        if (useIndexedCoupon_)
396✔
419
            // latest relevant date is calculated from earliestDate_
420
            latestRelevantDate_ = iborIndex_->maturityDate(earliestDate_);
346✔
421
        else {
422
            latestRelevantDate_ = maturityDate_;
50✔
423
            spanningTime_ = iborIndex_->dayCounter().yearFraction(earliestDate_, maturityDate_);
50✔
424
        }
425

426
        switch (pillarChoice_) {
396✔
427
          case Pillar::MaturityDate:
×
428
            pillarDate_ = maturityDate_;
×
UNCOV
429
            break;
×
430
          case Pillar::LastRelevantDate:
396✔
431
            pillarDate_ = latestRelevantDate_;
396✔
432
            break;
396✔
UNCOV
433
          case Pillar::CustomDate:
×
434
            // pillarDate_ already assigned at construction time
UNCOV
435
            QL_REQUIRE(pillarDate_ >= earliestDate_,
×
436
                       "pillar date (" << pillarDate_ << ") must be later "
437
                       "than or equal to the instrument's earliest date (" <<
438
                       earliestDate_ << ")");
UNCOV
439
            QL_REQUIRE(pillarDate_ <= latestRelevantDate_,
×
440
                       "pillar date (" << pillarDate_ << ") must be before "
441
                       "or equal to the instrument's latest relevant date (" <<
442
                       latestRelevantDate_ << ")");
443
            break;
444
          default:
×
UNCOV
445
            QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
×
446
        }
447

448
        latestDate_ = pillarDate_; // backward compatibility
396✔
449

450
        fixingDate_ = iborIndex_->fixingDate(earliestDate_);
396✔
451
    }
396✔
452

453
    void FraRateHelper::accept(AcyclicVisitor& v) {
×
454
        auto* v1 = dynamic_cast<Visitor<FraRateHelper>*>(&v);
×
455
        if (v1 != nullptr)
×
UNCOV
456
            v1->visit(*this);
×
457
        else
458
            RateHelper::accept(v);
×
UNCOV
459
    }
×
460

461

UNCOV
462
    SwapRateHelper::SwapRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
×
463
                                   const ext::shared_ptr<SwapIndex>& swapIndex,
464
                                   Handle<Quote> spread,
465
                                   const Period& fwdStart,
466
                                   Handle<YieldTermStructure> discount,
467
                                   Pillar::Choice pillarChoice,
468
                                   Date customPillarDate,
469
                                   bool endOfMonth,
470
                                   const ext::optional<bool>& useIndexedCoupons,
471
                                   const ext::shared_ptr<FloatingRateCouponPricer>& couponPricer)
×
472
    : SwapRateHelper(rate, swapIndex->tenor(), swapIndex->fixingCalendar(),
×
UNCOV
473
        swapIndex->fixedLegTenor().frequency(), swapIndex->fixedLegConvention(),
×
474
        swapIndex->dayCounter(), swapIndex->iborIndex(), std::move(spread), fwdStart,
×
475
        std::move(discount), Null<Natural>(), pillarChoice, customPillarDate, endOfMonth,
UNCOV
476
        useIndexedCoupons, ext::nullopt, couponPricer) {}
×
477

478
    SwapRateHelper::SwapRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
1,329✔
479
                                   const Period& tenor,
480
                                   Calendar calendar,
481
                                   Frequency fixedFrequency,
482
                                   BusinessDayConvention fixedConvention,
483
                                   DayCounter fixedDayCount,
484
                                   const ext::shared_ptr<IborIndex>& iborIndex,
485
                                   Handle<Quote> spread,
486
                                   const Period& fwdStart,
487
                                   Handle<YieldTermStructure> discount,
488
                                   Natural settlementDays,
489
                                   Pillar::Choice pillarChoice,
490
                                   Date customPillarDate,
491
                                   bool endOfMonth,
492
                                   const ext::optional<bool>& useIndexedCoupons,
493
                                   const ext::optional<BusinessDayConvention>& floatConvention,
494
                                   const ext::shared_ptr<FloatingRateCouponPricer>& couponPricer)
1,329✔
495
    : RelativeDateRateHelper(rate), settlementDays_(settlementDays), tenor_(tenor),
1,329✔
496
      pillarChoice_(pillarChoice), calendar_(std::move(calendar)),
1,329✔
497
      fixedConvention_(fixedConvention), fixedFrequency_(fixedFrequency),
1,329✔
498
      fixedDayCount_(std::move(fixedDayCount)), spread_(std::move(spread)), endOfMonth_(endOfMonth),
1,329✔
499
      fwdStart_(fwdStart), discountHandle_(std::move(discount)),
1,329✔
500
      useIndexedCoupons_(useIndexedCoupons), floatConvention_(floatConvention),
1,329✔
501
      couponPricer_(couponPricer) {
1,329✔
502
        initialize(iborIndex, customPillarDate);
1,329✔
503
    }
1,329✔
504

505
    SwapRateHelper::SwapRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
5✔
506
                                   const Date& startDate,
507
                                   const Date& endDate,
508
                                   Calendar calendar,
509
                                   Frequency fixedFrequency,
510
                                   BusinessDayConvention fixedConvention,
511
                                   DayCounter fixedDayCount,
512
                                   const ext::shared_ptr<IborIndex>& iborIndex,
513
                                   Handle<Quote> spread,
514
                                   Handle<YieldTermStructure> discount,
515
                                   Pillar::Choice pillarChoice,
516
                                   Date customPillarDate,
517
                                   bool endOfMonth,
518
                                   const ext::optional<bool>& useIndexedCoupons,
519
                                   const ext::optional<BusinessDayConvention>& floatConvention,
520
                                   const ext::shared_ptr<FloatingRateCouponPricer>& couponPricer)
5✔
521
    : RelativeDateRateHelper(rate, false), startDate_(startDate), endDate_(endDate),
5✔
522
      pillarChoice_(pillarChoice), calendar_(std::move(calendar)),
5✔
523
      fixedConvention_(fixedConvention), fixedFrequency_(fixedFrequency),
5✔
524
      fixedDayCount_(std::move(fixedDayCount)), spread_(std::move(spread)), endOfMonth_(endOfMonth),
5✔
525
      discountHandle_(std::move(discount)), useIndexedCoupons_(useIndexedCoupons),
5✔
526
      floatConvention_(floatConvention), couponPricer_(couponPricer) {
5✔
527
        QL_REQUIRE(fixedFrequency != Once,
5✔
528
            "fixedFrequency == Once is not supported when passing explicit "
529
            "startDate and endDate");
530
        initialize(iborIndex, customPillarDate);
5✔
531
    }
5✔
532

533
    void SwapRateHelper::initialize(const ext::shared_ptr<IborIndex>& iborIndex,
1,334✔
534
                                    Date customPillarDate) {
535
        // take fixing into account
536
        iborIndex_ = iborIndex->clone(termStructureHandle_);
1,334✔
537
        // We want to be notified of changes of fixings, but we don't
538
        // want notifications from termStructureHandle_ (they would
539
        // interfere with bootstrapping.)
540
        iborIndex_->unregisterWith(termStructureHandle_);
1,334✔
541

542
        registerWith(iborIndex_);
2,668✔
543
        registerWith(spread_);
2,668✔
544
        registerWith(discountHandle_);
1,334✔
545
        registerWith(couponPricer_);
1,334✔
546

547
        pillarDate_ = customPillarDate;
1,334✔
548
        SwapRateHelper::initializeDates();
1,334✔
549
    }
1,334✔
550

551
    void SwapRateHelper::initializeDates() {
39,317✔
552

553
        // 1. do not pass the spread here, as it might be a Quote
554
        //    i.e. it can dynamically change
555
        // 2. input discount curve Handle might be empty now but it could
556
        //    be assigned a curve later; use a RelinkableHandle here
557
        auto tmp = MakeVanillaSwap(tenor_, iborIndex_, 0.0, fwdStart_)
78,634✔
558
            .withEffectiveDate(startDate_)
39,317✔
559
            .withTerminationDate(endDate_)
39,317✔
560
            .withDiscountingTermStructure(discountRelinkableHandle_)
39,317✔
561
            .withFixedLegDayCount(fixedDayCount_)
39,317✔
562
            .withFixedLegTenor(fixedFrequency_ == Once ? tenor_ : Period(fixedFrequency_))
78,634✔
563
            .withFixedLegConvention(fixedConvention_)
39,317✔
564
            .withFixedLegTerminationDateConvention(fixedConvention_)
39,317✔
565
            .withFixedLegCalendar(calendar_)
39,317✔
566
            .withFixedLegEndOfMonth(endOfMonth_)
39,317✔
567
            .withFloatingLegCalendar(calendar_)
39,317✔
568
            .withFloatingLegEndOfMonth(endOfMonth_)
39,317✔
569
            .withIndexedCoupons(useIndexedCoupons_);
39,317✔
570
        if (floatConvention_) {
39,317✔
UNCOV
571
            tmp.withFloatingLegConvention(*floatConvention_)
×
UNCOV
572
               .withFloatingLegTerminationDateConvention(*floatConvention_);
×
573
        }
574
        // only set settlementDays when no explicit start date, to avoid conflict
575
        if (startDate_ == Date() && settlementDays_ != Null<Natural>())
39,317✔
UNCOV
576
            tmp.withSettlementDays(settlementDays_);
×
577
        swap_ = tmp;
78,634✔
578

579
        if (couponPricer_)
39,317✔
580
            setCouponPricer(swap_->floatingLeg(), couponPricer_);
37,924✔
581

582
        simplifyNotificationGraph(*swap_, true);
39,317✔
583

584
        earliestDate_ = swap_->startDate();
39,317✔
585
        maturityDate_ = swap_->maturityDate();
39,317✔
586

587
        ext::shared_ptr<IborCoupon> lastCoupon =
588
            ext::dynamic_pointer_cast<IborCoupon>(swap_->floatingLeg().back());
39,317✔
589
        latestRelevantDate_ = std::max(maturityDate_, lastCoupon->fixingEndDate());
39,317✔
590

591
        switch (pillarChoice_) {
39,317✔
UNCOV
592
          case Pillar::MaturityDate:
×
UNCOV
593
            pillarDate_ = maturityDate_;
×
UNCOV
594
            break;
×
595
          case Pillar::LastRelevantDate:
39,317✔
596
            pillarDate_ = latestRelevantDate_;
39,317✔
597
            break;
39,317✔
UNCOV
598
          case Pillar::CustomDate:
×
599
            // pillarDate_ already assigned at construction time
600
            QL_REQUIRE(pillarDate_ >= earliestDate_,
×
601
                "pillar date (" << pillarDate_ << ") must be later "
602
                "than or equal to the instrument's earliest date (" <<
603
                earliestDate_ << ")");
UNCOV
604
            QL_REQUIRE(pillarDate_ <= latestRelevantDate_,
×
605
                "pillar date (" << pillarDate_ << ") must be before "
606
                "or equal to the instrument's latest relevant date (" <<
607
                latestRelevantDate_ << ")");
608
            break;
UNCOV
609
          default:
×
UNCOV
610
            QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
×
611
        }
612

613
        latestDate_ = pillarDate_; // backward compatibility
39,317✔
614

615
    }
39,317✔
616

617
    void SwapRateHelper::setTermStructure(YieldTermStructure* t) {
1,751✔
618
        // do not set the relinkable handle as an observer -
619
        // force recalculation when needed
620
        bool observer = false;
621

622
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
623
        termStructureHandle_.linkTo(temp, observer);
1,751✔
624

625
        if (discountHandle_.empty())
1,751✔
626
            discountRelinkableHandle_.linkTo(temp, observer);
1,698✔
627
        else
628
            discountRelinkableHandle_.linkTo(*discountHandle_, observer);
53✔
629

630
        RelativeDateRateHelper::setTermStructure(t);
1,751✔
631
    }
1,751✔
632

633
    Real SwapRateHelper::impliedQuote() const {
41,035✔
634
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
41,035✔
635
        // we didn't register as observers - force calculation
636
        swap_->deepUpdate();
41,035✔
637
        // weak implementation... to be improved
638
        static const Spread basisPoint = 1.0e-4;
639
        Real floatingLegNPV = swap_->floatingLegNPV();
41,035✔
640
        Spread spread = spread_.empty() ? 0.0 : spread_->value();
41,035✔
641
        Real spreadNPV = swap_->floatingLegBPS()/basisPoint*spread;
41,035✔
642
        Real totNPV = - (floatingLegNPV+spreadNPV);
41,035✔
643
        Real result = totNPV/(swap_->fixedLegBPS()/basisPoint);
41,035✔
644
        return result;
41,035✔
645
    }
646

UNCOV
647
    void SwapRateHelper::accept(AcyclicVisitor& v) {
×
UNCOV
648
        auto* v1 = dynamic_cast<Visitor<SwapRateHelper>*>(&v);
×
UNCOV
649
        if (v1 != nullptr)
×
UNCOV
650
            v1->visit(*this);
×
651
        else
UNCOV
652
            RateHelper::accept(v);
×
UNCOV
653
    }
×
654

655
    BMASwapRateHelper::BMASwapRateHelper(const Handle<Quote>& liborFraction,
80✔
656
                                         const Period& tenor,
657
                                         Natural settlementDays,
658
                                         Calendar calendar,
659
                                         // bma leg
660
                                         const Period& bmaPeriod,
661
                                         BusinessDayConvention bmaConvention,
662
                                         DayCounter bmaDayCount,
663
                                         ext::shared_ptr<BMAIndex> bmaIndex,
664
                                         // libor leg
665
                                         ext::shared_ptr<IborIndex> iborIndex)
80✔
666
    : RelativeDateRateHelper(liborFraction), tenor_(tenor), settlementDays_(settlementDays),
80✔
667
      calendar_(std::move(calendar)), bmaPeriod_(bmaPeriod), bmaConvention_(bmaConvention),
80✔
668
      bmaDayCount_(std::move(bmaDayCount)), bmaIndex_(std::move(bmaIndex)),
669
      iborIndex_(std::move(iborIndex)) {
160✔
670
        registerWith(iborIndex_);
160✔
671
        registerWith(bmaIndex_);
80✔
672
        BMASwapRateHelper::initializeDates();
80✔
673
    }
80✔
674

675
    void BMASwapRateHelper::initializeDates() {
80✔
676
        // if the evaluation date is not a business day
677
        // then move to the next business day
678
        JointCalendar jc(calendar_,
80✔
679
                         iborIndex_->fixingCalendar());
80✔
680
        Date referenceDate = jc.adjust(evaluationDate_);
80✔
681
        earliestDate_ =
682
            calendar_.advance(referenceDate, settlementDays_ * Days, Following);
80✔
683

684
        Date maturity = earliestDate_ + tenor_;
80✔
685

686
        // dummy BMA index with curve/swap arguments
687
        ext::shared_ptr<BMAIndex> clonedIndex(new BMAIndex(termStructureHandle_));
80✔
688

689
        Schedule bmaSchedule =
690
            MakeSchedule().from(earliestDate_).to(maturity)
80✔
691
                          .withTenor(bmaPeriod_)
80✔
692
                          .withCalendar(bmaIndex_->fixingCalendar())
160✔
693
                          .withConvention(bmaConvention_)
80✔
694
                          .backwards();
80✔
695

696
        Schedule liborSchedule =
697
            MakeSchedule().from(earliestDate_).to(maturity)
80✔
698
                          .withTenor(iborIndex_->tenor())
80✔
699
                          .withCalendar(iborIndex_->fixingCalendar())
160✔
700
                          .withConvention(iborIndex_->businessDayConvention())
80✔
701
                          .endOfMonth(iborIndex_->endOfMonth())
80✔
702
                          .backwards();
80✔
703

704
        swap_ = ext::make_shared<BMASwap>(Swap::Payer, 100.0,
160✔
705
                                          liborSchedule,
706
                                          0.75, // arbitrary
160✔
707
                                          0.0,
80✔
708
                                          iborIndex_,
709
                                          iborIndex_->dayCounter(),
80✔
710
                                          bmaSchedule,
711
                                          clonedIndex,
712
                                          bmaDayCount_);
160✔
713
        swap_->setPricingEngine(ext::shared_ptr<PricingEngine>(new
320✔
714
            DiscountingSwapEngine(iborIndex_->forwardingTermStructure())));
160✔
715

716
        Date d = calendar_.adjust(swap_->maturityDate(), Following);
80✔
717
        Weekday w = d.weekday();
718
        Date nextWednesday = (w >= 4) ?
80✔
719
            d + (11 - w) * Days :
16✔
720
            d + (4 - w) * Days;
80✔
721
        latestDate_ = clonedIndex->valueDate(
160✔
722
                         clonedIndex->fixingCalendar().adjust(nextWednesday));
160✔
723
    }
160✔
724

725
    void BMASwapRateHelper::setTermStructure(YieldTermStructure* t) {
80✔
726
        // do not set the relinkable handle as an observer -
727
        // force recalculation when needed
728
        bool observer = false;
729

730
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
731
        termStructureHandle_.linkTo(temp, observer);
80✔
732

733
        RelativeDateRateHelper::setTermStructure(t);
80✔
734
    }
80✔
735

736
    Real BMASwapRateHelper::impliedQuote() const {
1,205✔
737
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
1,205✔
738
        // we didn't register as observers - force calculation
739
        swap_->deepUpdate();
1,205✔
740
        return swap_->fairLiborFraction();
1,205✔
741
    }
742

UNCOV
743
    void BMASwapRateHelper::accept(AcyclicVisitor& v) {
×
UNCOV
744
        auto* v1 = dynamic_cast<Visitor<BMASwapRateHelper>*>(&v);
×
UNCOV
745
        if (v1 != nullptr)
×
UNCOV
746
            v1->visit(*this);
×
747
        else
UNCOV
748
            RateHelper::accept(v);
×
UNCOV
749
    }
×
750

751
    FxSwapRateHelper::FxSwapRateHelper(const Handle<Quote>& fwdPoint,
6✔
752
                                       Handle<Quote> spotFx,
753
                                       const Period& tenor,
754
                                       Natural fixingDays,
755
                                       Calendar calendar,
756
                                       BusinessDayConvention convention,
757
                                       bool endOfMonth,
758
                                       bool isFxBaseCurrencyCollateralCurrency,
759
                                       Handle<YieldTermStructure> coll,
760
                                       Calendar tradingCalendar)
6✔
761
    : RelativeDateRateHelper(fwdPoint), spot_(std::move(spotFx)), tenor_(tenor),
6✔
762
      fixingDays_(fixingDays), cal_(std::move(calendar)), conv_(convention), eom_(endOfMonth),
6✔
763
      isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency),
6✔
764
      collHandle_(std::move(coll)), tradingCalendar_(std::move(tradingCalendar)) {
12✔
765
        registerWith(spot_);
12✔
766
        registerWith(collHandle_);
12✔
767

768
        if (tradingCalendar_.empty())
6✔
769
            jointCalendar_ = cal_;
770
        else
UNCOV
771
            jointCalendar_ = JointCalendar(tradingCalendar_, cal_,
×
772
                                           JoinHolidays);
773
        FxSwapRateHelper::initializeDates();
6✔
774
    }
6✔
775

776
    FxSwapRateHelper::FxSwapRateHelper(const Handle<Quote>& fwdPoint,
6✔
777
                                       Handle<Quote> spotFx,
778
                                       const Date& startDate,
779
                                       const Date& endDate,
780
                                       bool isFxBaseCurrencyCollateralCurrency,
781
                                       Handle<YieldTermStructure> coll)
6✔
782
    : RelativeDateRateHelper(fwdPoint, false), spot_(std::move(spotFx)),
783
      isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency),
6✔
784
      collHandle_(std::move(coll)) {
12✔
785
        registerWith(spot_);
12✔
786
        registerWith(collHandle_);
6✔
787
        earliestDate_ = startDate;
6✔
788
        latestDate_ = endDate;
6✔
789
    }
6✔
790

791
    void FxSwapRateHelper::initializeDates() {
6✔
792
        if (!updateDates_) return;
6✔
793
        // if the evaluation date is not a business day
794
        // then move to the next business day
795
        Date refDate = cal_.adjust(evaluationDate_);
6✔
796
        earliestDate_ = cal_.advance(refDate, fixingDays_*Days);
6✔
797

798
        if (!tradingCalendar_.empty()) {
6✔
799
            // check if fx trade can be settled in US, if not, adjust it
UNCOV
800
            earliestDate_ = jointCalendar_.adjust(earliestDate_);
×
UNCOV
801
            latestDate_ = jointCalendar_.advance(earliestDate_, tenor_,
×
UNCOV
802
                                                 conv_, eom_);
×
803
        } else {
804
            latestDate_ = cal_.advance(earliestDate_, tenor_, conv_, eom_);
6✔
805
        }
806
    }
807

808
    Real FxSwapRateHelper::impliedQuote() const {
122✔
809
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
122✔
810

811
        QL_REQUIRE(!collHandle_.empty(), "collateral term structure not set");
122✔
812

813
        DiscountFactor d1 = collHandle_->discount(earliestDate_);
122✔
814
        DiscountFactor d2 = collHandle_->discount(latestDate_);
122✔
815
        Real collRatio = d1 / d2;
122✔
816
        d1 = termStructureHandle_->discount(earliestDate_);
122✔
817
        d2 = termStructureHandle_->discount(latestDate_);
122✔
818
        Real ratio = d1 / d2;
122✔
819
        Real spot = spot_->value();
122✔
820
        if (isFxBaseCurrencyCollateralCurrency_) {
122✔
821
            return (ratio/collRatio-1)*spot;
122✔
822
        } else {
UNCOV
823
            return (collRatio/ratio-1)*spot;
×
824
        }
825
    }
826

827
    void FxSwapRateHelper::setTermStructure(YieldTermStructure* t) {
18✔
828
        // do not set the relinkable handle as an observer -
829
        // force recalculation when needed
830
        bool observer = false;
831

832
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
833
        termStructureHandle_.linkTo(temp, observer);
18✔
834

835
        collRelinkableHandle_.linkTo(*collHandle_, observer);
18✔
836

837
        RelativeDateRateHelper::setTermStructure(t);
18✔
838
    }
18✔
839

UNCOV
840
    void FxSwapRateHelper::accept(AcyclicVisitor& v) {
×
UNCOV
841
        auto* v1 = dynamic_cast<Visitor<FxSwapRateHelper>*>(&v);
×
UNCOV
842
        if (v1 != nullptr)
×
UNCOV
843
            v1->visit(*this);
×
844
        else
UNCOV
845
            RateHelper::accept(v);
×
UNCOV
846
    }
×
847

848
}
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