Coverage Report

Created: 2026-10-31 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/experimental/termstructures/crosscurrencyratehelpers.hpp
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
 Copyright (C) 2026 Kyrylo Protsenko
7
8
 This file is part of QuantLib, a free-software/open-source library
9
 for financial quantitative analysts and developers - http://quantlib.org/
10
11
 QuantLib is free software: you can redistribute it and/or modify it
12
 under the terms of the QuantLib license.  You should have received a
13
 copy of the license along with this program; if not, please email
14
 <quantlib-dev@lists.sf.net>. The license is also available online at
15
 <https://www.quantlib.org/license.shtml>.
16
17
 This program is distributed in the hope that it will be useful, but WITHOUT
18
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 FOR A PARTICULAR PURPOSE.  See the license for more details.
20
*/
21
22
/*! \file crosscurrencyratehelpers.hpp
23
    \brief FX and cross currency basis swaps rate helpers
24
*/
25
26
#ifndef quantlib_crosscurrencyratehelpers_hpp
27
#define quantlib_crosscurrencyratehelpers_hpp
28
29
#include <ql/cashflows/stubiborcoupon.hpp>
30
#include <ql/termstructures/yield/ratehelpers.hpp>
31
#include <ql/instruments/constnotionalcrosscurrencybasisswap.hpp>
32
#include <ql/instruments/constnotionalcrosscurrencyfixedvsfloatingswap.hpp>
33
#include <ql/experimental/fx/mtmcrosscurrencybasisswap.hpp>
34
#include <ql/optional.hpp>
35
36
namespace QuantLib {
37
38
    class CrossCurrencySwapRateHelperBase : public RelativeDateRateHelper {
39
      public:
40
        void setTermStructure(YieldTermStructure* t) override;
41
42
      protected:
43
        CrossCurrencySwapRateHelperBase(const Handle<Quote>& quote,
44
                                        const Period& tenor,
45
                                        Natural fixingDays,
46
                                        Calendar calendar,
47
                                        BusinessDayConvention convention,
48
                                        bool endOfMonth,
49
                                        Handle<YieldTermStructure> collateralCurve,
50
                                        Integer paymentLag,
51
                                        bool paymentLagOnNotionalExchanges = false);
52
53
        void initializeDatesFromLegs(const Leg& firstLeg, const Leg& secondLeg);
54
55
        Period tenor_;
56
        Natural fixingDays_;
57
        Calendar calendar_;
58
        BusinessDayConvention convention_;
59
        bool endOfMonth_;
60
        Integer paymentLag_;
61
        bool paymentLagOnNotionalExchanges_;
62
63
        Handle<YieldTermStructure> collateralHandle_;
64
65
        RelinkableHandle<YieldTermStructure> termStructureHandle_;
66
67
        Date initialNotionalExchangeDate_;
68
        Date finalNotionalExchangeDate_;
69
    };
70
71
72
    //! Base class for cross-currency basis swap rate helpers
73
    class CrossCurrencyBasisSwapRateHelperBase : public CrossCurrencySwapRateHelperBase {
74
      protected:
75
        CrossCurrencyBasisSwapRateHelperBase(const Handle<Quote>& basis,
76
                                             const Period& tenor,
77
                                             Natural fixingDays,
78
                                             Calendar calendar,
79
                                             BusinessDayConvention convention,
80
                                             bool endOfMonth,
81
                                             ext::shared_ptr<IborIndex> baseCurrencyIndex,
82
                                             ext::shared_ptr<IborIndex> quoteCurrencyIndex,
83
                                             Handle<YieldTermStructure> collateralCurve,
84
                                             bool isFxBaseCurrencyCollateralCurrency,
85
                                             bool isBasisOnFxBaseCurrencyLeg,
86
                                             std::optional<Frequency> paymentFrequency = std::nullopt,
87
                                             Integer paymentLag = 0,
88
                                             std::optional<Frequency> quoteCurrencyPaymentFrequency = std::nullopt,
89
                                             std::optional<bool> useIndexedCoupons = std::nullopt,
90
                                             bool paymentLagOnNotionalExchanges = false,
91
                                             StubIndexSelection baseStubIndexSelection = {},
92
                                             StubIndexSelection quoteStubIndexSelection = {});
93
94
        void initializeDates() override;
95
        const Handle<YieldTermStructure>& baseCcyLegDiscountHandle() const;
96
        const Handle<YieldTermStructure>& quoteCcyLegDiscountHandle() const;
97
98
        ext::shared_ptr<IborIndex> baseCcyIdx_;
99
        ext::shared_ptr<IborIndex> quoteCcyIdx_;
100
        bool isFxBaseCurrencyCollateralCurrency_;
101
        bool isBasisOnFxBaseCurrencyLeg_;
102
        std::optional<Frequency> paymentFrequency_;
103
        std::optional<Frequency> quoteCcyPaymentFrequency_;
104
        std::optional<bool> useIndexedCoupons_;
105
        StubIndexSelection baseStubIndexSelection_;
106
        StubIndexSelection quoteStubIndexSelection_;
107
108
        Schedule baseCcySchedule_;
109
        Schedule quoteCcySchedule_;
110
        Leg baseCcyIborLeg_;
111
        Leg quoteCcyIborLeg_;
112
    };
113
114
115
    //! Rate helper for bootstrapping over constant-notional cross-currency basis swaps
116
    /*!
117
    Unlike marked-to-market cross currency swaps, both notionals
118
    expressed in base and quote currency remain constant throughout
119
    the lifetime of the swap.
120
121
    Note on used conventions. Consider a currency pair EUR-USD.
122
    EUR is the base currency, while USD is the quote currency.
123
    The quote currency indicates the amount to be paid in that
124
    currency for one unit of base currency.
125
    Hence, for a cross currency swap we define a base currency
126
    leg and a quote currency leg. The parameters of the instrument,
127
    e.g. collateral currency, basis, resetting  or constant notional
128
    legs are defined relative to what base and quote currencies are.
129
    For example, in case of EUR-USD basis swaps the collateral is paid
130
    in quote currency (USD), the basis is given on the base currency
131
    leg (EUR), etc.
132
133
    For more details see:
134
    N. Moreni, A. Pallavicini (2015)
135
    FX Modelling in Collateralized Markets: foreign measures, basis curves
136
    and pricing formulae.
137
    */
138
    class ConstNotionalCrossCurrencyBasisSwapRateHelper : public CrossCurrencyBasisSwapRateHelperBase {
139
      public:
140
        /*! \param paymentFrequency
141
                payment frequency of the base-currency leg; if left unset (the
142
                default) the schedule is derived from the base-currency index tenor.
143
            \param paymentLag
144
                coupon payment lag, in days, applied to both legs (default: 0).
145
                Notional exchanges remain on the effective and maturity dates
146
                unless \c paymentLagOnNotionalExchanges is set.
147
            \param quoteCurrencyPaymentFrequency
148
                payment frequency of the quote-currency leg; if left unset (the
149
                default) it defaults to \c paymentFrequency, and if that is unset as
150
                well the schedule is derived from the quote-currency index tenor.
151
            \param useIndexedCoupons
152
                if provided, overrides the global IborCoupon setting for both legs.
153
            \param paymentLagOnNotionalExchanges
154
                if true, both notional exchanges are lagged by \c paymentLag like
155
                the coupons: the final exchange settles together with the final
156
                coupon and the initial exchange falls on the lagged settlement
157
                date (default: false).
158
            \param baseStubIndexSelection
159
                index selection applied to irregular coupons of the base-currency
160
                leg when it is an Ibor leg (see StubIndexSelection).  The candidate
161
                indices must use exogenous forwarding curves; the default prices
162
                broken periods off the leg's own index.
163
            \param quoteStubIndexSelection
164
                as baseStubIndexSelection, for the quote-currency leg.
165
            In both frequency parameters, \c NoFrequency is accepted as a synonym for
166
            an unset (null) value.
167
        */
168
        ConstNotionalCrossCurrencyBasisSwapRateHelper(
169
            const Handle<Quote>& basis,
170
            const Period& tenor,
171
            Natural fixingDays,
172
            const Calendar& calendar,
173
            BusinessDayConvention convention,
174
            bool endOfMonth,
175
            const ext::shared_ptr<IborIndex>& baseCurrencyIndex,
176
            const ext::shared_ptr<IborIndex>& quoteCurrencyIndex,
177
            const Handle<YieldTermStructure>& collateralCurve,
178
            bool isFxBaseCurrencyCollateralCurrency,
179
            bool isBasisOnFxBaseCurrencyLeg,
180
            std::optional<Frequency> paymentFrequency = std::nullopt,
181
            Integer paymentLag = 0,
182
            std::optional<Frequency> quoteCurrencyPaymentFrequency = std::nullopt,
183
            std::optional<bool> useIndexedCoupons = std::nullopt,
184
            bool paymentLagOnNotionalExchanges = false,
185
            StubIndexSelection baseStubIndexSelection = {},
186
            StubIndexSelection quoteStubIndexSelection = {});
187
        //! \name RateHelper interface
188
        //@{
189
        Real impliedQuote() const override;
190
191
0
        const ext::shared_ptr<ConstNotionalCrossCurrencyBasisSwap>& swap() const { return swap_; }
192
        //@}
193
        //! \name Visitability
194
        //@{
195
        void accept(AcyclicVisitor&) override;
196
        //@}
197
      protected:
198
        void initializeDates() override;
199
200
      private:
201
        void buildSwap();
202
203
        ext::shared_ptr<ConstNotionalCrossCurrencyBasisSwap> swap_;
204
    };
205
206
207
    //! Rate helper for bootstrapping over market-to-market cross-currency basis swaps
208
    /*!
209
    Helper for a cross currency swap with resetting notional.
210
    This means that at each accrual-period boundary the notional on the MtM
211
    leg is reset to reflect changes in the FX rate - reducing
212
    the counterparty and FX risk of the structure.
213
214
    For more details see:
215
    N. Moreni, A. Pallavicini (2015)
216
    FX Modelling in Collateralized Markets: foreign measures, basis curves
217
    and pricing formulae.
218
    */
219
    class MtMCrossCurrencyBasisSwapRateHelper : public CrossCurrencyBasisSwapRateHelperBase {
220
      public:
221
        /*! \param paymentFrequency
222
                payment frequency of the base-currency leg; if left unset (the
223
                default) the schedule is derived from the base-currency index tenor.
224
            \param paymentLag
225
                coupon payment lag, in days, applied to both legs (default: 0).
226
                Notional exchanges remain on the effective and maturity dates.
227
            \param quoteCurrencyPaymentFrequency
228
                payment frequency of the quote-currency leg; if left unset (the
229
                default) it defaults to \c paymentFrequency, and if that is unset as
230
                well the schedule is derived from the quote-currency index tenor.
231
            \param fxResetFixingDays
232
                number of business days by which each FX fixing precedes its
233
                accrual-start value date (default: 0).
234
            \param fxResetFixingCalendar
235
                calendar used for the FX fixing offset; for a non-zero fixing lag,
236
                \p calendar is used if this is empty.
237
            \param useIndexedCoupons
238
                if provided, overrides the global IborCoupon setting for both legs.
239
            \param baseStubIndexSelection
240
                index selection applied to irregular coupons of the base-currency
241
                leg when it is an Ibor leg (see StubIndexSelection).  The candidate
242
                indices must use exogenous forwarding curves; the default prices
243
                broken periods off the leg's own index.
244
            \param quoteStubIndexSelection
245
                as baseStubIndexSelection, for the quote-currency leg.
246
            In both frequency parameters, \c NoFrequency is accepted as a synonym for
247
            an unset (null) value.
248
        */
249
        MtMCrossCurrencyBasisSwapRateHelper(const Handle<Quote>& basis,
250
                                            const Period& tenor,
251
                                            Natural fixingDays,
252
                                            const Calendar& calendar,
253
                                            BusinessDayConvention convention,
254
                                            bool endOfMonth,
255
                                            const ext::shared_ptr<IborIndex>& baseCurrencyIndex,
256
                                            const ext::shared_ptr<IborIndex>& quoteCurrencyIndex,
257
                                            const Handle<YieldTermStructure>& collateralCurve,
258
                                            bool isFxBaseCurrencyCollateralCurrency,
259
                                            bool isBasisOnFxBaseCurrencyLeg,
260
                                            bool isFxBaseCurrencyLegResettable,
261
                                            std::optional<Frequency> paymentFrequency = std::nullopt,
262
                                            Integer paymentLag = 0,
263
                                            std::optional<Frequency> quoteCurrencyPaymentFrequency = std::nullopt,
264
                                            Natural fxResetFixingDays = 0,
265
                                            Calendar fxResetFixingCalendar = Calendar(),
266
                                            std::optional<bool> useIndexedCoupons = std::nullopt,
267
                                            StubIndexSelection baseStubIndexSelection = {},
268
                                            StubIndexSelection quoteStubIndexSelection = {});
269
        //! \name RateHelper interface
270
        //@{
271
        Real impliedQuote() const override;
272
        //@}
273
        //! \name Inspectors
274
        //@{
275
        //! the underlying par swap: unit notionals, zero spreads, spot FX = 1
276
0
        const ext::shared_ptr<MtMCrossCurrencyBasisSwap>& swap() const { return swap_; }
277
        //! the number of business days from an FX value date to its fixing date
278
0
        Natural fxResetFixingDays() const { return fxResetFixingDays_; }
279
        //! the calendar used to determine FX fixing dates
280
0
        const Calendar& fxResetFixingCalendar() const { return fxResetFixingCalendar_; }
281
        //@}
282
        //! \name Visitability
283
        //@{
284
        void accept(AcyclicVisitor&) override;
285
        //@}
286
      protected:
287
        void initializeDates() override;
288
289
      private:
290
        void buildSwap();
291
292
        bool isFxBaseCurrencyLegResettable_;
293
        Natural fxResetFixingDays_;
294
        Calendar fxResetFixingCalendar_;
295
        ext::shared_ptr<MtMCrossCurrencyBasisSwap> swap_;
296
    };
297
298
299
    //! Rate helper for bootstrapping fixed–vs-floating cross-currency par swaps
300
    /*!
301
    This helper represents a par cross-currency swap exchanging a fixed-rate leg
302
    against a floating-rate leg in a different currency. Since the swap is quoted
303
    at par, the FX spot cancels out and is not required.
304
305
    The collateralOnFixedLeg flag determines which leg is discounted using the provided
306
    collateral curve, while the other leg’s discount curve is the one being bootstrapped.
307
308
    The paymentLag parameter, in days, applies to the coupons of both legs;
309
    notional exchanges remain on the effective and maturity dates.
310
311
    The convention parameter applies to the schedule and payments of both legs,
312
    and the calendar parameter is used to roll and to pay on both legs.
313
314
    If provided, the useIndexedCoupons parameter overrides the global
315
    IborCoupon setting for the floating leg.
316
317
    The floatPaymentFrequency parameter is the payment frequency of the floating
318
    leg.  If left unset (the default) the schedule is derived from the floating
319
    index tenor, which is only meaningful for an ibor index; an overnight index
320
    has no payment frequency of its own, so one must be given explicitly.
321
    \c NoFrequency is accepted as a synonym for an unset (null) value.
322
323
    The floatStubIndexSelection parameter selects the indices used for irregular
324
    coupons of the floating leg when it is an Ibor leg (see StubIndexSelection).
325
    */
326
    class ConstNotionalCrossCurrencySwapRateHelper : public CrossCurrencySwapRateHelperBase {
327
      public:
328
        ConstNotionalCrossCurrencySwapRateHelper(
329
            const Handle<Quote>& fixedRate,
330
            const Period& tenor,
331
            Natural fixingDays,
332
            const Calendar& calendar,
333
            BusinessDayConvention convention,
334
            bool endOfMonth,
335
            Frequency fixedFrequency,
336
            DayCounter  fixedDayCount,
337
            const ext::shared_ptr<IborIndex>& floatIndex,
338
            const Handle<YieldTermStructure>& collateralCurve,
339
            bool collateralOnFixedLeg,
340
            Integer paymentLag = 0,
341
            std::optional<bool> useIndexedCoupons = std::nullopt,
342
            std::optional<Frequency> floatPaymentFrequency = std::nullopt,
343
            StubIndexSelection floatStubIndexSelection = {});
344
345
        Real impliedQuote() const override;
346
        void accept(AcyclicVisitor&) override;
347
348
0
        const ext::shared_ptr<ConstNotionalCrossCurrencyFixedVsFloatingSwap>& swap() const {
349
0
            return xccySwap_;
350
0
        }
351
352
      protected:
353
        void initializeDates() override;
354
        const Handle<YieldTermStructure>& fixedLegDiscountHandle() const;
355
        const Handle<YieldTermStructure>& floatingLegDiscountHandle() const;
356
357
        Frequency fixedFrequency_;
358
        DayCounter fixedDayCount_;
359
        ext::shared_ptr<IborIndex> floatIndex_;
360
        bool collateralOnFixedLeg_;
361
        std::optional<bool> useIndexedCoupons_;
362
        std::optional<Frequency> floatPaymentFrequency_;
363
        StubIndexSelection floatStubIndexSelection_;
364
365
        ext::shared_ptr<ConstNotionalCrossCurrencyFixedVsFloatingSwap> xccySwap_;
366
    };
367
368
}
369
370
#endif