/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 |