Coverage Report

Created: 2025-08-05 06:45

/src/quantlib/ql/termstructures/yield/ratehelpers.cpp
Line
Count
Source (jump to first uncovered line)
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
 <http://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
0
        void CheckDate(const Date& date, const Futures::Type type) {
46
0
            switch (type) {
47
0
              case Futures::IMM:
48
0
                QL_REQUIRE(IMM::isIMMdate(date, false), date << " is not a valid IMM date");
49
0
                break;
50
0
              case Futures::ASX:
51
0
                QL_REQUIRE(ASX::isASXdate(date, false), date << " is not a valid ASX date");
52
0
                break;
53
0
              case Futures::Custom:
54
0
                break;
55
0
              default:
56
0
                QL_FAIL("unknown futures type (" << type << ')');
57
0
            }
58
0
        }
59
60
        Time DetermineYearFraction(const Date& earliestDate,
61
                                   const Date& maturityDate,
62
0
                                   const DayCounter& dayCounter) {
63
0
            return dayCounter.yearFraction(earliestDate, maturityDate,
64
0
                                           earliestDate, maturityDate);
65
0
        }
66
67
    } // namespace
68
69
    FuturesRateHelper::FuturesRateHelper(const std::variant<Real, Handle<Quote>>& price,
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)
78
0
    : RateHelper(price), convAdj_(handleFromVariant(convAdj)) {
79
0
        CheckDate(iborStartDate, type);
80
81
0
        earliestDate_ = iborStartDate;
82
0
        maturityDate_ =
83
0
            calendar.advance(iborStartDate, lengthInMonths * Months, convention, endOfMonth);
84
0
        yearFraction_ = DetermineYearFraction(earliestDate_, maturityDate_, dayCounter);
85
0
        pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
86
87
0
        registerWith(convAdj_);
88
0
    }
89
90
    FuturesRateHelper::FuturesRateHelper(const std::variant<Real, Handle<Quote>>& price,
91
                                         const Date& iborStartDate,
92
                                         const Date& iborEndDate,
93
                                         const DayCounter& dayCounter,
94
                                         const std::variant<Real, Handle<Quote>>& convAdj,
95
                                         Futures::Type type)
96
0
    : RateHelper(price), convAdj_(handleFromVariant(convAdj)) {
97
0
        CheckDate(iborStartDate, type);
98
99
0
        const auto determineMaturityDate =
100
0
            [&iborStartDate, &iborEndDate](const auto nextDateCalculator) -> Date {
101
0
                Date maturityDate;
102
0
                if (iborEndDate == Date()) {
103
                    // advance 3 months
104
0
                    maturityDate = nextDateCalculator(iborStartDate);
105
0
                    maturityDate = nextDateCalculator(maturityDate);
106
0
                    maturityDate = nextDateCalculator(maturityDate);
107
0
                } else {
108
0
                    QL_REQUIRE(iborEndDate > iborStartDate,
109
0
                               "end date (" << iborEndDate << ") must be greater than start date ("
110
0
                                            << iborStartDate << ')');
111
0
                    maturityDate = iborEndDate;
112
0
                }
113
0
                return maturityDate;
114
0
            };
Unexecuted instantiation: ratehelpers.cpp:QuantLib::Date QuantLib::FuturesRateHelper::FuturesRateHelper(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_0::operator()<QuantLib::FuturesRateHelper::$_0(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_1>(QuantLib::FuturesRateHelper::$_0(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_1) const
Unexecuted instantiation: ratehelpers.cpp:QuantLib::Date QuantLib::FuturesRateHelper::FuturesRateHelper(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_0::operator()<QuantLib::FuturesRateHelper::$_0(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_2>(QuantLib::FuturesRateHelper::$_0(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_2) const
115
116
0
        switch (type) {
117
0
          case Futures::IMM:
118
0
            maturityDate_ = determineMaturityDate(
119
0
                [](const Date date) -> Date { return IMM::nextDate(date, false); });
120
0
            break;
121
0
          case Futures::ASX:
122
0
            maturityDate_ = determineMaturityDate(
123
0
                [](const Date date) -> Date { return ASX::nextDate(date, false); });
124
0
            break;
125
0
          case Futures::Custom:
126
0
            maturityDate_ = iborEndDate;
127
0
            break;
128
0
          default:
129
0
            QL_FAIL("unsupported futures type (" << type << ')');
130
0
        }
131
0
        earliestDate_ = iborStartDate;
132
0
        yearFraction_ = DetermineYearFraction(earliestDate_, maturityDate_, dayCounter);
133
0
        pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
134
135
0
        registerWith(convAdj_);
136
0
    }
137
138
    FuturesRateHelper::FuturesRateHelper(const std::variant<Real, Handle<Quote>>& price,
139
                                         const Date& iborStartDate,
140
                                         const ext::shared_ptr<IborIndex>& index,
141
                                         const std::variant<Real, Handle<Quote>>& convAdj,
142
                                         Futures::Type type)
143
0
    : RateHelper(price), convAdj_(handleFromVariant(convAdj)) {
144
0
        CheckDate(iborStartDate, type);
145
146
0
        earliestDate_ = iborStartDate;
147
0
        const Calendar& cal = index->fixingCalendar();
148
0
        maturityDate_ =
149
0
            cal.advance(iborStartDate, index->tenor(), index->businessDayConvention());
150
0
        yearFraction_ = DetermineYearFraction(earliestDate_, maturityDate_, index->dayCounter());
151
0
        pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
152
153
0
        registerWith(convAdj_);
154
0
    }
155
156
0
    Real FuturesRateHelper::impliedQuote() const {
157
0
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
158
0
        Rate forwardRate = (termStructure_->discount(earliestDate_) /
159
0
            termStructure_->discount(maturityDate_) - 1.0) / yearFraction_;
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
0
        Rate futureRate = forwardRate + convexityAdjustment();
164
0
        return 100.0 * (1.0 - futureRate);
165
0
    }
166
167
0
    Real FuturesRateHelper::convexityAdjustment() const {
168
0
        return convAdj_.empty() ? 0.0 : convAdj_->value();
169
0
    }
170
171
0
    void FuturesRateHelper::accept(AcyclicVisitor& v) {
172
0
        auto* v1 = dynamic_cast<Visitor<FuturesRateHelper>*>(&v);
173
0
        if (v1 != nullptr)
174
0
            v1->visit(*this);
175
0
        else
176
0
            RateHelper::accept(v);
177
0
    }
178
179
    DepositRateHelper::DepositRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
180
                                         const Period& tenor,
181
                                         Natural fixingDays,
182
                                         const Calendar& calendar,
183
                                         BusinessDayConvention convention,
184
                                         bool endOfMonth,
185
                                         const DayCounter& dayCounter)
186
0
    : RelativeDateRateHelper(rate) {
187
0
        iborIndex_ = ext::make_shared<IborIndex>("no-fix", // never take fixing into account
188
0
                      tenor, fixingDays,
189
0
                      Currency(), calendar, convention,
190
0
                      endOfMonth, dayCounter, termStructureHandle_);
191
0
        DepositRateHelper::initializeDates();
192
0
    }
193
194
    DepositRateHelper::DepositRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
195
                                         const ext::shared_ptr<IborIndex>& i)
196
0
    : RelativeDateRateHelper(rate) {
197
0
        iborIndex_ = i->clone(termStructureHandle_);
198
0
        DepositRateHelper::initializeDates();
199
0
    }
200
201
    DepositRateHelper::DepositRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
202
                                         Date fixingDate,
203
                                         const ext::shared_ptr<IborIndex>& i)
204
0
    : RelativeDateRateHelper(rate, false), fixingDate_(fixingDate) {
205
0
        iborIndex_ = i->clone(termStructureHandle_);
206
0
        DepositRateHelper::initializeDates();
207
0
    }
208
209
0
    Real DepositRateHelper::impliedQuote() const {
210
0
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
211
        // the forecast fixing flag is set to true because
212
        // we do not want to take fixing into account
213
0
        return iborIndex_->fixing(fixingDate_, true);
214
0
    }
215
216
0
    void DepositRateHelper::setTermStructure(YieldTermStructure* t) {
217
        // do not set the relinkable handle as an observer -
218
        // force recalculation when needed---the index is not lazy
219
0
        bool observer = false;
220
221
0
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
222
0
        termStructureHandle_.linkTo(temp, observer);
223
224
0
        RelativeDateRateHelper::setTermStructure(t);
225
0
    }
226
227
0
    void DepositRateHelper::initializeDates() {
228
0
        if (updateDates_) {
229
            // if the evaluation date is not a business day
230
            // then move to the next business day
231
0
            Date referenceDate =
232
0
                iborIndex_->fixingCalendar().adjust(evaluationDate_);
233
0
            earliestDate_ = iborIndex_->valueDate(referenceDate);
234
0
            fixingDate_ = iborIndex_->fixingDate(earliestDate_);
235
0
        } else {
236
0
            earliestDate_ = iborIndex_->valueDate(fixingDate_);
237
0
        }
238
0
        maturityDate_ = iborIndex_->maturityDate(earliestDate_);
239
0
        pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
240
0
    }
241
242
0
    void DepositRateHelper::accept(AcyclicVisitor& v) {
243
0
        auto* v1 = dynamic_cast<Visitor<DepositRateHelper>*>(&v);
244
0
        if (v1 != nullptr)
245
0
            v1->visit(*this);
246
0
        else
247
0
            RateHelper::accept(v);
248
0
    }
249
250
251
    FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
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)
262
0
    : FraRateHelper(rate, monthsToStart*Months, monthsToEnd-monthsToStart, fixingDays, calendar,
263
0
        convention, endOfMonth, dayCounter, pillarChoice, customPillarDate, useIndexedCoupon) {
264
0
        QL_REQUIRE(monthsToEnd>monthsToStart,
265
0
                   "monthsToEnd (" << monthsToEnd <<
266
0
                   ") must be grater than monthsToStart (" << monthsToStart <<
267
0
                   ")");
268
0
    }
269
270
    FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
271
                                 Natural monthsToStart,
272
                                 const ext::shared_ptr<IborIndex>& i,
273
                                 Pillar::Choice pillarChoice,
274
                                 Date customPillarDate,
275
                                 bool useIndexedCoupon)
276
0
    : FraRateHelper(rate, monthsToStart*Months, i, pillarChoice, customPillarDate, useIndexedCoupon)
277
0
    {}
278
279
    FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
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)
290
0
    : RelativeDateRateHelper(rate), periodToStart_(periodToStart),
291
0
      pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
292
        // no way to take fixing into account,
293
        // even if we would like to for FRA over today
294
0
        iborIndex_ = ext::make_shared<IborIndex>("no-fix", // correct family name would be needed
295
0
                      lengthInMonths*Months,
296
0
                      fixingDays,
297
0
                      Currency(), calendar, convention,
298
0
                      endOfMonth, dayCounter, termStructureHandle_);
299
0
        pillarDate_ = customPillarDate;
300
0
        FraRateHelper::initializeDates();
301
0
    }
302
303
    FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
304
                                 Period periodToStart,
305
                                 const ext::shared_ptr<IborIndex>& i,
306
                                 Pillar::Choice pillarChoice,
307
                                 Date customPillarDate,
308
                                 bool useIndexedCoupon)
309
0
    : RelativeDateRateHelper(rate), periodToStart_(periodToStart),
310
0
      pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
311
        // take fixing into account
312
0
        iborIndex_ = i->clone(termStructureHandle_);
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
0
        iborIndex_->unregisterWith(termStructureHandle_);
317
0
        registerWith(iborIndex_);
318
0
        pillarDate_ = customPillarDate;
319
0
        FraRateHelper::initializeDates();
320
0
    }
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
0
    : RelativeDateRateHelper(rate), immOffsetStart_(immOffsetStart), immOffsetEnd_(immOffsetEnd),
330
0
      pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
331
        // take fixing into account
332
0
        iborIndex_ = i->clone(termStructureHandle_);
333
        // see above
334
0
        iborIndex_->unregisterWith(termStructureHandle_);
335
0
        registerWith(iborIndex_);
336
0
        pillarDate_ = customPillarDate;
337
0
        FraRateHelper::initializeDates();
338
0
    }
339
340
    FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
341
                                 Date startDate,
342
                                 Date endDate,
343
                                 const ext::shared_ptr<IborIndex>& i,
344
                                 Pillar::Choice pillarChoice,
345
                                 Date customPillarDate,
346
                                 bool useIndexedCoupon)
347
0
    : RelativeDateRateHelper(rate, false), pillarChoice_(pillarChoice),
348
0
      useIndexedCoupon_(useIndexedCoupon) {
349
        // take fixing into account
350
0
        iborIndex_ = i->clone(termStructureHandle_);
351
        // see above
352
0
        iborIndex_->unregisterWith(termStructureHandle_);
353
0
        registerWith(iborIndex_);
354
0
        earliestDate_ = startDate;
355
0
        maturityDate_ = endDate;
356
0
        pillarDate_ = customPillarDate;
357
0
        FraRateHelper::initializeDates();
358
0
    }
359
360
0
    Real FraRateHelper::impliedQuote() const {
361
0
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
362
0
        if (useIndexedCoupon_)
363
0
            return iborIndex_->fixing(fixingDate_, true);
364
0
        else
365
0
            return (termStructure_->discount(earliestDate_) /
366
0
                        termStructure_->discount(maturityDate_) -
367
0
                    1.0) /
368
0
                   spanningTime_;
369
0
    }
370
371
0
    void FraRateHelper::setTermStructure(YieldTermStructure* t) {
372
        // do not set the relinkable handle as an observer -
373
        // force recalculation when needed---the index is not lazy
374
0
        bool observer = false;
375
376
0
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
377
0
        termStructureHandle_.linkTo(temp, observer);
378
379
0
        RelativeDateRateHelper::setTermStructure(t);
380
0
    }
381
382
    namespace {
383
0
        Date nthImmDate(const Date& asof, const Size n) {
384
0
            Date imm = asof;
385
0
            for (Size i = 0; i < n; ++i) {
386
0
                imm = IMM::nextDate(imm, true);
387
0
            }
388
0
            return imm;
389
0
        }
390
    }
391
392
0
    void FraRateHelper::initializeDates() {
393
0
        if (updateDates_) {
394
            // if the evaluation date is not a business day
395
            // then move to the next business day
396
0
            Date referenceDate =
397
0
                iborIndex_->fixingCalendar().adjust(evaluationDate_);
398
0
            Date spotDate = iborIndex_->fixingCalendar().advance(
399
0
                referenceDate, iborIndex_->fixingDays()*Days);
400
0
            if (periodToStart_) { // NOLINT(readability-implicit-bool-conversion)
401
0
                earliestDate_ = iborIndex_->fixingCalendar().advance(
402
0
                    spotDate, *periodToStart_, iborIndex_->businessDayConvention(),
403
0
                    iborIndex_->endOfMonth());
404
                // maturity date is calculated from spot date
405
0
                maturityDate_ = iborIndex_->fixingCalendar().advance(
406
0
                    spotDate, *periodToStart_ + iborIndex_->tenor(), iborIndex_->businessDayConvention(),
407
0
                    iborIndex_->endOfMonth());
408
409
0
            } else if ((immOffsetStart_) && (immOffsetEnd_)) { // NOLINT(readability-implicit-bool-conversion)
410
0
                earliestDate_ = iborIndex_->fixingCalendar().adjust(nthImmDate(spotDate, *immOffsetStart_));
411
0
                maturityDate_ = iborIndex_->fixingCalendar().adjust(nthImmDate(spotDate, *immOffsetEnd_));
412
0
            } else {
413
0
                QL_FAIL("neither periodToStart nor immOffsetStart/End given");
414
0
            }
415
0
        }
416
417
0
        if (useIndexedCoupon_)
418
            // latest relevant date is calculated from earliestDate_
419
0
            latestRelevantDate_ = iborIndex_->maturityDate(earliestDate_);
420
0
        else {
421
0
            latestRelevantDate_ = maturityDate_;
422
0
            spanningTime_ = iborIndex_->dayCounter().yearFraction(earliestDate_, maturityDate_);
423
0
        }
424
425
0
        switch (pillarChoice_) {
426
0
          case Pillar::MaturityDate:
427
0
            pillarDate_ = maturityDate_;
428
0
            break;
429
0
          case Pillar::LastRelevantDate:
430
0
            pillarDate_ = latestRelevantDate_;
431
0
            break;
432
0
          case Pillar::CustomDate:
433
            // pillarDate_ already assigned at construction time
434
0
            QL_REQUIRE(pillarDate_ >= earliestDate_,
435
0
                       "pillar date (" << pillarDate_ << ") must be later "
436
0
                       "than or equal to the instrument's earliest date (" <<
437
0
                       earliestDate_ << ")");
438
0
            QL_REQUIRE(pillarDate_ <= latestRelevantDate_,
439
0
                       "pillar date (" << pillarDate_ << ") must be before "
440
0
                       "or equal to the instrument's latest relevant date (" <<
441
0
                       latestRelevantDate_ << ")");
442
0
            break;
443
0
          default:
444
0
            QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
445
0
        }
446
447
0
        latestDate_ = pillarDate_; // backward compatibility
448
449
0
        fixingDate_ = iborIndex_->fixingDate(earliestDate_);
450
0
    }
451
452
0
    void FraRateHelper::accept(AcyclicVisitor& v) {
453
0
        auto* v1 = dynamic_cast<Visitor<FraRateHelper>*>(&v);
454
0
        if (v1 != nullptr)
455
0
            v1->visit(*this);
456
0
        else
457
0
            RateHelper::accept(v);
458
0
    }
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
0
    : SwapRateHelper(rate, swapIndex->tenor(), swapIndex->fixingCalendar(),
471
0
        swapIndex->fixedLegTenor().frequency(), swapIndex->fixedLegConvention(),
472
0
        swapIndex->dayCounter(), swapIndex->iborIndex(), std::move(spread), fwdStart,
473
0
        std::move(discount), Null<Natural>(), pillarChoice, customPillarDate, endOfMonth,
474
0
        useIndexedCoupons) {}
475
476
    SwapRateHelper::SwapRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
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)
492
0
    : RelativeDateRateHelper(rate), settlementDays_(settlementDays), tenor_(tenor),
493
0
      pillarChoice_(pillarChoice), calendar_(std::move(calendar)),
494
0
      fixedConvention_(fixedConvention), fixedFrequency_(fixedFrequency),
495
0
      fixedDayCount_(std::move(fixedDayCount)), spread_(std::move(spread)), endOfMonth_(endOfMonth),
496
0
      fwdStart_(fwdStart), discountHandle_(std::move(discount)),
497
0
      useIndexedCoupons_(useIndexedCoupons), floatConvention_(floatConvention) {
498
0
        initialize(iborIndex, customPillarDate);
499
0
    }
500
501
    SwapRateHelper::SwapRateHelper(const std::variant<Rate, Handle<Quote>>& rate,
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)
516
0
    : RelativeDateRateHelper(rate, false), startDate_(startDate), endDate_(endDate),
517
0
      pillarChoice_(pillarChoice), calendar_(std::move(calendar)),
518
0
      fixedConvention_(fixedConvention), fixedFrequency_(fixedFrequency),
519
0
      fixedDayCount_(std::move(fixedDayCount)), spread_(std::move(spread)), endOfMonth_(endOfMonth),
520
0
      discountHandle_(std::move(discount)), useIndexedCoupons_(useIndexedCoupons),
521
0
      floatConvention_(floatConvention) {
522
0
        QL_REQUIRE(fixedFrequency != Once,
523
0
            "fixedFrequency == Once is not supported when passing explicit "
524
0
            "startDate and endDate");
525
0
        initialize(iborIndex, customPillarDate);
526
0
    }
527
528
    void SwapRateHelper::initialize(const ext::shared_ptr<IborIndex>& iborIndex,
529
0
                                    Date customPillarDate) {
530
        // take fixing into account
531
0
        iborIndex_ = iborIndex->clone(termStructureHandle_);
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
0
        iborIndex_->unregisterWith(termStructureHandle_);
536
537
0
        registerWith(iborIndex_);
538
0
        registerWith(spread_);
539
0
        registerWith(discountHandle_);
540
541
0
        pillarDate_ = customPillarDate;
542
0
        SwapRateHelper::initializeDates();
543
0
    }
544
545
0
    void SwapRateHelper::initializeDates() {
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
0
        auto tmp = MakeVanillaSwap(tenor_, iborIndex_, 0.0, fwdStart_)
552
0
            .withSettlementDays(settlementDays_)  // resets effectiveDate
553
0
            .withEffectiveDate(startDate_)
554
0
            .withTerminationDate(endDate_)
555
0
            .withDiscountingTermStructure(discountRelinkableHandle_)
556
0
            .withFixedLegDayCount(fixedDayCount_)
557
0
            .withFixedLegTenor(fixedFrequency_ == Once ? tenor_ : Period(fixedFrequency_))
558
0
            .withFixedLegConvention(fixedConvention_)
559
0
            .withFixedLegTerminationDateConvention(fixedConvention_)
560
0
            .withFixedLegCalendar(calendar_)
561
0
            .withFixedLegEndOfMonth(endOfMonth_)
562
0
            .withFloatingLegCalendar(calendar_)
563
0
            .withFloatingLegEndOfMonth(endOfMonth_)
564
0
            .withIndexedCoupons(useIndexedCoupons_);
565
0
        if (floatConvention_) {
566
0
            tmp.withFloatingLegConvention(*floatConvention_)
567
0
               .withFloatingLegTerminationDateConvention(*floatConvention_);
568
0
        }
569
0
        swap_ = tmp;
570
571
0
        simplifyNotificationGraph(*swap_, true);
572
573
0
        earliestDate_ = swap_->startDate();
574
0
        maturityDate_ = swap_->maturityDate();
575
576
0
        ext::shared_ptr<IborCoupon> lastCoupon =
577
0
            ext::dynamic_pointer_cast<IborCoupon>(swap_->floatingLeg().back());
578
0
        latestRelevantDate_ = std::max(maturityDate_, lastCoupon->fixingEndDate());
579
580
0
        switch (pillarChoice_) {
581
0
          case Pillar::MaturityDate:
582
0
            pillarDate_ = maturityDate_;
583
0
            break;
584
0
          case Pillar::LastRelevantDate:
585
0
            pillarDate_ = latestRelevantDate_;
586
0
            break;
587
0
          case Pillar::CustomDate:
588
            // pillarDate_ already assigned at construction time
589
0
            QL_REQUIRE(pillarDate_ >= earliestDate_,
590
0
                "pillar date (" << pillarDate_ << ") must be later "
591
0
                "than or equal to the instrument's earliest date (" <<
592
0
                earliestDate_ << ")");
593
0
            QL_REQUIRE(pillarDate_ <= latestRelevantDate_,
594
0
                "pillar date (" << pillarDate_ << ") must be before "
595
0
                "or equal to the instrument's latest relevant date (" <<
596
0
                latestRelevantDate_ << ")");
597
0
            break;
598
0
          default:
599
0
            QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
600
0
        }
601
602
0
        latestDate_ = pillarDate_; // backward compatibility
603
604
0
    }
605
606
0
    void SwapRateHelper::setTermStructure(YieldTermStructure* t) {
607
        // do not set the relinkable handle as an observer -
608
        // force recalculation when needed
609
0
        bool observer = false;
610
611
0
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
612
0
        termStructureHandle_.linkTo(temp, observer);
613
614
0
        if (discountHandle_.empty())
615
0
            discountRelinkableHandle_.linkTo(temp, observer);
616
0
        else
617
0
            discountRelinkableHandle_.linkTo(*discountHandle_, observer);
618
619
0
        RelativeDateRateHelper::setTermStructure(t);
620
0
    }
621
622
0
    Real SwapRateHelper::impliedQuote() const {
623
0
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
624
        // we didn't register as observers - force calculation
625
0
        swap_->deepUpdate();
626
        // weak implementation... to be improved
627
0
        static const Spread basisPoint = 1.0e-4;
628
0
        Real floatingLegNPV = swap_->floatingLegNPV();
629
0
        Spread spread = spread_.empty() ? 0.0 : spread_->value();
630
0
        Real spreadNPV = swap_->floatingLegBPS()/basisPoint*spread;
631
0
        Real totNPV = - (floatingLegNPV+spreadNPV);
632
0
        Real result = totNPV/(swap_->fixedLegBPS()/basisPoint);
633
0
        return result;
634
0
    }
635
636
0
    void SwapRateHelper::accept(AcyclicVisitor& v) {
637
0
        auto* v1 = dynamic_cast<Visitor<SwapRateHelper>*>(&v);
638
0
        if (v1 != nullptr)
639
0
            v1->visit(*this);
640
0
        else
641
0
            RateHelper::accept(v);
642
0
    }
643
644
    BMASwapRateHelper::BMASwapRateHelper(const Handle<Quote>& liborFraction,
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)
655
0
    : RelativeDateRateHelper(liborFraction), tenor_(tenor), settlementDays_(settlementDays),
656
0
      calendar_(std::move(calendar)), bmaPeriod_(bmaPeriod), bmaConvention_(bmaConvention),
657
0
      bmaDayCount_(std::move(bmaDayCount)), bmaIndex_(std::move(bmaIndex)),
658
0
      iborIndex_(std::move(iborIndex)) {
659
0
        registerWith(iborIndex_);
660
0
        registerWith(bmaIndex_);
661
0
        BMASwapRateHelper::initializeDates();
662
0
    }
663
664
0
    void BMASwapRateHelper::initializeDates() {
665
        // if the evaluation date is not a business day
666
        // then move to the next business day
667
0
        JointCalendar jc(calendar_,
668
0
                         iborIndex_->fixingCalendar());
669
0
        Date referenceDate = jc.adjust(evaluationDate_);
670
0
        earliestDate_ =
671
0
            calendar_.advance(referenceDate, settlementDays_ * Days, Following);
672
673
0
        Date maturity = earliestDate_ + tenor_;
674
675
        // dummy BMA index with curve/swap arguments
676
0
        ext::shared_ptr<BMAIndex> clonedIndex(new BMAIndex(termStructureHandle_));
677
678
0
        Schedule bmaSchedule =
679
0
            MakeSchedule().from(earliestDate_).to(maturity)
680
0
                          .withTenor(bmaPeriod_)
681
0
                          .withCalendar(bmaIndex_->fixingCalendar())
682
0
                          .withConvention(bmaConvention_)
683
0
                          .backwards();
684
685
0
        Schedule liborSchedule =
686
0
            MakeSchedule().from(earliestDate_).to(maturity)
687
0
                          .withTenor(iborIndex_->tenor())
688
0
                          .withCalendar(iborIndex_->fixingCalendar())
689
0
                          .withConvention(iborIndex_->businessDayConvention())
690
0
                          .endOfMonth(iborIndex_->endOfMonth())
691
0
                          .backwards();
692
693
0
        swap_ = ext::make_shared<BMASwap>(Swap::Payer, 100.0,
694
0
                                          liborSchedule,
695
0
                                          0.75, // arbitrary
696
0
                                          0.0,
697
0
                                          iborIndex_,
698
0
                                          iborIndex_->dayCounter(),
699
0
                                          bmaSchedule,
700
0
                                          clonedIndex,
701
0
                                          bmaDayCount_);
702
0
        swap_->setPricingEngine(ext::shared_ptr<PricingEngine>(new
703
0
            DiscountingSwapEngine(iborIndex_->forwardingTermStructure())));
704
705
0
        Date d = calendar_.adjust(swap_->maturityDate(), Following);
706
0
        Weekday w = d.weekday();
707
0
        Date nextWednesday = (w >= 4) ?
708
0
            d + (11 - w) * Days :
709
0
            d + (4 - w) * Days;
710
0
        latestDate_ = clonedIndex->valueDate(
711
0
                         clonedIndex->fixingCalendar().adjust(nextWednesday));
712
0
    }
713
714
0
    void BMASwapRateHelper::setTermStructure(YieldTermStructure* t) {
715
        // do not set the relinkable handle as an observer -
716
        // force recalculation when needed
717
0
        bool observer = false;
718
719
0
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
720
0
        termStructureHandle_.linkTo(temp, observer);
721
722
0
        RelativeDateRateHelper::setTermStructure(t);
723
0
    }
724
725
0
    Real BMASwapRateHelper::impliedQuote() const {
726
0
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
727
        // we didn't register as observers - force calculation
728
0
        swap_->deepUpdate();
729
0
        return swap_->fairLiborFraction();
730
0
    }
731
732
0
    void BMASwapRateHelper::accept(AcyclicVisitor& v) {
733
0
        auto* v1 = dynamic_cast<Visitor<BMASwapRateHelper>*>(&v);
734
0
        if (v1 != nullptr)
735
0
            v1->visit(*this);
736
0
        else
737
0
            RateHelper::accept(v);
738
0
    }
739
740
    FxSwapRateHelper::FxSwapRateHelper(const Handle<Quote>& fwdPoint,
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)
750
0
    : RelativeDateRateHelper(fwdPoint), spot_(std::move(spotFx)), tenor_(tenor),
751
0
      fixingDays_(fixingDays), cal_(std::move(calendar)), conv_(convention), eom_(endOfMonth),
752
0
      isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency),
753
0
      collHandle_(std::move(coll)), tradingCalendar_(std::move(tradingCalendar)) {
754
0
        registerWith(spot_);
755
0
        registerWith(collHandle_);
756
757
0
        if (tradingCalendar_.empty())
758
0
            jointCalendar_ = cal_;
759
0
        else
760
0
            jointCalendar_ = JointCalendar(tradingCalendar_, cal_,
761
0
                                           JoinHolidays);
762
0
        FxSwapRateHelper::initializeDates();
763
0
    }
764
765
    FxSwapRateHelper::FxSwapRateHelper(const Handle<Quote>& fwdPoint,
766
                                       Handle<Quote> spotFx,
767
                                       const Date& startDate,
768
                                       const Date& endDate,
769
                                       bool isFxBaseCurrencyCollateralCurrency,
770
                                       Handle<YieldTermStructure> coll)
771
0
    : RelativeDateRateHelper(fwdPoint, false), spot_(std::move(spotFx)),
772
0
      isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency),
773
0
      collHandle_(std::move(coll)) {
774
0
        registerWith(spot_);
775
0
        registerWith(collHandle_);
776
0
        earliestDate_ = startDate;
777
0
        latestDate_ = endDate;
778
0
    }
779
780
0
    void FxSwapRateHelper::initializeDates() {
781
0
        if (!updateDates_) return;
782
        // if the evaluation date is not a business day
783
        // then move to the next business day
784
0
        Date refDate = cal_.adjust(evaluationDate_);
785
0
        earliestDate_ = cal_.advance(refDate, fixingDays_*Days);
786
787
0
        if (!tradingCalendar_.empty()) {
788
            // check if fx trade can be settled in US, if not, adjust it
789
0
            earliestDate_ = jointCalendar_.adjust(earliestDate_);
790
0
            latestDate_ = jointCalendar_.advance(earliestDate_, tenor_,
791
0
                                                 conv_, eom_);
792
0
        } else {
793
0
            latestDate_ = cal_.advance(earliestDate_, tenor_, conv_, eom_);
794
0
        }
795
0
    }
796
797
0
    Real FxSwapRateHelper::impliedQuote() const {
798
0
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
799
800
0
        QL_REQUIRE(!collHandle_.empty(), "collateral term structure not set");
801
802
0
        DiscountFactor d1 = collHandle_->discount(earliestDate_);
803
0
        DiscountFactor d2 = collHandle_->discount(latestDate_);
804
0
        Real collRatio = d1 / d2;
805
0
        d1 = termStructureHandle_->discount(earliestDate_);
806
0
        d2 = termStructureHandle_->discount(latestDate_);
807
0
        Real ratio = d1 / d2;
808
0
        Real spot = spot_->value();
809
0
        if (isFxBaseCurrencyCollateralCurrency_) {
810
0
            return (ratio/collRatio-1)*spot;
811
0
        } else {
812
0
            return (collRatio/ratio-1)*spot;
813
0
        }
814
0
    }
815
816
0
    void FxSwapRateHelper::setTermStructure(YieldTermStructure* t) {
817
        // do not set the relinkable handle as an observer -
818
        // force recalculation when needed
819
0
        bool observer = false;
820
821
0
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
822
0
        termStructureHandle_.linkTo(temp, observer);
823
824
0
        collRelinkableHandle_.linkTo(*collHandle_, observer);
825
826
0
        RelativeDateRateHelper::setTermStructure(t);
827
0
    }
828
829
0
    void FxSwapRateHelper::accept(AcyclicVisitor& v) {
830
0
        auto* v1 = dynamic_cast<Visitor<FxSwapRateHelper>*>(&v);
831
0
        if (v1 != nullptr)
832
0
            v1->visit(*this);
833
0
        else
834
0
            RateHelper::accept(v);
835
0
    }
836
837
}