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

lballabio / QuantLib / 18089177727

29 Sep 2025 07:23AM UTC coverage: 73.914% (+0.1%) from 73.816%
18089177727

Pull #2265

github

web-flow
Merge 13a7b3b53 into 787317697
Pull Request #2265: Fix potential dangling reference in MultiCubicSpline

2 of 2 new or added lines in 1 file covered. (100.0%)

219 existing lines in 29 files now uncovered.

57063 of 77202 relevant lines covered (73.91%)

8763673.17 hits per line

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

76.28
/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

41
namespace QuantLib {
42

43
    namespace {
44

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

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

67
    } // namespace
68

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

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

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

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

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

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

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

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

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

153
        registerWith(convAdj_);
266✔
154
    }
133✔
155

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

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

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

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

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

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

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

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

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

224
        RelativeDateRateHelper::setTermStructure(t);
571✔
225
    }
571✔
226

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

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

250

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

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

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

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

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

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

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

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

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

379
        RelativeDateRateHelper::setTermStructure(t);
392✔
380
    }
392✔
381

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

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

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

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

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

447
        latestDate_ = pillarDate_; // backward compatibility
387✔
448

449
        fixingDate_ = iborIndex_->fixingDate(earliestDate_);
387✔
450
    }
387✔
451

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

460

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

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

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

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

537
        registerWith(iborIndex_);
2,464✔
538
        registerWith(spread_);
2,464✔
539
        registerWith(discountHandle_);
1,232✔
540

541
        pillarDate_ = customPillarDate;
1,232✔
542
        SwapRateHelper::initializeDates();
1,232✔
543
    }
1,232✔
544

545
    void SwapRateHelper::initializeDates() {
1,295✔
546

547
        // 1. do not pass the spread here, as it might be a Quote
548
        //    i.e. it can dynamically change
549
        // 2. input discount curve Handle might be empty now but it could
550
        //    be assigned a curve later; use a RelinkableHandle here
551
        auto tmp = MakeVanillaSwap(tenor_, iborIndex_, 0.0, fwdStart_)
2,590✔
552
            .withSettlementDays(settlementDays_)  // resets effectiveDate
1,295✔
553
            .withEffectiveDate(startDate_)
1,295✔
554
            .withTerminationDate(endDate_)
1,295✔
555
            .withDiscountingTermStructure(discountRelinkableHandle_)
1,295✔
556
            .withFixedLegDayCount(fixedDayCount_)
1,295✔
557
            .withFixedLegTenor(fixedFrequency_ == Once ? tenor_ : Period(fixedFrequency_))
2,590✔
558
            .withFixedLegConvention(fixedConvention_)
1,295✔
559
            .withFixedLegTerminationDateConvention(fixedConvention_)
1,295✔
560
            .withFixedLegCalendar(calendar_)
1,295✔
561
            .withFixedLegEndOfMonth(endOfMonth_)
1,295✔
562
            .withFloatingLegCalendar(calendar_)
1,295✔
563
            .withFloatingLegEndOfMonth(endOfMonth_)
1,295✔
564
            .withIndexedCoupons(useIndexedCoupons_);
1,295✔
565
        if (floatConvention_) {
1,295✔
UNCOV
566
            tmp.withFloatingLegConvention(*floatConvention_)
×
UNCOV
567
               .withFloatingLegTerminationDateConvention(*floatConvention_);
×
568
        }
569
        swap_ = tmp;
1,295✔
570

571
        simplifyNotificationGraph(*swap_, true);
1,295✔
572

573
        earliestDate_ = swap_->startDate();
1,295✔
574
        maturityDate_ = swap_->maturityDate();
1,295✔
575

576
        ext::shared_ptr<IborCoupon> lastCoupon =
577
            ext::dynamic_pointer_cast<IborCoupon>(swap_->floatingLeg().back());
1,295✔
578
        latestRelevantDate_ = std::max(maturityDate_, lastCoupon->fixingEndDate());
1,295✔
579

580
        switch (pillarChoice_) {
1,295✔
581
          case Pillar::MaturityDate:
×
UNCOV
582
            pillarDate_ = maturityDate_;
×
UNCOV
583
            break;
×
584
          case Pillar::LastRelevantDate:
1,295✔
585
            pillarDate_ = latestRelevantDate_;
1,295✔
586
            break;
1,295✔
UNCOV
587
          case Pillar::CustomDate:
×
588
            // pillarDate_ already assigned at construction time
UNCOV
589
            QL_REQUIRE(pillarDate_ >= earliestDate_,
×
590
                "pillar date (" << pillarDate_ << ") must be later "
591
                "than or equal to the instrument's earliest date (" <<
592
                earliestDate_ << ")");
UNCOV
593
            QL_REQUIRE(pillarDate_ <= latestRelevantDate_,
×
594
                "pillar date (" << pillarDate_ << ") must be before "
595
                "or equal to the instrument's latest relevant date (" <<
596
                latestRelevantDate_ << ")");
597
            break;
UNCOV
598
          default:
×
UNCOV
599
            QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
×
600
        }
601

602
        latestDate_ = pillarDate_; // backward compatibility
1,295✔
603

604
    }
1,295✔
605

606
    void SwapRateHelper::setTermStructure(YieldTermStructure* t) {
1,699✔
607
        // do not set the relinkable handle as an observer -
608
        // force recalculation when needed
609
        bool observer = false;
610

611
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
612
        termStructureHandle_.linkTo(temp, observer);
1,699✔
613

614
        if (discountHandle_.empty())
1,699✔
615
            discountRelinkableHandle_.linkTo(temp, observer);
1,665✔
616
        else
617
            discountRelinkableHandle_.linkTo(*discountHandle_, observer);
34✔
618

619
        RelativeDateRateHelper::setTermStructure(t);
1,699✔
620
    }
1,699✔
621

622
    Real SwapRateHelper::impliedQuote() const {
33,384✔
623
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
33,384✔
624
        // we didn't register as observers - force calculation
625
        swap_->deepUpdate();
33,384✔
626
        // weak implementation... to be improved
627
        static const Spread basisPoint = 1.0e-4;
628
        Real floatingLegNPV = swap_->floatingLegNPV();
33,384✔
629
        Spread spread = spread_.empty() ? 0.0 : spread_->value();
33,384✔
630
        Real spreadNPV = swap_->floatingLegBPS()/basisPoint*spread;
33,384✔
631
        Real totNPV = - (floatingLegNPV+spreadNPV);
33,384✔
632
        Real result = totNPV/(swap_->fixedLegBPS()/basisPoint);
33,384✔
633
        return result;
33,384✔
634
    }
635

UNCOV
636
    void SwapRateHelper::accept(AcyclicVisitor& v) {
×
UNCOV
637
        auto* v1 = dynamic_cast<Visitor<SwapRateHelper>*>(&v);
×
UNCOV
638
        if (v1 != nullptr)
×
UNCOV
639
            v1->visit(*this);
×
640
        else
UNCOV
641
            RateHelper::accept(v);
×
UNCOV
642
    }
×
643

644
    BMASwapRateHelper::BMASwapRateHelper(const Handle<Quote>& liborFraction,
80✔
645
                                         const Period& tenor,
646
                                         Natural settlementDays,
647
                                         Calendar calendar,
648
                                         // bma leg
649
                                         const Period& bmaPeriod,
650
                                         BusinessDayConvention bmaConvention,
651
                                         DayCounter bmaDayCount,
652
                                         ext::shared_ptr<BMAIndex> bmaIndex,
653
                                         // libor leg
654
                                         ext::shared_ptr<IborIndex> iborIndex)
80✔
655
    : RelativeDateRateHelper(liborFraction), tenor_(tenor), settlementDays_(settlementDays),
80✔
656
      calendar_(std::move(calendar)), bmaPeriod_(bmaPeriod), bmaConvention_(bmaConvention),
80✔
657
      bmaDayCount_(std::move(bmaDayCount)), bmaIndex_(std::move(bmaIndex)),
658
      iborIndex_(std::move(iborIndex)) {
160✔
659
        registerWith(iborIndex_);
160✔
660
        registerWith(bmaIndex_);
80✔
661
        BMASwapRateHelper::initializeDates();
80✔
662
    }
80✔
663

664
    void BMASwapRateHelper::initializeDates() {
80✔
665
        // if the evaluation date is not a business day
666
        // then move to the next business day
667
        JointCalendar jc(calendar_,
80✔
668
                         iborIndex_->fixingCalendar());
80✔
669
        Date referenceDate = jc.adjust(evaluationDate_);
80✔
670
        earliestDate_ =
671
            calendar_.advance(referenceDate, settlementDays_ * Days, Following);
80✔
672

673
        Date maturity = earliestDate_ + tenor_;
80✔
674

675
        // dummy BMA index with curve/swap arguments
676
        ext::shared_ptr<BMAIndex> clonedIndex(new BMAIndex(termStructureHandle_));
80✔
677

678
        Schedule bmaSchedule =
679
            MakeSchedule().from(earliestDate_).to(maturity)
80✔
680
                          .withTenor(bmaPeriod_)
80✔
681
                          .withCalendar(bmaIndex_->fixingCalendar())
160✔
682
                          .withConvention(bmaConvention_)
80✔
683
                          .backwards();
80✔
684

685
        Schedule liborSchedule =
686
            MakeSchedule().from(earliestDate_).to(maturity)
80✔
687
                          .withTenor(iborIndex_->tenor())
80✔
688
                          .withCalendar(iborIndex_->fixingCalendar())
160✔
689
                          .withConvention(iborIndex_->businessDayConvention())
80✔
690
                          .endOfMonth(iborIndex_->endOfMonth())
80✔
691
                          .backwards();
80✔
692

693
        swap_ = ext::make_shared<BMASwap>(Swap::Payer, 100.0,
160✔
694
                                          liborSchedule,
695
                                          0.75, // arbitrary
160✔
696
                                          0.0,
80✔
697
                                          iborIndex_,
698
                                          iborIndex_->dayCounter(),
80✔
699
                                          bmaSchedule,
700
                                          clonedIndex,
701
                                          bmaDayCount_);
160✔
702
        swap_->setPricingEngine(ext::shared_ptr<PricingEngine>(new
320✔
703
            DiscountingSwapEngine(iborIndex_->forwardingTermStructure())));
160✔
704

705
        Date d = calendar_.adjust(swap_->maturityDate(), Following);
80✔
706
        Weekday w = d.weekday();
707
        Date nextWednesday = (w >= 4) ?
80✔
708
            d + (11 - w) * Days :
32✔
709
            d + (4 - w) * Days;
80✔
710
        latestDate_ = clonedIndex->valueDate(
160✔
711
                         clonedIndex->fixingCalendar().adjust(nextWednesday));
160✔
712
    }
160✔
713

714
    void BMASwapRateHelper::setTermStructure(YieldTermStructure* t) {
80✔
715
        // do not set the relinkable handle as an observer -
716
        // force recalculation when needed
717
        bool observer = false;
718

719
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
720
        termStructureHandle_.linkTo(temp, observer);
80✔
721

722
        RelativeDateRateHelper::setTermStructure(t);
80✔
723
    }
80✔
724

725
    Real BMASwapRateHelper::impliedQuote() const {
1,208✔
726
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
1,208✔
727
        // we didn't register as observers - force calculation
728
        swap_->deepUpdate();
1,208✔
729
        return swap_->fairLiborFraction();
1,208✔
730
    }
731

UNCOV
732
    void BMASwapRateHelper::accept(AcyclicVisitor& v) {
×
UNCOV
733
        auto* v1 = dynamic_cast<Visitor<BMASwapRateHelper>*>(&v);
×
UNCOV
734
        if (v1 != nullptr)
×
UNCOV
735
            v1->visit(*this);
×
736
        else
UNCOV
737
            RateHelper::accept(v);
×
UNCOV
738
    }
×
739

740
    FxSwapRateHelper::FxSwapRateHelper(const Handle<Quote>& fwdPoint,
6✔
741
                                       Handle<Quote> spotFx,
742
                                       const Period& tenor,
743
                                       Natural fixingDays,
744
                                       Calendar calendar,
745
                                       BusinessDayConvention convention,
746
                                       bool endOfMonth,
747
                                       bool isFxBaseCurrencyCollateralCurrency,
748
                                       Handle<YieldTermStructure> coll,
749
                                       Calendar tradingCalendar)
6✔
750
    : RelativeDateRateHelper(fwdPoint), spot_(std::move(spotFx)), tenor_(tenor),
6✔
751
      fixingDays_(fixingDays), cal_(std::move(calendar)), conv_(convention), eom_(endOfMonth),
6✔
752
      isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency),
6✔
753
      collHandle_(std::move(coll)), tradingCalendar_(std::move(tradingCalendar)) {
12✔
754
        registerWith(spot_);
12✔
755
        registerWith(collHandle_);
12✔
756

757
        if (tradingCalendar_.empty())
6✔
758
            jointCalendar_ = cal_;
759
        else
UNCOV
760
            jointCalendar_ = JointCalendar(tradingCalendar_, cal_,
×
761
                                           JoinHolidays);
762
        FxSwapRateHelper::initializeDates();
6✔
763
    }
6✔
764

765
    FxSwapRateHelper::FxSwapRateHelper(const Handle<Quote>& fwdPoint,
6✔
766
                                       Handle<Quote> spotFx,
767
                                       const Date& startDate,
768
                                       const Date& endDate,
769
                                       bool isFxBaseCurrencyCollateralCurrency,
770
                                       Handle<YieldTermStructure> coll)
6✔
771
    : RelativeDateRateHelper(fwdPoint, false), spot_(std::move(spotFx)),
772
      isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency),
6✔
773
      collHandle_(std::move(coll)) {
12✔
774
        registerWith(spot_);
12✔
775
        registerWith(collHandle_);
6✔
776
        earliestDate_ = startDate;
6✔
777
        latestDate_ = endDate;
6✔
778
    }
6✔
779

780
    void FxSwapRateHelper::initializeDates() {
6✔
781
        if (!updateDates_) return;
6✔
782
        // if the evaluation date is not a business day
783
        // then move to the next business day
784
        Date refDate = cal_.adjust(evaluationDate_);
6✔
785
        earliestDate_ = cal_.advance(refDate, fixingDays_*Days);
6✔
786

787
        if (!tradingCalendar_.empty()) {
6✔
788
            // check if fx trade can be settled in US, if not, adjust it
UNCOV
789
            earliestDate_ = jointCalendar_.adjust(earliestDate_);
×
UNCOV
790
            latestDate_ = jointCalendar_.advance(earliestDate_, tenor_,
×
UNCOV
791
                                                 conv_, eom_);
×
792
        } else {
793
            latestDate_ = cal_.advance(earliestDate_, tenor_, conv_, eom_);
6✔
794
        }
795
    }
796

797
    Real FxSwapRateHelper::impliedQuote() const {
122✔
798
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
122✔
799

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

802
        DiscountFactor d1 = collHandle_->discount(earliestDate_);
122✔
803
        DiscountFactor d2 = collHandle_->discount(latestDate_);
122✔
804
        Real collRatio = d1 / d2;
122✔
805
        d1 = termStructureHandle_->discount(earliestDate_);
122✔
806
        d2 = termStructureHandle_->discount(latestDate_);
122✔
807
        Real ratio = d1 / d2;
122✔
808
        Real spot = spot_->value();
122✔
809
        if (isFxBaseCurrencyCollateralCurrency_) {
122✔
810
            return (ratio/collRatio-1)*spot;
122✔
811
        } else {
UNCOV
812
            return (collRatio/ratio-1)*spot;
×
813
        }
814
    }
815

816
    void FxSwapRateHelper::setTermStructure(YieldTermStructure* t) {
18✔
817
        // do not set the relinkable handle as an observer -
818
        // force recalculation when needed
819
        bool observer = false;
820

821
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
822
        termStructureHandle_.linkTo(temp, observer);
18✔
823

824
        collRelinkableHandle_.linkTo(*collHandle_, observer);
18✔
825

826
        RelativeDateRateHelper::setTermStructure(t);
18✔
827
    }
18✔
828

UNCOV
829
    void FxSwapRateHelper::accept(AcyclicVisitor& v) {
×
UNCOV
830
        auto* v1 = dynamic_cast<Visitor<FxSwapRateHelper>*>(&v);
×
UNCOV
831
        if (v1 != nullptr)
×
UNCOV
832
            v1->visit(*this);
×
833
        else
UNCOV
834
            RateHelper::accept(v);
×
UNCOV
835
    }
×
836

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