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

lballabio / QuantLib / 19787828006

29 Nov 2025 06:42PM UTC coverage: 74.352% (+0.4%) from 73.914%
19787828006

Pull #2344

github

web-flow
Merge e103123e2 into 08ef06893
Pull Request #2344: add multicurve bootstrap

117 of 131 new or added lines in 6 files covered. (89.31%)

413 existing lines in 27 files now uncovered.

57224 of 76964 relevant lines covered (74.35%)

8799680.15 hits per line

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

90.72
/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,
419✔
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,
421✔
44
                       "XCCY instrument tenor should not be smaller than coupon frequency.");
45

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

59
        Leg buildFloatingLeg(const Date& evaluationDate,
409✔
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);
409✔
69

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

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

88
        Leg buildFixedLeg(const Date& evaluationDate,
11✔
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
            Period freqPeriod = Period(paymentFrequency);
11✔
99

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

108
        std::pair<Real, Real>
109
        npvbpsConstNotionalLeg(const Leg& leg,
2,772✔
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();
2,772✔
115
            const YieldTermStructure& discountRef = **discountCurveHandle;
2,772✔
116
            bool includeSettleDtFlows = true;
117
            auto [npv, bps] = CashFlows::npvbps(leg, discountRef, includeSettleDtFlows, refDt, refDt);
2,772✔
118
            // Include NPV of the notional exchange at start and maturity.
119
            npv += (-1.0) * discountRef.discount(initialNotionalExchangeDate);
2,772✔
120
            npv += discountRef.discount(finalNotionalExchangeDate);
2,772✔
121
            bps /= basisPoint;
2,772✔
122
            return { npv, bps };
2,772✔
123
        }
124

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

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

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

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

158
                DiscountFactor discountStart, discountEnd;
159

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

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

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

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

195
        std::pair<Real, Real> npvbpsResettingLeg(const Leg& iborLeg,
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 : iborLeg) {
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

214

215
    CrossCurrencySwapRateHelperBase::CrossCurrencySwapRateHelperBase(
211✔
216
        const Handle<Quote>& quote,
217
        const Period& tenor,
218
        Natural fixingDays,
219
        Calendar calendar,
220
        BusinessDayConvention convention,
221
        bool endOfMonth,
222
        Handle<YieldTermStructure> collateralCurve,
223
        Integer paymentLag)
211✔
224
    : RelativeDateRateHelper(quote), tenor_(tenor), fixingDays_(fixingDays),
211✔
225
      calendar_(std::move(calendar)), convention_(convention), endOfMonth_(endOfMonth),
211✔
226
      paymentLag_(paymentLag), collateralHandle_(std::move(collateralCurve)) {
422✔
227
        registerWith(collateralHandle_);
211✔
228
    }
211✔
229

230
    void CrossCurrencySwapRateHelperBase::setTermStructure(YieldTermStructure* t) {
209✔
231
        // do not set the relinkable handle as an observer -
232
        // force recalculation when needed
233
        bool observer = false;
234

235
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
236
        termStructureHandle_.linkTo(temp, observer);
209✔
237

238
        RelativeDateRateHelper::setTermStructure(t);
209✔
239
    }
209✔
240

241
    void CrossCurrencySwapRateHelperBase::initializeDatesFromLegs(const Leg& firstLeg,
209✔
242
                                                                  const Leg& secondLeg) {
243
        earliestDate_ = std::min(CashFlows::startDate(firstLeg),
209✔
244
                                 CashFlows::startDate(secondLeg));
209✔
245

246
        maturityDate_ = std::max(CashFlows::maturityDate(firstLeg),
209✔
247
                                 CashFlows::maturityDate(secondLeg));
209✔
248

249
        if (paymentLag_ == 0) {
209✔
250
            initialNotionalExchangeDate_ = earliestDate_;
193✔
251
            finalNotionalExchangeDate_   = maturityDate_;
193✔
252
        } else {
253
            initialNotionalExchangeDate_ = calendar_.advance(earliestDate_, paymentLag_, Days, convention_);
16✔
254
            finalNotionalExchangeDate_   = calendar_.advance(maturityDate_, paymentLag_, Days, convention_);
16✔
255
        }
256

257
        Date lastPaymentDate =
258
            std::max(firstLeg.back()->date(),
209✔
259
                     secondLeg.back()->date());
209✔
260

261
        latestRelevantDate_ = latestDate_ = std::max(maturityDate_, lastPaymentDate);
209✔
262
    }
209✔
263

264

265

266
    CrossCurrencyBasisSwapRateHelperBase::CrossCurrencyBasisSwapRateHelperBase(
200✔
267
        const Handle<Quote>& basis,
268
        const Period& tenor,
269
        Natural fixingDays,
270
        Calendar calendar,
271
        BusinessDayConvention convention,
272
        bool endOfMonth,
273
        ext::shared_ptr<IborIndex> baseCurrencyIndex,
274
        ext::shared_ptr<IborIndex> quoteCurrencyIndex,
275
        Handle<YieldTermStructure> collateralCurve,
276
        bool isFxBaseCurrencyCollateralCurrency,
277
        bool isBasisOnFxBaseCurrencyLeg,
278
        Frequency paymentFrequency,
279
        Integer paymentLag)
200✔
280
    : CrossCurrencySwapRateHelperBase(basis, tenor, fixingDays, calendar, convention, endOfMonth,
281
                                      std::move(collateralCurve), paymentLag),
282
      baseCcyIdx_(std::move(baseCurrencyIndex)), quoteCcyIdx_(std::move(quoteCurrencyIndex)),
283
      isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency),
200✔
284
      isBasisOnFxBaseCurrencyLeg_(isBasisOnFxBaseCurrencyLeg),
200✔
285
      paymentFrequency_(paymentFrequency) {
400✔
286
        registerWith(baseCcyIdx_);
400✔
287
        registerWith(quoteCcyIdx_);
200✔
288

289
        CrossCurrencyBasisSwapRateHelperBase::initializeDates();
200✔
290
    }
204✔
291

292
    void CrossCurrencyBasisSwapRateHelperBase::initializeDates() {
200✔
293
        baseCcyIborLeg_ = buildFloatingLeg(evaluationDate_, tenor_, fixingDays_, calendar_, convention_,
198✔
294
                                           endOfMonth_, baseCcyIdx_, paymentFrequency_, paymentLag_);
200✔
295

296
        quoteCcyIborLeg_ = buildFloatingLeg(evaluationDate_, tenor_, fixingDays_, calendar_,
198✔
297
                                            convention_, endOfMonth_, quoteCcyIdx_, paymentFrequency_, paymentLag_);
198✔
298

299
        initializeDatesFromLegs(baseCcyIborLeg_, quoteCcyIborLeg_);
198✔
300
    }
198✔
301

302
    const Handle<YieldTermStructure>&
303
    CrossCurrencyBasisSwapRateHelperBase::baseCcyLegDiscountHandle() const {
2,087✔
304
        return isFxBaseCurrencyCollateralCurrency_ ? collateralHandle_ : termStructureHandle_;
2,087✔
305
    }
306

307
    const Handle<YieldTermStructure>&
308
    CrossCurrencyBasisSwapRateHelperBase::quoteCcyLegDiscountHandle() const {
1,809✔
309
        return isFxBaseCurrencyCollateralCurrency_ ? termStructureHandle_ : collateralHandle_;
1,809✔
310
    }
311

312
    ConstNotionalCrossCurrencyBasisSwapRateHelper::ConstNotionalCrossCurrencyBasisSwapRateHelper(
122✔
313
        const Handle<Quote>& basis,
314
        const Period& tenor,
315
        Natural fixingDays,
316
        const Calendar& calendar,
317
        BusinessDayConvention convention,
318
        bool endOfMonth,
319
        const ext::shared_ptr<IborIndex>& baseCurrencyIndex,
320
        const ext::shared_ptr<IborIndex>& quoteCurrencyIndex,
321
        const Handle<YieldTermStructure>& collateralCurve,
322
        bool isFxBaseCurrencyCollateralCurrency,
323
        bool isBasisOnFxBaseCurrencyLeg,
324
        Frequency paymentFrequency,
325
        Integer paymentLag)
122✔
326
    : CrossCurrencyBasisSwapRateHelperBase(basis,
327
                                           tenor,
328
                                           fixingDays,
329
                                           calendar,
330
                                           convention,
331
                                           endOfMonth,
332
                                           baseCurrencyIndex,
333
                                           quoteCurrencyIndex,
334
                                           collateralCurve,
335
                                           isFxBaseCurrencyCollateralCurrency,
336
                                           isBasisOnFxBaseCurrencyLeg,
337
                                           paymentFrequency,
338
                                           paymentLag) {}
368✔
339

340
    Real ConstNotionalCrossCurrencyBasisSwapRateHelper::impliedQuote() const {
970✔
341
        QL_REQUIRE(!termStructureHandle_.empty(), "term structure not set");
970✔
342
        QL_REQUIRE(!collateralHandle_.empty(), "collateral term structure not set");
970✔
343

344
        auto [npvBaseCcy, bpsBaseCcy] = npvbpsConstNotionalLeg(baseCcyIborLeg_, initialNotionalExchangeDate_, finalNotionalExchangeDate_, baseCcyLegDiscountHandle());
970✔
345
        auto [npvQuoteCcy, bpsQuoteCcy] = npvbpsConstNotionalLeg(quoteCcyIborLeg_, initialNotionalExchangeDate_, finalNotionalExchangeDate_, quoteCcyLegDiscountHandle());
970✔
346

347
        Real bps = isBasisOnFxBaseCurrencyLeg_ ? -bpsBaseCcy : bpsQuoteCcy;
970✔
348

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

351
        return -(npvQuoteCcy - npvBaseCcy) / bps;
970✔
352
    }
353

UNCOV
354
    void ConstNotionalCrossCurrencyBasisSwapRateHelper::accept(AcyclicVisitor& v) {
×
UNCOV
355
        auto* v1 = dynamic_cast<Visitor<ConstNotionalCrossCurrencyBasisSwapRateHelper>*>(&v);
×
UNCOV
356
        if (v1 != nullptr)
×
UNCOV
357
            v1->visit(*this);
×
358
        else
359
            RateHelper::accept(v);
×
360
    }
×
361

362

363
    MtMCrossCurrencyBasisSwapRateHelper::MtMCrossCurrencyBasisSwapRateHelper(
78✔
364
        const Handle<Quote>& basis,
365
        const Period& tenor,
366
        Natural fixingDays,
367
        const Calendar& calendar,
368
        BusinessDayConvention convention,
369
        bool endOfMonth,
370
        const ext::shared_ptr<IborIndex>& baseCurrencyIndex,
371
        const ext::shared_ptr<IborIndex>& quoteCurrencyIndex,
372
        const Handle<YieldTermStructure>& collateralCurve,
373
        bool isFxBaseCurrencyCollateralCurrency,
374
        bool isBasisOnFxBaseCurrencyLeg,
375
        bool isFxBaseCurrencyLegResettable,
376
        Frequency paymentFrequency,
377
        Integer paymentLag)
78✔
378
    : CrossCurrencyBasisSwapRateHelperBase(basis,
379
                                           tenor,
380
                                           fixingDays,
381
                                           calendar,
382
                                           convention,
383
                                           endOfMonth,
384
                                           baseCurrencyIndex,
385
                                           quoteCurrencyIndex,
386
                                           collateralCurve,
387
                                           isFxBaseCurrencyCollateralCurrency,
388
                                           isBasisOnFxBaseCurrencyLeg,
389
                                           paymentFrequency,
390
                                           paymentLag),
391
      isFxBaseCurrencyLegResettable_(isFxBaseCurrencyLegResettable) {}
236✔
392

393
    Real MtMCrossCurrencyBasisSwapRateHelper::impliedQuote() const {
652✔
394
        QL_REQUIRE(!termStructureHandle_.empty(), "term structure not set");
652✔
395
        QL_REQUIRE(!collateralHandle_.empty(), "collateral term structure not set");
652✔
396

397
        auto [npvBaseCcy, bpsBaseCcy] =
398
            isFxBaseCurrencyLegResettable_ ?
652✔
399
                npvbpsResettingLeg(baseCcyIborLeg_, paymentLag_, calendar_, convention_,
187✔
400
                                   baseCcyLegDiscountHandle(), quoteCcyLegDiscountHandle()) :
401
                npvbpsConstNotionalLeg(baseCcyIborLeg_, initialNotionalExchangeDate_,
465✔
402
                                       finalNotionalExchangeDate_, baseCcyLegDiscountHandle());
652✔
403

404
        auto [npvQuoteCcy, bpsQuoteCcy] =
405
            isFxBaseCurrencyLegResettable_ ?
652✔
406
                npvbpsConstNotionalLeg(quoteCcyIborLeg_, initialNotionalExchangeDate_,
187✔
407
                                       finalNotionalExchangeDate_, quoteCcyLegDiscountHandle()) :
187✔
408
                npvbpsResettingLeg(quoteCcyIborLeg_, paymentLag_, calendar_, convention_,
465✔
409
                                   quoteCcyLegDiscountHandle(), baseCcyLegDiscountHandle());
652✔
410

411
        Real bps = isBasisOnFxBaseCurrencyLeg_ ? -bpsBaseCcy : bpsQuoteCcy;
652✔
412

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

415
        return -(npvQuoteCcy - npvBaseCcy) / bps;
652✔
416
    }
417

UNCOV
418
    void MtMCrossCurrencyBasisSwapRateHelper::accept(AcyclicVisitor& v) {
×
UNCOV
419
        auto* v1 = dynamic_cast<Visitor<MtMCrossCurrencyBasisSwapRateHelper>*>(&v);
×
UNCOV
420
        if (v1 != nullptr)
×
UNCOV
421
            v1->visit(*this);
×
422
        else
UNCOV
423
            RateHelper::accept(v);
×
UNCOV
424
    }
×
425

426

427
    ConstNotionalCrossCurrencySwapRateHelper::ConstNotionalCrossCurrencySwapRateHelper(
11✔
428
        const Handle<Quote>& fixedRate,
429
        const Period& tenor,
430
        Natural fixingDays,
431
        const Calendar& calendar,
432
        BusinessDayConvention convention,
433
        bool endOfMonth,
434
        Frequency fixedFrequency,
435
        const DayCounter& fixedDayCount,
436
        const ext::shared_ptr<IborIndex>& floatIndex,
437
        const Handle<YieldTermStructure>& collateralCurve,
438
        bool collateralOnFixedLeg,
439
        Integer paymentLag)
11✔
440
    : CrossCurrencySwapRateHelperBase(fixedRate, tenor, fixingDays, calendar, convention, endOfMonth,
441
                                      std::move(collateralCurve), paymentLag),
442
      fixedFrequency_(fixedFrequency),
11✔
443
      fixedDayCount_(fixedDayCount),
444
      floatIndex_(floatIndex),
445
      collateralOnFixedLeg_(collateralOnFixedLeg) {
33✔
446

447
        QL_REQUIRE(floatIndex_, "floating index required");
11✔
448
        registerWith(floatIndex_);
11✔
449

450
        initializeDates();
11✔
451
    }
11✔
452

453
    void ConstNotionalCrossCurrencySwapRateHelper::initializeDates() {
11✔
454
        fixedLeg_ = buildFixedLeg(evaluationDate_, tenor_, fixingDays_, calendar_, convention_,
11✔
455
                                  endOfMonth_, fixedFrequency_, fixedDayCount_, paymentLag_);
11✔
456
        floatLeg_ = buildFloatingLeg(evaluationDate_, tenor_, fixingDays_, floatIndex_->fixingCalendar(),
22✔
457
                                     floatIndex_->businessDayConvention(), endOfMonth_,
11✔
458
                                     floatIndex_, floatIndex_->tenor().frequency(), paymentLag_);
22✔
459

460
        initializeDatesFromLegs(fixedLeg_, floatLeg_);
11✔
461
    }
11✔
462

463
    const Handle<YieldTermStructure>&
464
    ConstNotionalCrossCurrencySwapRateHelper::fixedLegDiscountHandle() const {
90✔
465
        return collateralOnFixedLeg_ ? collateralHandle_ : termStructureHandle_;
90✔
466
    }
467

468
    const Handle<YieldTermStructure>&
469
    ConstNotionalCrossCurrencySwapRateHelper::floatingLegDiscountHandle() const {
90✔
470
        return collateralOnFixedLeg_ ? termStructureHandle_ : collateralHandle_;
90✔
471
    }
472

473
    Real ConstNotionalCrossCurrencySwapRateHelper::impliedQuote() const {
90✔
474
        QL_REQUIRE(!termStructureHandle_.empty(), "term structure not set");
90✔
475
        QL_REQUIRE(!collateralHandle_.empty(), "collateral term structure not set");
90✔
476

477
        auto [fixedNpv, fixedBps] = npvbpsConstNotionalLeg(
180✔
478
            fixedLeg_, initialNotionalExchangeDate_, finalNotionalExchangeDate_, fixedLegDiscountHandle());
90✔
479

480
        auto [floatNpv, floatBps] = npvbpsConstNotionalLeg(
90✔
481
            floatLeg_, initialNotionalExchangeDate_, finalNotionalExchangeDate_, floatingLegDiscountHandle());
90✔
482

483
        QL_REQUIRE(std::fabs(fixedBps) > 0.0, "null fixed-leg BPS");
90✔
484

485
        return sample_fixed_rate + (floatNpv - fixedNpv) / fixedBps;
90✔
486
    }
487

UNCOV
488
    void ConstNotionalCrossCurrencySwapRateHelper::accept(AcyclicVisitor& v) {
×
UNCOV
489
        auto* v1 = dynamic_cast<Visitor<ConstNotionalCrossCurrencySwapRateHelper>*>(&v);
×
UNCOV
490
        if (v1 != nullptr)
×
UNCOV
491
            v1->visit(*this);
×
492
        else
UNCOV
493
            RateHelper::accept(v);
×
UNCOV
494
    }
×
495

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