Coverage Report

Created: 2026-06-08 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/experimental/termstructures/crosscurrencyratehelpers.cpp
Line
Count
Source
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/pricingengines/swap/discountingconstnotionalcrosscurrencyswapengine.hpp>
28
#include <ql/utilities/null_deleter.hpp>
29
#include <utility>
30
31
namespace QuantLib {
32
33
    namespace {
34
35
        constexpr double sample_fixed_rate = 0.01;
36
37
        // Treat an explicitly-passed NoFrequency the same as an unset (nullopt)
38
        // payment frequency.  Before these parameters were migrated to
39
        // ext::optional<Frequency>, NoFrequency was the sentinel meaning "derive
40
        // the schedule from the index tenor".  Normalizing it here preserves that
41
        // behavior and keeps the stored optional either empty or holding an
42
        // actual frequency, so the rest of the code can treat the two cases
43
        // identically.
44
0
        ext::optional<Frequency> normalizedPaymentFrequency(ext::optional<Frequency> frequency) {
45
0
            if (frequency && *frequency == NoFrequency)
46
0
                return ext::nullopt;
47
0
            return frequency;
48
0
        }
49
50
        Schedule legSchedule(const Date& evaluationDate,
51
                             const Period& tenor,
52
                             const Period& frequency,
53
                             Natural fixingDays,
54
                             const Calendar& calendar,
55
                             BusinessDayConvention convention,
56
0
                             bool endOfMonth) {
57
0
            QL_REQUIRE(tenor >= frequency,
58
0
                       "XCCY instrument tenor should not be smaller than coupon frequency.");
59
60
0
            Date referenceDate = calendar.adjust(evaluationDate);
61
0
            Date earliestDate = calendar.advance(referenceDate, fixingDays * Days, convention);
62
0
            Date maturity = earliestDate + tenor;
63
0
            return MakeSchedule()
64
0
                .from(earliestDate)
65
0
                .to(maturity)
66
0
                .withTenor(frequency)
67
0
                .withCalendar(calendar)
68
0
                .withConvention(convention)
69
0
                .endOfMonth(endOfMonth)
70
0
                .backwards();
71
0
        }
72
73
        Leg buildFloatingLeg(const Date& evaluationDate,
74
                         const Period& tenor,
75
                         Natural fixingDays,
76
                         const Calendar& calendar,
77
                         BusinessDayConvention convention,
78
                         bool endOfMonth,
79
                         const ext::shared_ptr<IborIndex>& idx,
80
                         ext::optional<Frequency> paymentFrequency,
81
0
                         Integer paymentLag) {
82
0
            auto overnightIndex = ext::dynamic_pointer_cast<OvernightIndex>(idx);
83
84
0
            Period freqPeriod;
85
0
            if (!paymentFrequency) {
86
0
                QL_REQUIRE(!overnightIndex, "Require payment frequency for overnight indices.");
87
0
                freqPeriod = idx->tenor();
88
0
            } else {
89
0
                freqPeriod = Period(*paymentFrequency);
90
0
            }
91
92
0
            Schedule sch = legSchedule(evaluationDate, tenor, freqPeriod, fixingDays, calendar,
93
0
                                       convention, endOfMonth);
94
0
            if (overnightIndex != nullptr) {
95
0
                return OvernightLeg(sch, overnightIndex)
96
0
                    .withNotionals(1.0)
97
0
                    .withPaymentLag(paymentLag);
98
0
            }
99
0
            return IborLeg(sch, idx).withNotionals(1.0).withPaymentLag(paymentLag);
100
0
        }
101
102
        std::pair<Real, Real>
103
        npvbpsConstNotionalLeg(const Leg& leg,
104
                               const Date& initialNotionalExchangeDate,
105
                               const Date& finalNotionalExchangeDate,
106
0
                               const Handle<YieldTermStructure>& discountCurveHandle) {
107
0
            const Spread basisPoint = 1.0e-4;
108
0
            Date refDt = discountCurveHandle->referenceDate();
109
0
            const YieldTermStructure& discountRef = **discountCurveHandle;
110
0
            bool includeSettleDtFlows = true;
111
0
            auto [npv, bps] = CashFlows::npvbps(leg, discountRef, includeSettleDtFlows, refDt, refDt);
112
            // Include NPV of the notional exchange at start and maturity.
113
0
            npv += (-1.0) * discountRef.discount(initialNotionalExchangeDate);
114
0
            npv += discountRef.discount(finalNotionalExchangeDate);
115
0
            bps /= basisPoint;
116
0
            return { npv, bps };
117
0
        }
118
119
        class ResettingLegHelper {
120
          public:
121
            explicit ResettingLegHelper(const YieldTermStructure& discountCurve,
122
                                        const YieldTermStructure& foreignCurve)
123
0
            : discountCurve_(discountCurve), foreignCurve_(foreignCurve) {}
124
0
            DiscountFactor discount(const Date& d) const {
125
0
                return discountCurve_.discount(d);
126
0
            }
127
0
            Real notionalAdjustment(const Date& d) const {
128
0
                return foreignCurve_.discount(d) / discountCurve_.discount(d);
129
0
            }
130
131
          private:
132
            const YieldTermStructure& discountCurve_;
133
            const YieldTermStructure& foreignCurve_;
134
        };
135
136
        class ResettingLegCalculator : public AcyclicVisitor, public Visitor<Coupon> {
137
          public:
138
            explicit ResettingLegCalculator(const YieldTermStructure& discountCurve,
139
                                            const YieldTermStructure& foreignCurve,
140
                                            Integer paymentLag,
141
                                            Calendar paymentCalendar,
142
                                            BusinessDayConvention convention)
143
0
            : helper_(discountCurve, foreignCurve), paymentLag_(paymentLag),
144
0
              paymentCalendar_(std::move(paymentCalendar)), convention_(convention) {}
145
146
0
            void visit(Coupon& c) override {
147
0
                Date start = c.accrualStartDate();
148
0
                Date end = c.accrualEndDate();
149
0
                Time accrual = c.accrualPeriod();
150
0
                Real adjustedNotional = c.nominal() * helper_.notionalAdjustment(start);
151
152
0
                DiscountFactor discountStart, discountEnd;
153
154
0
                if (paymentLag_ == 0) {
155
0
                    discountStart = helper_.discount(start);
156
0
                    discountEnd = helper_.discount(end);
157
0
                } else {
158
0
                    Date paymentStart =
159
0
                        paymentCalendar_.advance(start, paymentLag_, Days, convention_);
160
0
                    Date paymentEnd = paymentCalendar_.advance(end, paymentLag_, Days, convention_);
161
0
                    discountStart = helper_.discount(paymentStart);
162
0
                    discountEnd = helper_.discount(paymentEnd);
163
0
                }
164
165
                // NPV of a resetting coupon consists of a redemption of borrowed amount occurring
166
                // at the end of the accrual period plus the accrued interest, minus the borrowed
167
                // amount at the start of the period. All amounts are corrected by an adjustment
168
                // corresponding to the implied forward exchange rate, which is estimated by
169
                // the ratio of foreign and domestic curves discount factors.
170
0
                Real npvRedeemedAmount =
171
0
                    adjustedNotional * discountEnd * (1.0 + c.rate() * accrual);
172
0
                Real npvBorrowedAmount = -adjustedNotional * discountStart;
173
174
0
                npv_ += (npvRedeemedAmount + npvBorrowedAmount);
175
0
                bps_ += adjustedNotional * discountEnd * accrual;
176
0
            }
177
0
            Real NPV() const { return npv_; }
178
0
            Real BPS() const { return bps_; }
179
180
          private:
181
            ResettingLegHelper helper_;
182
            Real npv_ = 0.0;
183
            Real bps_ = 0.0;
184
            Integer paymentLag_;
185
            Calendar paymentCalendar_;
186
            BusinessDayConvention convention_;
187
        };
188
189
        std::pair<Real, Real> npvbpsResettingLeg(const Leg& iborLeg,
190
                                                 Integer paymentLag,
191
                                                 const Calendar& paymentCalendar,
192
                                                 BusinessDayConvention convention,
193
                                                 const Handle<YieldTermStructure>& discountCurveHandle,
194
0
                                                 const Handle<YieldTermStructure>& foreignCurveHandle) {
195
0
            const YieldTermStructure& discountCurveRef = **discountCurveHandle;
196
0
            const YieldTermStructure& foreignCurveRef = **foreignCurveHandle;
197
198
0
            ResettingLegCalculator calc(discountCurveRef, foreignCurveRef, paymentLag,
199
0
                                        paymentCalendar, convention);
200
0
            for (const auto& i : iborLeg) {
201
0
                CashFlow& cf = *i;
202
0
                cf.accept(calc);
203
0
            }
204
0
            return { calc.NPV(), calc.BPS() };
205
0
        }
206
    }
207
208
209
    CrossCurrencySwapRateHelperBase::CrossCurrencySwapRateHelperBase(
210
        const Handle<Quote>& quote,
211
        const Period& tenor,
212
        Natural fixingDays,
213
        Calendar calendar,
214
        BusinessDayConvention convention,
215
        bool endOfMonth,
216
        Handle<YieldTermStructure> collateralCurve,
217
        Integer paymentLag)
218
0
    : RelativeDateRateHelper(quote), tenor_(tenor), fixingDays_(fixingDays),
219
0
      calendar_(std::move(calendar)), convention_(convention), endOfMonth_(endOfMonth),
220
0
      paymentLag_(paymentLag), collateralHandle_(std::move(collateralCurve)) {
221
0
        registerWith(collateralHandle_);
222
0
    }
223
224
0
    void CrossCurrencySwapRateHelperBase::setTermStructure(YieldTermStructure* t) {
225
        // do not set the relinkable handle as an observer -
226
        // force recalculation when needed
227
0
        bool observer = false;
228
229
0
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
230
0
        termStructureHandle_.linkTo(temp, observer);
231
232
0
        RelativeDateRateHelper::setTermStructure(t);
233
0
    }
234
235
    void CrossCurrencySwapRateHelperBase::initializeDatesFromLegs(const Leg& firstLeg,
236
0
                                                                  const Leg& secondLeg) {
237
0
        earliestDate_ = std::min(CashFlows::startDate(firstLeg),
238
0
                                 CashFlows::startDate(secondLeg));
239
240
0
        maturityDate_ = std::max(CashFlows::maturityDate(firstLeg),
241
0
                                 CashFlows::maturityDate(secondLeg));
242
243
0
        if (paymentLag_ == 0) {
244
0
            initialNotionalExchangeDate_ = earliestDate_;
245
0
            finalNotionalExchangeDate_   = maturityDate_;
246
0
        } else {
247
0
            initialNotionalExchangeDate_ = calendar_.advance(earliestDate_, paymentLag_, Days, convention_);
248
0
            finalNotionalExchangeDate_   = calendar_.advance(maturityDate_, paymentLag_, Days, convention_);
249
0
        }
250
251
0
        Date lastPaymentDate =
252
0
            std::max(firstLeg.back()->date(),
253
0
                     secondLeg.back()->date());
254
255
0
        latestRelevantDate_ = latestDate_ = std::max(maturityDate_, lastPaymentDate);
256
0
    }
257
258
259
260
    CrossCurrencyBasisSwapRateHelperBase::CrossCurrencyBasisSwapRateHelperBase(
261
        const Handle<Quote>& basis,
262
        const Period& tenor,
263
        Natural fixingDays,
264
        Calendar calendar,
265
        BusinessDayConvention convention,
266
        bool endOfMonth,
267
        ext::shared_ptr<IborIndex> baseCurrencyIndex,
268
        ext::shared_ptr<IborIndex> quoteCurrencyIndex,
269
        Handle<YieldTermStructure> collateralCurve,
270
        bool isFxBaseCurrencyCollateralCurrency,
271
        bool isBasisOnFxBaseCurrencyLeg,
272
        ext::optional<Frequency> paymentFrequency,
273
        Integer paymentLag,
274
        ext::optional<Frequency> quoteCurrencyPaymentFrequency)
275
0
    : CrossCurrencySwapRateHelperBase(basis, tenor, fixingDays, std::move(calendar), convention, endOfMonth,
276
0
                                      std::move(collateralCurve), paymentLag),
277
0
      baseCcyIdx_(std::move(baseCurrencyIndex)), quoteCcyIdx_(std::move(quoteCurrencyIndex)),
278
0
      isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency),
279
0
      isBasisOnFxBaseCurrencyLeg_(isBasisOnFxBaseCurrencyLeg),
280
0
      paymentFrequency_(normalizedPaymentFrequency(paymentFrequency)),
281
0
      quoteCcyPaymentFrequency_(normalizedPaymentFrequency(quoteCurrencyPaymentFrequency)) {
282
0
        registerWith(baseCcyIdx_);
283
0
        registerWith(quoteCcyIdx_);
284
285
0
        CrossCurrencyBasisSwapRateHelperBase::initializeDates();
286
0
    }
287
288
0
    void CrossCurrencyBasisSwapRateHelperBase::initializeDates() {
289
0
        baseCcyIborLeg_ = buildFloatingLeg(evaluationDate_, tenor_, fixingDays_, calendar_, convention_,
290
0
                                           endOfMonth_, baseCcyIdx_, paymentFrequency_, paymentLag_);
291
292
        // If no quote-currency payment frequency was given, fall back to the
293
        // base-currency payment frequency (which may itself be unset, in which
294
        // case the quote-currency leg uses its own index tenor).
295
0
        ext::optional<Frequency> effectiveQuoteCcyFreq =
296
0
            quoteCcyPaymentFrequency_ ? quoteCcyPaymentFrequency_ : paymentFrequency_;
297
0
        quoteCcyIborLeg_ = buildFloatingLeg(evaluationDate_, tenor_, fixingDays_, calendar_,
298
0
                                            convention_, endOfMonth_, quoteCcyIdx_, effectiveQuoteCcyFreq, paymentLag_);
299
300
0
        initializeDatesFromLegs(baseCcyIborLeg_, quoteCcyIborLeg_);
301
0
    }
302
303
    const Handle<YieldTermStructure>&
304
0
    CrossCurrencyBasisSwapRateHelperBase::baseCcyLegDiscountHandle() const {
305
0
        return isFxBaseCurrencyCollateralCurrency_ ? collateralHandle_ : termStructureHandle_;
306
0
    }
307
308
    const Handle<YieldTermStructure>&
309
0
    CrossCurrencyBasisSwapRateHelperBase::quoteCcyLegDiscountHandle() const {
310
0
        return isFxBaseCurrencyCollateralCurrency_ ? termStructureHandle_ : collateralHandle_;
311
0
    }
312
313
    ConstNotionalCrossCurrencyBasisSwapRateHelper::ConstNotionalCrossCurrencyBasisSwapRateHelper(
314
        const Handle<Quote>& basis,
315
        const Period& tenor,
316
        Natural fixingDays,
317
        const Calendar& calendar,
318
        BusinessDayConvention convention,
319
        bool endOfMonth,
320
        const ext::shared_ptr<IborIndex>& baseCurrencyIndex,
321
        const ext::shared_ptr<IborIndex>& quoteCurrencyIndex,
322
        const Handle<YieldTermStructure>& collateralCurve,
323
        bool isFxBaseCurrencyCollateralCurrency,
324
        bool isBasisOnFxBaseCurrencyLeg,
325
        ext::optional<Frequency> paymentFrequency,
326
        Integer paymentLag,
327
        ext::optional<Frequency> quoteCurrencyPaymentFrequency)
328
0
    : CrossCurrencyBasisSwapRateHelperBase(basis,
329
0
                                           tenor,
330
0
                                           fixingDays,
331
0
                                           calendar,
332
0
                                           convention,
333
0
                                           endOfMonth,
334
0
                                           baseCurrencyIndex,
335
0
                                           quoteCurrencyIndex,
336
0
                                           collateralCurve,
337
0
                                           isFxBaseCurrencyCollateralCurrency,
338
0
                                           isBasisOnFxBaseCurrencyLeg,
339
0
                                           paymentFrequency,
340
0
                                           paymentLag,
341
0
                                           quoteCurrencyPaymentFrequency) {}
342
343
0
    Real ConstNotionalCrossCurrencyBasisSwapRateHelper::impliedQuote() const {
344
0
        QL_REQUIRE(!termStructureHandle_.empty(), "term structure not set");
345
0
        QL_REQUIRE(!collateralHandle_.empty(), "collateral term structure not set");
346
347
0
        auto [npvBaseCcy, bpsBaseCcy] = npvbpsConstNotionalLeg(baseCcyIborLeg_, initialNotionalExchangeDate_, finalNotionalExchangeDate_, baseCcyLegDiscountHandle());
348
0
        auto [npvQuoteCcy, bpsQuoteCcy] = npvbpsConstNotionalLeg(quoteCcyIborLeg_, initialNotionalExchangeDate_, finalNotionalExchangeDate_, quoteCcyLegDiscountHandle());
349
350
0
        Real bps = isBasisOnFxBaseCurrencyLeg_ ? -bpsBaseCcy : bpsQuoteCcy;
351
352
0
        QL_REQUIRE(std::fabs(bps) > 0.0, "null BPS");
353
354
0
        return -(npvQuoteCcy - npvBaseCcy) / bps;
355
0
    }
356
357
0
    void ConstNotionalCrossCurrencyBasisSwapRateHelper::accept(AcyclicVisitor& v) {
358
0
        auto* v1 = dynamic_cast<Visitor<ConstNotionalCrossCurrencyBasisSwapRateHelper>*>(&v);
359
0
        if (v1 != nullptr)
360
0
            v1->visit(*this);
361
0
        else
362
0
            RateHelper::accept(v);
363
0
    }
364
365
366
    MtMCrossCurrencyBasisSwapRateHelper::MtMCrossCurrencyBasisSwapRateHelper(
367
        const Handle<Quote>& basis,
368
        const Period& tenor,
369
        Natural fixingDays,
370
        const Calendar& calendar,
371
        BusinessDayConvention convention,
372
        bool endOfMonth,
373
        const ext::shared_ptr<IborIndex>& baseCurrencyIndex,
374
        const ext::shared_ptr<IborIndex>& quoteCurrencyIndex,
375
        const Handle<YieldTermStructure>& collateralCurve,
376
        bool isFxBaseCurrencyCollateralCurrency,
377
        bool isBasisOnFxBaseCurrencyLeg,
378
        bool isFxBaseCurrencyLegResettable,
379
        ext::optional<Frequency> paymentFrequency,
380
        Integer paymentLag,
381
        ext::optional<Frequency> quoteCurrencyPaymentFrequency)
382
0
    : CrossCurrencyBasisSwapRateHelperBase(basis,
383
0
                                           tenor,
384
0
                                           fixingDays,
385
0
                                           calendar,
386
0
                                           convention,
387
0
                                           endOfMonth,
388
0
                                           baseCurrencyIndex,
389
0
                                           quoteCurrencyIndex,
390
0
                                           collateralCurve,
391
0
                                           isFxBaseCurrencyCollateralCurrency,
392
0
                                           isBasisOnFxBaseCurrencyLeg,
393
0
                                           paymentFrequency,
394
0
                                           paymentLag,
395
0
                                           quoteCurrencyPaymentFrequency),
396
0
      isFxBaseCurrencyLegResettable_(isFxBaseCurrencyLegResettable) {}
397
398
0
    Real MtMCrossCurrencyBasisSwapRateHelper::impliedQuote() const {
399
0
        QL_REQUIRE(!termStructureHandle_.empty(), "term structure not set");
400
0
        QL_REQUIRE(!collateralHandle_.empty(), "collateral term structure not set");
401
402
0
        auto [npvBaseCcy, bpsBaseCcy] =
403
0
            isFxBaseCurrencyLegResettable_ ?
404
0
                npvbpsResettingLeg(baseCcyIborLeg_, paymentLag_, calendar_, convention_,
405
0
                                   baseCcyLegDiscountHandle(), quoteCcyLegDiscountHandle()) :
406
0
                npvbpsConstNotionalLeg(baseCcyIborLeg_, initialNotionalExchangeDate_,
407
0
                                       finalNotionalExchangeDate_, baseCcyLegDiscountHandle());
408
409
0
        auto [npvQuoteCcy, bpsQuoteCcy] =
410
0
            isFxBaseCurrencyLegResettable_ ?
411
0
                npvbpsConstNotionalLeg(quoteCcyIborLeg_, initialNotionalExchangeDate_,
412
0
                                       finalNotionalExchangeDate_, quoteCcyLegDiscountHandle()) :
413
0
                npvbpsResettingLeg(quoteCcyIborLeg_, paymentLag_, calendar_, convention_,
414
0
                                   quoteCcyLegDiscountHandle(), baseCcyLegDiscountHandle());
415
416
0
        Real bps = isBasisOnFxBaseCurrencyLeg_ ? -bpsBaseCcy : bpsQuoteCcy;
417
418
0
        QL_REQUIRE(std::fabs(bps) > 0.0, "null BPS");
419
420
0
        return -(npvQuoteCcy - npvBaseCcy) / bps;
421
0
    }
422
423
0
    void MtMCrossCurrencyBasisSwapRateHelper::accept(AcyclicVisitor& v) {
424
0
        auto* v1 = dynamic_cast<Visitor<MtMCrossCurrencyBasisSwapRateHelper>*>(&v);
425
0
        if (v1 != nullptr)
426
0
            v1->visit(*this);
427
0
        else
428
0
            RateHelper::accept(v);
429
0
    }
430
431
432
    ConstNotionalCrossCurrencySwapRateHelper::ConstNotionalCrossCurrencySwapRateHelper(
433
        const Handle<Quote>& fixedRate,
434
        const Period& tenor,
435
        Natural fixingDays,
436
        const Calendar& calendar,
437
        BusinessDayConvention convention,
438
        bool endOfMonth,
439
        Frequency fixedFrequency,
440
        DayCounter  fixedDayCount,
441
        const ext::shared_ptr<IborIndex>& floatIndex,
442
        const Handle<YieldTermStructure>& collateralCurve,
443
        bool collateralOnFixedLeg,
444
        Integer paymentLag)
445
0
    : CrossCurrencySwapRateHelperBase(fixedRate, tenor, fixingDays, calendar, convention, endOfMonth,
446
0
                                      collateralCurve, paymentLag),
447
0
      fixedFrequency_(fixedFrequency),
448
0
      fixedDayCount_(std::move(fixedDayCount)),
449
0
      floatIndex_(floatIndex),
450
0
      collateralOnFixedLeg_(collateralOnFixedLeg) {
451
452
0
        QL_REQUIRE(floatIndex_, "floating index required");
453
0
        registerWith(floatIndex_);
454
455
0
        initializeDates();
456
0
    }
457
458
0
    void ConstNotionalCrossCurrencySwapRateHelper::initializeDates() {
459
0
        auto overnightIndex = ext::dynamic_pointer_cast<OvernightIndex>(floatIndex_);
460
461
0
        Period floatFreqPeriod;
462
0
        if (floatIndex_->tenor().frequency() == NoFrequency) {
463
0
            QL_REQUIRE(!overnightIndex, "Require payment frequency for overnight indices.");
464
0
            floatFreqPeriod = floatIndex_->tenor();
465
0
        } else {
466
0
            floatFreqPeriod = Period(floatIndex_->tenor().frequency());
467
0
        }
468
469
0
        Real nominal = 1.0;
470
0
        Schedule fixedSch = legSchedule(evaluationDate_, tenor_, Period(fixedFrequency_), fixingDays_, calendar_,
471
0
                                       convention_, endOfMonth_);
472
0
        Schedule floatSch = legSchedule(evaluationDate_, tenor_, floatFreqPeriod, fixingDays_, floatIndex_->fixingCalendar(),
473
0
                                    floatIndex_->businessDayConvention(), endOfMonth_);
474
475
0
        xccySwap_ = ext::make_shared<ConstNotionalCrossCurrencyFixedVsFloatingSwap>(
476
0
            Swap::Payer,
477
0
            nominal,
478
0
            Currency(), 
479
0
            fixedSch,
480
0
            sample_fixed_rate,
481
0
            fixedDayCount_,
482
0
            convention_,
483
0
            paymentLag_,
484
0
            calendar_,
485
0
            nominal,
486
0
            floatIndex_->currency(),
487
0
            floatSch,
488
0
            floatIndex_,
489
0
            Spread(0.0),
490
0
            floatIndex_->businessDayConvention(),
491
0
            paymentLag_,
492
0
            calendar_
493
0
        );
494
0
        auto engine = ext::make_shared<DiscountingConstNotionalCrossCurrencySwapEngine>(
495
0
            floatIndex_->currency(), floatingLegDiscountHandle(),
496
0
            Currency(), fixedLegDiscountHandle(),
497
0
            makeQuoteHandle(1.0), true);
498
0
        xccySwap_->setPricingEngine(engine);
499
500
0
        initializeDatesFromLegs(xccySwap_->leg(0), xccySwap_->leg(1));
501
0
    }
502
503
    const Handle<YieldTermStructure>&
504
0
    ConstNotionalCrossCurrencySwapRateHelper::fixedLegDiscountHandle() const {
505
0
        return collateralOnFixedLeg_ ? collateralHandle_ : termStructureHandle_;
506
0
    }
507
508
    const Handle<YieldTermStructure>&
509
0
    ConstNotionalCrossCurrencySwapRateHelper::floatingLegDiscountHandle() const {
510
0
        return collateralOnFixedLeg_ ? termStructureHandle_ : collateralHandle_;
511
0
    }
512
513
0
    Real ConstNotionalCrossCurrencySwapRateHelper::impliedQuote() const {
514
0
        QL_REQUIRE(!termStructureHandle_.empty(), "term structure not set");
515
0
        QL_REQUIRE(!collateralHandle_.empty(), "collateral term structure not set");
516
0
        xccySwap_->deepUpdate();
517
518
0
        return xccySwap_->fairRate();
519
0
    }
520
521
0
    void ConstNotionalCrossCurrencySwapRateHelper::accept(AcyclicVisitor& v) {
522
0
        auto* v1 = dynamic_cast<Visitor<ConstNotionalCrossCurrencySwapRateHelper>*>(&v);
523
0
        if (v1 != nullptr)
524
0
            v1->visit(*this);
525
0
        else
526
0
            RateHelper::accept(v);
527
0
    }
528
529
}