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

lballabio / QuantLib / 20666718181

02 Jan 2026 09:07PM UTC coverage: 74.186% (+0.01%) from 74.172%
20666718181

Pull #2413

github

web-flow
Merge d2dd5c446 into b43bcbfe7
Pull Request #2413: Non-deliverable cross currency swap

87 of 93 new or added lines in 1 file covered. (93.55%)

8 existing lines in 1 file now uncovered.

57576 of 77610 relevant lines covered (74.19%)

8745679.9 hits per line

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

90.63
/ql/experimental/termstructures/crosscurrencyratehelpers.cpp
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2

3
/*
4
 Copyright (C) 2021 Marcin Rybacki
5
 Copyright (C) 2025 Uzair Beg
6

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

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

16
 This program is distributed in the hope that it will be useful, but WITHOUT
17
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
 FOR A PARTICULAR PURPOSE.  See the license for more details.
19
*/
20

21
#include <ql/cashflows/overnightindexedcoupon.hpp>
22
#include <ql/cashflows/iborcoupon.hpp>
23
#include <ql/cashflows/cashflows.hpp>
24
#include <ql/cashflows/simplecashflow.hpp>
25
#include <ql/cashflows/fixedratecoupon.hpp>
26
#include <ql/experimental/termstructures/crosscurrencyratehelpers.hpp>
27
#include <ql/utilities/null_deleter.hpp>
28
#include <utility>
29

30
namespace QuantLib {
31

32
    namespace {
33

34
        constexpr double sample_fixed_rate = 0.01;
35

36
        Schedule legSchedule(const Date& evaluationDate,
489✔
37
                             const Period& tenor,
38
                             const Period& frequency,
39
                             Natural fixingDays,
40
                             const Calendar& calendar,
41
                             BusinessDayConvention convention,
42
                             bool endOfMonth) {
43
            QL_REQUIRE(tenor >= frequency,
491✔
44
                       "XCCY instrument tenor should not be smaller than coupon frequency.");
45

46
            Date referenceDate = calendar.adjust(evaluationDate);
488✔
47
            Date earliestDate = calendar.advance(referenceDate, fixingDays * Days, convention);
488✔
48
            Date maturity = earliestDate + tenor;
488✔
49
            return MakeSchedule()
488✔
50
                .from(earliestDate)
488✔
51
                .to(maturity)
488✔
52
                .withTenor(frequency)
488✔
53
                .withCalendar(calendar)
488✔
54
                .withConvention(convention)
488✔
55
                .endOfMonth(endOfMonth)
488✔
56
                .backwards();
976✔
57
        }
58

59
        Leg buildFloatingLeg(const Date& evaluationDate,
444✔
60
                             const Period& tenor,
61
                             Natural fixingDays,
62
                             const Calendar& calendar,
63
                             BusinessDayConvention convention,
64
                             bool endOfMonth,
65
                             const ext::shared_ptr<IborIndex>& idx,
66
                             Frequency paymentFrequency,
67
                             Integer paymentLag) {
68
            auto overnightIndex = ext::dynamic_pointer_cast<OvernightIndex>(idx);
444✔
69

70
            Period freqPeriod;
444✔
71
            if (paymentFrequency == NoFrequency) {
444✔
72
                QL_REQUIRE(!overnightIndex, "Require payment frequency for overnight indices.");
367✔
73
                freqPeriod = idx->tenor();
364✔
74
            } else {
75
                freqPeriod = Period(paymentFrequency);
79✔
76
            }
77

78
            Schedule sch = legSchedule(evaluationDate, tenor, freqPeriod, fixingDays, calendar,
79
                                       convention, endOfMonth);
443✔
80
            if (overnightIndex != nullptr) {
442✔
81
                return OvernightLeg(sch, overnightIndex)
114✔
82
                    .withNotionals(1.0)
57✔
83
                    .withPaymentLag(paymentLag);
57✔
84
            }
85
            return IborLeg(sch, idx).withNotionals(1.0).withPaymentLag(paymentLag);
385✔
86
        }
442✔
87

88
        Leg buildFixedLeg(const Date& evaluationDate,
46✔
89
                          const Period& tenor,
90
                          Natural fixingDays,
91
                          const Calendar& calendar,
92
                          BusinessDayConvention convention,
93
                          bool endOfMonth,
94
                          Frequency paymentFrequency,
95
                          const DayCounter& dayCount,
96
                          Integer paymentLag) {
97

98
            auto freqPeriod = Period(paymentFrequency);
46✔
99

100
            Schedule sch = legSchedule(evaluationDate, tenor, freqPeriod, fixingDays, calendar,
101
                                       convention, endOfMonth);
46✔
102
            return FixedRateLeg(sch)
92✔
103
                .withNotionals(1.0)
46✔
104
                .withCouponRates(sample_fixed_rate, dayCount)
46✔
105
                .withPaymentLag(paymentLag);
92✔
106
        }
46✔
107

108
        std::pair<Real, Real>
109
        npvbpsConstNotionalLeg(const Leg& leg,
3,222✔
110
                               const Date& initialNotionalExchangeDate,
111
                               const Date& finalNotionalExchangeDate,
112
                               const Handle<YieldTermStructure>& discountCurveHandle) {
113
            const Spread basisPoint = 1.0e-4;
114
            Date refDt = discountCurveHandle->referenceDate();
3,222✔
115
            const YieldTermStructure& discountRef = **discountCurveHandle;
3,222✔
116
            bool includeSettleDtFlows = true;
117
            auto [npv, bps] =
118
                CashFlows::npvbps(leg, discountRef, includeSettleDtFlows, refDt, refDt);
3,222✔
119
            // Include NPV of the notional exchange at start and maturity.
120
            npv += (-1.0) * discountRef.discount(initialNotionalExchangeDate);
3,222✔
121
            npv += discountRef.discount(finalNotionalExchangeDate);
3,222✔
122
            bps /= basisPoint;
3,222✔
123
            return {npv, bps};
3,222✔
124
        }
125

126
        class ResettingLegHelper {
127
          public:
128
            explicit ResettingLegHelper(const YieldTermStructure& discountCurve,
129
                                        const YieldTermStructure& foreignCurve)
130
            : discountCurve_(discountCurve), foreignCurve_(foreignCurve) {}
830✔
131
            DiscountFactor discount(const Date& d) const { return discountCurve_.discount(d); }
67,046✔
132
            Real notionalAdjustment(const Date& d) const {
67,046✔
133
                return foreignCurve_.discount(d) / discountCurve_.discount(d);
67,046✔
134
            }
135

136
          private:
137
            const YieldTermStructure& discountCurve_;
138
            const YieldTermStructure& foreignCurve_;
139
        };
140

141
        class ResettingLegCalculator : public AcyclicVisitor, public Visitor<Coupon> {
142
          public:
143
            explicit ResettingLegCalculator(const YieldTermStructure& discountCurve,
144
                                            const YieldTermStructure& foreignCurve,
145
                                            Integer paymentLag,
146
                                            Calendar paymentCalendar,
147
                                            BusinessDayConvention convention)
148
            : helper_(discountCurve, foreignCurve), paymentLag_(paymentLag),
652✔
149
              paymentCalendar_(std::move(paymentCalendar)), convention_(convention) {}
652✔
150

151
            void visit(Coupon& c) override {
64,644✔
152
                Date start = c.accrualStartDate();
64,644✔
153
                Date end = c.accrualEndDate();
64,644✔
154
                Time accrual = c.accrualPeriod();
64,644✔
155
                Real adjustedNotional = c.nominal() * helper_.notionalAdjustment(start);
64,644✔
156

157
                DiscountFactor discountStart, discountEnd;
158

159
                if (paymentLag_ == 0) {
64,644✔
160
                    discountStart = helper_.discount(start);
161
                    discountEnd = helper_.discount(end);
162
                } else {
163
                    Date paymentStart =
164
                        paymentCalendar_.advance(start, paymentLag_, Days, convention_);
3,360✔
165
                    Date paymentEnd = paymentCalendar_.advance(end, paymentLag_, Days, convention_);
3,360✔
166
                    discountStart = helper_.discount(paymentStart);
167
                    discountEnd = helper_.discount(paymentEnd);
168
                }
169

170
                // NPV of a resetting coupon consists of a redemption of borrowed amount occurring
171
                // at the end of the accrual period plus the accrued interest, minus the borrowed
172
                // amount at the start of the period. All amounts are corrected by an adjustment
173
                // corresponding to the implied forward exchange rate, which is estimated by
174
                // the ratio of foreign and domestic curves discount factors.
175
                Real npvRedeemedAmount =
176
                    adjustedNotional * discountEnd * (1.0 + c.rate() * accrual);
64,644✔
177
                Real npvBorrowedAmount = -adjustedNotional * discountStart;
64,644✔
178

179
                npv_ += (npvRedeemedAmount + npvBorrowedAmount);
64,644✔
180
                bps_ += adjustedNotional * discountEnd * accrual;
64,644✔
181
            }
64,644✔
182
            Real NPV() const { return npv_; }
652✔
183
            Real BPS() const { return bps_; }
652✔
184

185
          private:
186
            ResettingLegHelper helper_;
187
            Real npv_ = 0.0;
188
            Real bps_ = 0.0;
189
            Integer paymentLag_;
190
            Calendar paymentCalendar_;
191
            BusinessDayConvention convention_;
192
        };
193

194
        std::pair<Real, Real>
195
        npvbpsResettingLeg(const Leg& leg,
652✔
196
                           Integer paymentLag,
197
                           const Calendar& paymentCalendar,
198
                           BusinessDayConvention convention,
199
                           const Handle<YieldTermStructure>& discountCurveHandle,
200
                           const Handle<YieldTermStructure>& foreignCurveHandle) {
201
            const YieldTermStructure& discountCurveRef = **discountCurveHandle;
652✔
202
            const YieldTermStructure& foreignCurveRef = **foreignCurveHandle;
652✔
203

204
            ResettingLegCalculator calc(discountCurveRef, foreignCurveRef, paymentLag,
205
                                        paymentCalendar, convention);
652✔
206
            for (const auto& i : leg) {
65,296✔
207
                CashFlow& cf = *i;
64,644✔
208
                cf.accept(calc);
64,644✔
209
            }
210
            return {calc.NPV(), calc.BPS()};
652✔
211
        }
652✔
212

213
        class NonDeliverableLegCalculator : public AcyclicVisitor,
214
                                            public Visitor<Coupon>,
215
                                            public Visitor<CashFlow> {
216
          public:
217
            explicit NonDeliverableLegCalculator(const YieldTermStructure& discountCurve,
218
                                                 const YieldTermStructure& foreignCurve,
219
                                                 Integer fxFixingDelay,
220
                                                 Integer paymentLag,
221
                                                 Calendar calendar)
222
            : helper_(discountCurve, foreignCurve), fxFixingDelay_(fxFixingDelay),
178✔
223
              paymentLag_(paymentLag), calendar_(std::move(calendar)) {}
178✔
224

225
            void visit(Coupon& c) override {
2,046✔
226
                Date fixingDate = applyFixingDelay(c.accrualEndDate());
2,046✔
227
                Real fxAdjustment = helper_.notionalAdjustment(fixingDate);
2,046✔
228
                DiscountFactor discount = helper_.discount(c.date());
2,046✔
229

230
                npv_ += c.amount() * fxAdjustment * discount;
2,046✔
231
                bps_ += c.accrualPeriod() * fxAdjustment * discount;
2,046✔
232
            }
2,046✔
233

234
            void visit(CashFlow& c) override {
356✔
235
                Date originalDate = c.date();
356✔
236
                if (paymentLag_ != 0) {
356✔
237
                    originalDate =
238
                        calendar_.advance(originalDate, -paymentLag_, Days, Following);
180✔
239
                }
240
                Date fixingDate = applyFixingDelay(originalDate);
356✔
241
                Real fxAdjustment = helper_.notionalAdjustment(fixingDate);
356✔
242
                DiscountFactor discount = helper_.discount(c.date());
356✔
243
                npv_ += c.amount() * fxAdjustment * discount;
356✔
244
            }
356✔
245

246
            Real NPV() const { return npv_; }
178✔
247
            Real BPS() const { return bps_; }
178✔
248

249
          private:
250
            Date applyFixingDelay(const Date& d) const {
2,402✔
251
                return fxFixingDelay_ == 0 ? d :
2,402✔
252
                                             calendar_.advance(d, -fxFixingDelay_, Days, Following);
2,402✔
253
            }
254

255
            ResettingLegHelper helper_;
256
            Real npv_ = 0.0;
257
            Real bps_ = 0.0;
258
            Integer fxFixingDelay_;
259
            Integer paymentLag_;
260
            Calendar calendar_;
261
        };
262

263
        std::pair<Real, Real>
264
        npvbpsNonDeliverableConstNotionalLeg(const Leg& leg,
178✔
265
                                             const Date& initialNotionalExchangeDate,
266
                                             const Date& finalNotionalExchangeDate,
267
                                             Integer fxFixingDelay,
268
                                             Integer paymentLag,
269
                                             const Calendar& calendar,
270
                                             const Handle<YieldTermStructure>& discountCurveHandle,
271
                                             const Handle<YieldTermStructure>& foreignCurveHandle) {
272
            const YieldTermStructure& discountCurveRef = **discountCurveHandle;
178✔
273
            const YieldTermStructure& foreignCurveRef = **foreignCurveHandle;
178✔
274

275
            NonDeliverableLegCalculator calc(discountCurveRef, foreignCurveRef, fxFixingDelay,
276
                                             paymentLag, calendar);
178✔
277

278
            Leg legWithNotionals;
279
            legWithNotionals.reserve(2 + leg.size());
178✔
280

281
            legWithNotionals.emplace_back(
178✔
282
                ext::make_shared<SimpleCashFlow>(-1.0, initialNotionalExchangeDate));
178✔
283
            legWithNotionals.emplace_back(
178✔
284
                ext::make_shared<SimpleCashFlow>(1.0, finalNotionalExchangeDate));
356✔
285

286
            legWithNotionals.insert(legWithNotionals.end(), leg.begin(), leg.end());
178✔
287

288
            for (const auto& i : legWithNotionals) {
2,580✔
289
                CashFlow& cf = *i;
2,402✔
290
                cf.accept(calc);
2,402✔
291
            }
292

293
            return {calc.NPV(), calc.BPS()};
178✔
294
        }
178✔
295
    }
296

297
    CrossCurrencySwapRateHelperBase::CrossCurrencySwapRateHelperBase(
246✔
298
        const Handle<Quote>& quote,
299
        const Period& tenor,
300
        Natural fixingDays,
301
        Calendar calendar,
302
        BusinessDayConvention convention,
303
        bool endOfMonth,
304
        Handle<YieldTermStructure> collateralCurve,
305
        Integer paymentLag)
246✔
306
    : RelativeDateRateHelper(quote), tenor_(tenor), fixingDays_(fixingDays),
246✔
307
      calendar_(std::move(calendar)), convention_(convention), endOfMonth_(endOfMonth),
246✔
308
      paymentLag_(paymentLag), collateralHandle_(std::move(collateralCurve)) {
492✔
309
        registerWith(collateralHandle_);
246✔
310
    }
246✔
311

312
    void CrossCurrencySwapRateHelperBase::setTermStructure(YieldTermStructure* t) {
244✔
313
        // do not set the relinkable handle as an observer -
314
        // force recalculation when needed
315
        bool observer = false;
316

317
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
318
        termStructureHandle_.linkTo(temp, observer);
244✔
319

320
        RelativeDateRateHelper::setTermStructure(t);
244✔
321
    }
244✔
322

323
    void CrossCurrencySwapRateHelperBase::initializeDatesFromLegs(const Leg& firstLeg,
244✔
324
                                                                  const Leg& secondLeg) {
325
        earliestDate_ = std::min(CashFlows::startDate(firstLeg), CashFlows::startDate(secondLeg));
244✔
326

327
        maturityDate_ =
244✔
328
            std::max(CashFlows::maturityDate(firstLeg), CashFlows::maturityDate(secondLeg));
244✔
329

330
        if (paymentLag_ == 0) {
244✔
331
            initialNotionalExchangeDate_ = earliestDate_;
203✔
332
            finalNotionalExchangeDate_ = maturityDate_;
203✔
333
        } else {
334
            initialNotionalExchangeDate_ =
335
                calendar_.advance(earliestDate_, paymentLag_, Days, convention_);
41✔
336
            finalNotionalExchangeDate_ =
337
                calendar_.advance(maturityDate_, paymentLag_, Days, convention_);
41✔
338
        }
339

340
        Date lastPaymentDate = std::max(firstLeg.back()->date(), secondLeg.back()->date());
244✔
341

342
        latestRelevantDate_ = latestDate_ = std::max(maturityDate_, lastPaymentDate);
244✔
343
    }
244✔
344

345
    CrossCurrencyBasisSwapRateHelperBase::CrossCurrencyBasisSwapRateHelperBase(
200✔
346
        const Handle<Quote>& basis,
347
        const Period& tenor,
348
        Natural fixingDays,
349
        Calendar calendar,
350
        BusinessDayConvention convention,
351
        bool endOfMonth,
352
        ext::shared_ptr<IborIndex> baseCurrencyIndex,
353
        ext::shared_ptr<IborIndex> quoteCurrencyIndex,
354
        Handle<YieldTermStructure> collateralCurve,
355
        bool isFxBaseCurrencyCollateralCurrency,
356
        bool isBasisOnFxBaseCurrencyLeg,
357
        Frequency paymentFrequency,
358
        Integer paymentLag)
200✔
359
    : CrossCurrencySwapRateHelperBase(basis, tenor, fixingDays, std::move(calendar), convention, endOfMonth,
360
                                      std::move(collateralCurve), paymentLag),
361
      baseCcyIdx_(std::move(baseCurrencyIndex)), quoteCcyIdx_(std::move(quoteCurrencyIndex)),
362
      isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency),
200✔
363
      isBasisOnFxBaseCurrencyLeg_(isBasisOnFxBaseCurrencyLeg), paymentFrequency_(paymentFrequency) {
400✔
364
        registerWith(baseCcyIdx_);
400✔
365
        registerWith(quoteCcyIdx_);
200✔
366

367
        CrossCurrencyBasisSwapRateHelperBase::initializeDates();
200✔
368
    }
204✔
369

370
    void CrossCurrencyBasisSwapRateHelperBase::initializeDates() {
200✔
371
        baseCcyIborLeg_ =
372
            buildFloatingLeg(evaluationDate_, tenor_, fixingDays_, calendar_, convention_,
198✔
373
                             endOfMonth_, baseCcyIdx_, paymentFrequency_, paymentLag_);
200✔
374

375
        quoteCcyIborLeg_ =
376
            buildFloatingLeg(evaluationDate_, tenor_, fixingDays_, calendar_, convention_,
198✔
377
                             endOfMonth_, quoteCcyIdx_, paymentFrequency_, paymentLag_);
198✔
378

379
        initializeDatesFromLegs(baseCcyIborLeg_, quoteCcyIborLeg_);
198✔
380
    }
198✔
381

382
    const Handle<YieldTermStructure>&
383
    CrossCurrencyBasisSwapRateHelperBase::baseCcyLegDiscountHandle() const {
2,087✔
384
        return isFxBaseCurrencyCollateralCurrency_ ? collateralHandle_ : termStructureHandle_;
2,087✔
385
    }
386

387
    const Handle<YieldTermStructure>&
388
    CrossCurrencyBasisSwapRateHelperBase::quoteCcyLegDiscountHandle() const {
1,809✔
389
        return isFxBaseCurrencyCollateralCurrency_ ? termStructureHandle_ : collateralHandle_;
1,809✔
390
    }
391

392
    ConstNotionalCrossCurrencyBasisSwapRateHelper::ConstNotionalCrossCurrencyBasisSwapRateHelper(
122✔
393
        const Handle<Quote>& basis,
394
        const Period& tenor,
395
        Natural fixingDays,
396
        const Calendar& calendar,
397
        BusinessDayConvention convention,
398
        bool endOfMonth,
399
        const ext::shared_ptr<IborIndex>& baseCurrencyIndex,
400
        const ext::shared_ptr<IborIndex>& quoteCurrencyIndex,
401
        const Handle<YieldTermStructure>& collateralCurve,
402
        bool isFxBaseCurrencyCollateralCurrency,
403
        bool isBasisOnFxBaseCurrencyLeg,
404
        Frequency paymentFrequency,
405
        Integer paymentLag)
122✔
406
    : CrossCurrencyBasisSwapRateHelperBase(basis,
407
                                           tenor,
408
                                           fixingDays,
409
                                           calendar,
410
                                           convention,
411
                                           endOfMonth,
412
                                           baseCurrencyIndex,
413
                                           quoteCurrencyIndex,
414
                                           collateralCurve,
415
                                           isFxBaseCurrencyCollateralCurrency,
416
                                           isBasisOnFxBaseCurrencyLeg,
417
                                           paymentFrequency,
418
                                           paymentLag) {}
368✔
419

420
    Real ConstNotionalCrossCurrencyBasisSwapRateHelper::impliedQuote() const {
970✔
421
        QL_REQUIRE(!termStructureHandle_.empty(), "term structure not set");
970✔
422
        QL_REQUIRE(!collateralHandle_.empty(), "collateral term structure not set");
970✔
423

424
        auto [npvBaseCcy, bpsBaseCcy] =
425
            npvbpsConstNotionalLeg(baseCcyIborLeg_, initialNotionalExchangeDate_,
970✔
426
                                   finalNotionalExchangeDate_, baseCcyLegDiscountHandle());
970✔
427
        auto [npvQuoteCcy, bpsQuoteCcy] =
428
            npvbpsConstNotionalLeg(quoteCcyIborLeg_, initialNotionalExchangeDate_,
970✔
429
                                   finalNotionalExchangeDate_, quoteCcyLegDiscountHandle());
430

431
        Real bps = isBasisOnFxBaseCurrencyLeg_ ? -bpsBaseCcy : bpsQuoteCcy;
970✔
432

433
        QL_REQUIRE(std::fabs(bps) > 0.0, "null BPS");
970✔
434

435
        return -(npvQuoteCcy - npvBaseCcy) / bps;
970✔
436
    }
437

438
    void ConstNotionalCrossCurrencyBasisSwapRateHelper::accept(AcyclicVisitor& v) {
×
439
        auto* v1 = dynamic_cast<Visitor<ConstNotionalCrossCurrencyBasisSwapRateHelper>*>(&v);
×
440
        if (v1 != nullptr)
×
441
            v1->visit(*this);
×
442
        else
UNCOV
443
            RateHelper::accept(v);
×
UNCOV
444
    }
×
445

446

447
    MtMCrossCurrencyBasisSwapRateHelper::MtMCrossCurrencyBasisSwapRateHelper(
78✔
448
        const Handle<Quote>& basis,
449
        const Period& tenor,
450
        Natural fixingDays,
451
        const Calendar& calendar,
452
        BusinessDayConvention convention,
453
        bool endOfMonth,
454
        const ext::shared_ptr<IborIndex>& baseCurrencyIndex,
455
        const ext::shared_ptr<IborIndex>& quoteCurrencyIndex,
456
        const Handle<YieldTermStructure>& collateralCurve,
457
        bool isFxBaseCurrencyCollateralCurrency,
458
        bool isBasisOnFxBaseCurrencyLeg,
459
        bool isFxBaseCurrencyLegResettable,
460
        Frequency paymentFrequency,
461
        Integer paymentLag)
78✔
462
    : CrossCurrencyBasisSwapRateHelperBase(basis,
463
                                           tenor,
464
                                           fixingDays,
465
                                           calendar,
466
                                           convention,
467
                                           endOfMonth,
468
                                           baseCurrencyIndex,
469
                                           quoteCurrencyIndex,
470
                                           collateralCurve,
471
                                           isFxBaseCurrencyCollateralCurrency,
472
                                           isBasisOnFxBaseCurrencyLeg,
473
                                           paymentFrequency,
474
                                           paymentLag),
475
      isFxBaseCurrencyLegResettable_(isFxBaseCurrencyLegResettable) {}
236✔
476

477
    Real MtMCrossCurrencyBasisSwapRateHelper::impliedQuote() const {
652✔
478
        QL_REQUIRE(!termStructureHandle_.empty(), "term structure not set");
652✔
479
        QL_REQUIRE(!collateralHandle_.empty(), "collateral term structure not set");
652✔
480

481
        auto [npvBaseCcy, bpsBaseCcy] =
482
            isFxBaseCurrencyLegResettable_ ?
652✔
483
                npvbpsResettingLeg(baseCcyIborLeg_, paymentLag_, calendar_, convention_,
187✔
484
                                   baseCcyLegDiscountHandle(), quoteCcyLegDiscountHandle()) :
485
                npvbpsConstNotionalLeg(baseCcyIborLeg_, initialNotionalExchangeDate_,
465✔
486
                                       finalNotionalExchangeDate_, baseCcyLegDiscountHandle());
652✔
487

488
        auto [npvQuoteCcy, bpsQuoteCcy] =
489
            isFxBaseCurrencyLegResettable_ ?
652✔
490
                npvbpsConstNotionalLeg(quoteCcyIborLeg_, initialNotionalExchangeDate_,
187✔
491
                                       finalNotionalExchangeDate_, quoteCcyLegDiscountHandle()) :
187✔
492
                npvbpsResettingLeg(quoteCcyIborLeg_, paymentLag_, calendar_, convention_,
465✔
493
                                   quoteCcyLegDiscountHandle(), baseCcyLegDiscountHandle());
652✔
494

495
        Real bps = isBasisOnFxBaseCurrencyLeg_ ? -bpsBaseCcy : bpsQuoteCcy;
652✔
496

497
        QL_REQUIRE(std::fabs(bps) > 0.0, "null BPS");
652✔
498

499
        return -(npvQuoteCcy - npvBaseCcy) / bps;
652✔
500
    }
501

UNCOV
502
    void MtMCrossCurrencyBasisSwapRateHelper::accept(AcyclicVisitor& v) {
×
UNCOV
503
        auto* v1 = dynamic_cast<Visitor<MtMCrossCurrencyBasisSwapRateHelper>*>(&v);
×
UNCOV
504
        if (v1 != nullptr)
×
505
            v1->visit(*this);
×
506
        else
UNCOV
507
            RateHelper::accept(v);
×
UNCOV
508
    }
×
509

510

511
    ConstNotionalCrossCurrencySwapRateHelper::ConstNotionalCrossCurrencySwapRateHelper(
46✔
512
        const Handle<Quote>& fixedRate,
513
        const Period& tenor,
514
        Natural fixingDays,
515
        Calendar calendar,
516
        BusinessDayConvention convention,
517
        bool endOfMonth,
518
        Frequency fixedFrequency,
519
        DayCounter fixedDayCount,
520
        const ext::shared_ptr<IborIndex>& floatIndex,
521
        Handle<YieldTermStructure> collateralCurve,
522
        bool collateralOnFixedLeg,
523
        Frequency floatingFrequency,
524
        Integer paymentLag)
46✔
525
    : CrossCurrencySwapRateHelperBase(fixedRate,
526
                                      tenor,
527
                                      fixingDays,
528
                                      std::move(calendar),
529
                                      convention,
530
                                      endOfMonth,
531
                                      collateralCurve,
532
                                      paymentLag),
533
      fixedFrequency_(fixedFrequency), floatingFrequency_(floatingFrequency),
46✔
534
      fixedDayCount_(std::move(fixedDayCount)), floatIndex_(floatIndex),
535
      collateralOnFixedLeg_(collateralOnFixedLeg) {
92✔
536

537
        QL_REQUIRE(floatIndex_, "floating index required");
46✔
538
        registerWith(floatIndex_);
46✔
539

540
        initializeDates();
46✔
541
    }
46✔
542

543
    void ConstNotionalCrossCurrencySwapRateHelper::initializeDates() {
46✔
544
        fixedLeg_ = buildFixedLeg(evaluationDate_, tenor_, fixingDays_, calendar_, convention_,
46✔
545
                                  endOfMonth_, fixedFrequency_, fixedDayCount_, paymentLag_);
46✔
546
        floatLeg_ = buildFloatingLeg(evaluationDate_, tenor_, fixingDays_, calendar_, convention_,
46✔
547
                                     endOfMonth_, floatIndex_, floatingFrequency_, paymentLag_);
46✔
548

549
        initializeDatesFromLegs(fixedLeg_, floatLeg_);
46✔
550
    }
46✔
551

552
    const Handle<YieldTermStructure>&
553
    ConstNotionalCrossCurrencySwapRateHelper::fixedLegDiscountHandle() const {
226✔
554
        return collateralOnFixedLeg_ ? collateralHandle_ : termStructureHandle_;
226✔
555
    }
556

557
    const Handle<YieldTermStructure>&
558
    ConstNotionalCrossCurrencySwapRateHelper::floatingLegDiscountHandle() const {
226✔
559
        return collateralOnFixedLeg_ ? termStructureHandle_ : collateralHandle_;
226✔
560
    }
561

562
    Real ConstNotionalCrossCurrencySwapRateHelper::impliedQuote() const {
226✔
563
        QL_REQUIRE(!termStructureHandle_.empty(), "term structure not set");
226✔
564
        QL_REQUIRE(!collateralHandle_.empty(), "collateral term structure not set");
226✔
565

566
        auto [fixedNpv, fixedBps] =
567
            npvbpsConstNotionalLeg(fixedLeg_, initialNotionalExchangeDate_,
452✔
568
                                   finalNotionalExchangeDate_, fixedLegDiscountHandle());
226✔
569

570
        auto [floatNpv, floatBps] =
571
            npvbpsConstNotionalLeg(floatLeg_, initialNotionalExchangeDate_,
226✔
572
                                   finalNotionalExchangeDate_, floatingLegDiscountHandle());
573

574
        QL_REQUIRE(std::fabs(fixedBps) > 0.0, "null fixed-leg BPS");
226✔
575

576
        return sample_fixed_rate + (floatNpv - fixedNpv) / fixedBps;
226✔
577
    }
578

579
    void ConstNotionalCrossCurrencySwapRateHelper::accept(AcyclicVisitor& v) {
×
580
        auto* v1 = dynamic_cast<Visitor<ConstNotionalCrossCurrencySwapRateHelper>*>(&v);
×
581
        if (v1 != nullptr)
×
UNCOV
582
            v1->visit(*this);
×
583
        else
584
            RateHelper::accept(v);
×
585
    }
×
586

587
    NonDeliverableConstNotionalCrossCurrencySwapRateHelper::
20✔
588
        NonDeliverableConstNotionalCrossCurrencySwapRateHelper(
589
            const Handle<Quote>& fixedRate,
590
            const Period& tenor,
591
            Natural fixingDays,
592
            Calendar calendar,
593
            BusinessDayConvention convention,
594
            bool endOfMonth,
595
            Frequency fixedFrequency,
596
            DayCounter fixedDayCount,
597
            const ext::shared_ptr<IborIndex>& floatIndex,
598
            Handle<YieldTermStructure> collateralCurve,
599
            bool collateralOnFixedLeg,
600
            Frequency floatingFrequency,
601
            Integer paymentLag,
602
            Integer fxFixingDelay)
20✔
603
    : ConstNotionalCrossCurrencySwapRateHelper(fixedRate,
604
                                               tenor,
605
                                               fixingDays,
606
                                               std::move(calendar),
607
                                               convention,
608
                                               endOfMonth,
609
                                               fixedFrequency,
610
                                               std::move(fixedDayCount),
611
                                               floatIndex,
612
                                               std::move(collateralCurve),
613
                                               collateralOnFixedLeg,
614
                                               floatingFrequency,
615
                                               paymentLag),
616
      fxFixingDelay_(fxFixingDelay) {}
60✔
617

618
    Real NonDeliverableConstNotionalCrossCurrencySwapRateHelper::impliedQuote() const {
178✔
619
        QL_REQUIRE(!termStructureHandle_.empty(), "term structure not set");
178✔
620
        QL_REQUIRE(!collateralHandle_.empty(), "collateral term structure not set");
178✔
621

622
        auto [fixedNpv, fixedBps] =
623
            collateralOnFixedLeg_ ?
178✔
624
                npvbpsConstNotionalLeg(fixedLeg_, initialNotionalExchangeDate_,
82✔
625
                                       finalNotionalExchangeDate_, collateralHandle_) :
82✔
626
                npvbpsNonDeliverableConstNotionalLeg(fixedLeg_, initialNotionalExchangeDate_,
96✔
627
                                                     finalNotionalExchangeDate_, fxFixingDelay_,
96✔
628
                                                     paymentLag_, calendar_, collateralHandle_,
96✔
629
                                                     termStructureHandle_);
178✔
630

631
        auto [floatNpv, floatBps] =
632
            !collateralOnFixedLeg_ ?
178✔
633
                npvbpsConstNotionalLeg(floatLeg_, initialNotionalExchangeDate_,
96✔
634
                                       finalNotionalExchangeDate_, collateralHandle_) :
96✔
635
                npvbpsNonDeliverableConstNotionalLeg(floatLeg_, initialNotionalExchangeDate_,
82✔
636
                                                     finalNotionalExchangeDate_, fxFixingDelay_,
82✔
637
                                                     paymentLag_, calendar_, collateralHandle_,
82✔
638
                                                     termStructureHandle_);
178✔
639

640
        QL_REQUIRE(std::fabs(fixedBps) > 0.0, "null fixed-leg BPS");
178✔
641

642
        return sample_fixed_rate + (floatNpv - fixedNpv) / fixedBps;
178✔
643
    }
644

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

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