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

lballabio / QuantLib / 23060584769

13 Mar 2026 04:32PM UTC coverage: 74.35% (+0.001%) from 74.349%
23060584769

Pull #2481

github

web-flow
Merge 0a53bdb63 into 6650464bb
Pull Request #2481: Throw when both effectiveDate and settlementDays are set in MakeVanillaSwap/MakeOIS

5 of 6 new or added lines in 4 files covered. (83.33%)

58120 of 78171 relevant lines covered (74.35%)

8700001.41 hits per line

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

76.1
/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) {
153✔
46
            switch (type) {
153✔
47
              case Futures::IMM:
78✔
48
                QL_REQUIRE(IMM::isIMMdate(date, false), date << " is not a valid IMM date");
78✔
49
                break;
50
              case Futures::ASX:
72✔
51
                QL_REQUIRE(ASX::isASXdate(date, false), date << " is not a valid ASX date");
72✔
52
                break;
53
              case Futures::Custom:
54
                break;
55
              default:
×
56
                QL_FAIL("unknown futures type (" << type << ')');
×
57
            }
58
        }
153✔
59

60
        Time DetermineYearFraction(const Date& earliestDate,
153✔
61
                                   const Date& maturityDate,
62
                                   const DayCounter& dayCounter) {
63
            return dayCounter.yearFraction(earliestDate, maturityDate,
153✔
64
                                           earliestDate, maturityDate);
153✔
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,
151✔
139
                                         const Date& iborStartDate,
140
                                         const ext::shared_ptr<IborIndex>& index,
141
                                         const std::variant<Real, Handle<Quote>>& convAdj,
142
                                         Futures::Type type)
151✔
143
    : RateHelper(price), convAdj_(handleFromVariant(convAdj)) {
151✔
144
        CheckDate(iborStartDate, type);
151✔
145

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

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

156
    Real FuturesRateHelper::impliedQuote() const {
1,532✔
157
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
1,532✔
158
        Rate forwardRate = (termStructure_->discount(earliestDate_) /
1,532✔
159
            termStructure_->discount(maturityDate_) - 1.0) / yearFraction_;
1,532✔
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,532✔
164
        return 100.0 * (1.0 - futureRate);
1,532✔
165
    }
166

167
    Real FuturesRateHelper::convexityAdjustment() const {
1,532✔
168
        return convAdj_.empty() ? 0.0 : convAdj_->value();
1,532✔
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,
305✔
180
                                         const Period& tenor,
181
                                         Natural fixingDays,
182
                                         const Calendar& calendar,
183
                                         BusinessDayConvention convention,
184
                                         bool endOfMonth,
185
                                         const DayCounter& dayCounter)
305✔
186
    : RelativeDateRateHelper(rate) {
305✔
187
        iborIndex_ = ext::make_shared<IborIndex>("no-fix", // never take fixing into account
610✔
188
                      tenor, fixingDays,
189
                      Currency(), calendar, convention,
305✔
190
                      endOfMonth, dayCounter, termStructureHandle_);
305✔
191
        DepositRateHelper::initializeDates();
305✔
192
    }
305✔
193

194
    DepositRateHelper::DepositRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
144✔
195
                                         const ext::shared_ptr<IborIndex>& i)
144✔
196
    : RelativeDateRateHelper(rate) {
144✔
197
        iborIndex_ = i->clone(termStructureHandle_);
144✔
198
        DepositRateHelper::initializeDates();
144✔
199
    }
144✔
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,405✔
210
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
6,405✔
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,405✔
214
    }
215

216
    void DepositRateHelper::setTermStructure(YieldTermStructure* t) {
575✔
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);
575✔
223

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

227
    void DepositRateHelper::initializeDates() {
482✔
228
        if (updateDates_) {
482✔
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_);
476✔
233
            earliestDate_ = iborIndex_->valueDate(referenceDate);
476✔
234
            fixingDate_ = iborIndex_->fixingDate(earliestDate_);
476✔
235
        } else {
236
            earliestDate_ = iborIndex_->valueDate(fixingDate_);
6✔
237
        }
238
        maturityDate_ = iborIndex_->maturityDate(earliestDate_);
482✔
239
        pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
482✔
240
    }
482✔
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,
339✔
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)
339✔
262
    : FraRateHelper(rate, monthsToStart*Months, monthsToEnd-monthsToStart, fixingDays, calendar,
263
        convention, endOfMonth, dayCounter, pillarChoice, customPillarDate, useIndexedCoupon) {
339✔
264
        QL_REQUIRE(monthsToEnd>monthsToStart,
339✔
265
                   "monthsToEnd (" << monthsToEnd <<
266
                   ") must be grater than monthsToStart (" << monthsToStart <<
267
                   ")");
268
    }
339✔
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,
339✔
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)
339✔
290
    : RelativeDateRateHelper(rate), periodToStart_(periodToStart),
291
      pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
339✔
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
678✔
295
                      lengthInMonths*Months,
678✔
296
                      fixingDays,
297
                      Currency(), calendar, convention,
339✔
298
                      endOfMonth, dayCounter, termStructureHandle_);
339✔
299
        pillarDate_ = customPillarDate;
339✔
300
        FraRateHelper::initializeDates();
339✔
301
    }
339✔
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 {
13,153✔
361
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
13,153✔
362
        if (useIndexedCoupon_)
13,153✔
363
            return iborIndex_->fixing(fixingDate_, true);
12,356✔
364
        else
365
            return (termStructure_->discount(earliestDate_) /
797✔
366
                        termStructure_->discount(maturityDate_) -
797✔
367
                    1.0) /
368
                   spanningTime_;
797✔
369
    }
370

371
    void FraRateHelper::setTermStructure(YieldTermStructure* t) {
401✔
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);
401✔
378

379
        RelativeDateRateHelper::setTermStructure(t);
401✔
380
    }
401✔
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() {
396✔
393
        if (updateDates_) {
396✔
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_);
391✔
398
            Date spotDate = iborIndex_->fixingCalendar().advance(
782✔
399
                referenceDate, iborIndex_->fixingDays()*Days);
391✔
400
            if (periodToStart_) { // NOLINT(readability-implicit-bool-conversion)
391✔
401
                earliestDate_ = iborIndex_->fixingCalendar().advance(
782✔
402
                    spotDate, *periodToStart_, iborIndex_->businessDayConvention(),
403
                    iborIndex_->endOfMonth());
391✔
404
                // maturity date is calculated from spot date
405
                maturityDate_ = iborIndex_->fixingCalendar().advance(
1,564✔
406
                    spotDate, *periodToStart_ + iborIndex_->tenor(), iborIndex_->businessDayConvention(),
782✔
407
                    iborIndex_->endOfMonth());
391✔
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_)
396✔
418
            // latest relevant date is calculated from earliestDate_
419
            latestRelevantDate_ = iborIndex_->maturityDate(earliestDate_);
346✔
420
        else {
421
            latestRelevantDate_ = maturityDate_;
50✔
422
            spanningTime_ = iborIndex_->dayCounter().yearFraction(earliestDate_, maturityDate_);
50✔
423
        }
424

425
        switch (pillarChoice_) {
396✔
426
          case Pillar::MaturityDate:
×
427
            pillarDate_ = maturityDate_;
×
428
            break;
×
429
          case Pillar::LastRelevantDate:
396✔
430
            pillarDate_ = latestRelevantDate_;
396✔
431
            break;
396✔
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
396✔
448

449
        fixingDate_ = iborIndex_->fixingDate(earliestDate_);
396✔
450
    }
396✔
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,325✔
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,325✔
492
    : RelativeDateRateHelper(rate), settlementDays_(settlementDays), tenor_(tenor),
1,325✔
493
      pillarChoice_(pillarChoice), calendar_(std::move(calendar)),
1,325✔
494
      fixedConvention_(fixedConvention), fixedFrequency_(fixedFrequency),
1,325✔
495
      fixedDayCount_(std::move(fixedDayCount)), spread_(std::move(spread)), endOfMonth_(endOfMonth),
1,325✔
496
      fwdStart_(fwdStart), discountHandle_(std::move(discount)),
1,325✔
497
      useIndexedCoupons_(useIndexedCoupons), floatConvention_(floatConvention) {
1,325✔
498
        initialize(iborIndex, customPillarDate);
1,325✔
499
    }
1,325✔
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,330✔
529
                                    Date customPillarDate) {
530
        // take fixing into account
531
        iborIndex_ = iborIndex->clone(termStructureHandle_);
1,330✔
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,330✔
536

537
        registerWith(iborIndex_);
2,660✔
538
        registerWith(spread_);
2,660✔
539
        registerWith(discountHandle_);
1,330✔
540

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

545
    void SwapRateHelper::initializeDates() {
1,393✔
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,786✔
552
            .withEffectiveDate(startDate_)
1,393✔
553
            .withTerminationDate(endDate_)
1,393✔
554
            .withDiscountingTermStructure(discountRelinkableHandle_)
1,393✔
555
            .withFixedLegDayCount(fixedDayCount_)
1,393✔
556
            .withFixedLegTenor(fixedFrequency_ == Once ? tenor_ : Period(fixedFrequency_))
2,786✔
557
            .withFixedLegConvention(fixedConvention_)
1,393✔
558
            .withFixedLegTerminationDateConvention(fixedConvention_)
1,393✔
559
            .withFixedLegCalendar(calendar_)
1,393✔
560
            .withFixedLegEndOfMonth(endOfMonth_)
1,393✔
561
            .withFloatingLegCalendar(calendar_)
1,393✔
562
            .withFloatingLegEndOfMonth(endOfMonth_)
1,393✔
563
            .withIndexedCoupons(useIndexedCoupons_);
1,393✔
564
        if (floatConvention_) {
1,393✔
565
            tmp.withFloatingLegConvention(*floatConvention_)
×
566
               .withFloatingLegTerminationDateConvention(*floatConvention_);
×
567
        }
568
        // only set settlementDays when no explicit start date, to avoid conflict
569
        if (startDate_ == Date() && settlementDays_ != Null<Natural>())
1,393✔
NEW
570
            tmp.withSettlementDays(settlementDays_);
×
571
        swap_ = tmp;
1,393✔
572

573
        simplifyNotificationGraph(*swap_, true);
1,393✔
574

575
        earliestDate_ = swap_->startDate();
1,393✔
576
        maturityDate_ = swap_->maturityDate();
1,393✔
577

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

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

604
        latestDate_ = pillarDate_; // backward compatibility
1,393✔
605

606
    }
1,393✔
607

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

613
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
614
        termStructureHandle_.linkTo(temp, observer);
1,752✔
615

616
        if (discountHandle_.empty())
1,752✔
617
            discountRelinkableHandle_.linkTo(temp, observer);
1,699✔
618
        else
619
            discountRelinkableHandle_.linkTo(*discountHandle_, observer);
53✔
620

621
        RelativeDateRateHelper::setTermStructure(t);
1,752✔
622
    }
1,752✔
623

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

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

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

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

675
        Date maturity = earliestDate_ + tenor_;
80✔
676

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

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

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

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

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

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

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

724
        RelativeDateRateHelper::setTermStructure(t);
80✔
725
    }
80✔
726

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

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

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

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

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

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

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

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

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

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

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

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

826
        collRelinkableHandle_.linkTo(*collHandle_, observer);
18✔
827

828
        RelativeDateRateHelper::setTermStructure(t);
18✔
829
    }
18✔
830

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

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