| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | /* |
| 3 | Copyright (C) 2021 Marcin Rybacki |
| 4 | |
| 5 | This file is part of QuantLib, a free-software/open-source library |
| 6 | for financial quantitative analysts and developers - http://quantlib.org/ |
| 7 | |
| 8 | QuantLib is free software: you can redistribute it and/or modify it |
| 9 | under the terms of the QuantLib license. You should have received a |
| 10 | copy of the license along with this program; if not, please email |
| 11 | <quantlib-dev@lists.sf.net>. The license is also available online at |
| 12 | <http://quantlib.org/license.shtml>. |
| 13 | |
| 14 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 15 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 16 | FOR A PARTICULAR PURPOSE. See the license for more details. |
| 17 | */ |
| 18 | |
| 19 | #include "crosscurrencyratehelpers.hpp" |
| 20 | #include "utilities.hpp" |
| 21 | #include <ql/experimental/termstructures/crosscurrencyratehelpers.hpp> |
| 22 | #include <ql/indexes/ibor/euribor.hpp> |
| 23 | #include <ql/indexes/ibor/usdlibor.hpp> |
| 24 | #include <ql/cashflows/iborcoupon.hpp> |
| 25 | #include <ql/cashflows/simplecashflow.hpp> |
| 26 | #include <ql/math/interpolations/loginterpolation.hpp> |
| 27 | #include <ql/pricingengines/swap/discountingswapengine.hpp> |
| 28 | #include <ql/termstructures/yield/flatforward.hpp> |
| 29 | #include <ql/termstructures/yield/piecewiseyieldcurve.hpp> |
| 30 | #include <ql/time/calendars/target.hpp> |
| 31 | #include <ql/time/calendars/unitedstates.hpp> |
| 32 | |
| 33 | using namespace QuantLib; |
| 34 | using namespace boost::unit_test_framework; |
| 35 | |
| 36 | namespace crosscurrencyratehelpers_test { |
| 37 | |
| 38 | struct XccyTestDatum { |
| 39 | Integer n; |
| 40 | TimeUnit units; |
| 41 | Spread basis; |
| 42 | |
| 43 | XccyTestDatum(Integer n, TimeUnit units, Spread basis) : n(n), units(units), basis(basis) {} |
| 44 | }; |
| 45 | |
| 46 | struct CommonVars { |
| 47 | Real basisPoint; |
| 48 | Real fxSpot; |
| 49 | |
| 50 | Date today, settlement; |
| 51 | Calendar calendar; |
| 52 | Natural settlementDays; |
| 53 | Currency ccy; |
| 54 | BusinessDayConvention businessConvention; |
| 55 | DayCounter dayCount; |
| 56 | bool endOfMonth; |
| 57 | |
| 58 | ext::shared_ptr<IborIndex> baseCcyIdx; |
| 59 | ext::shared_ptr<IborIndex> quoteCcyIdx; |
| 60 | |
| 61 | RelinkableHandle<YieldTermStructure> baseCcyIdxHandle; |
| 62 | RelinkableHandle<YieldTermStructure> quoteCcyIdxHandle; |
| 63 | |
| 64 | std::vector<XccyTestDatum> basisData; |
| 65 | |
| 66 | // utilities |
| 67 | |
| 68 | ext::shared_ptr<RateHelper> |
| 69 | constantNotionalXccyRateHelper(const XccyTestDatum& q, |
| 70 | const Handle<YieldTermStructure>& collateralHandle, |
| 71 | bool isFxBaseCurrencyCollateralCurrency, |
| 72 | bool isBasisOnFxBaseCurrencyLeg) const { |
| 73 | Handle<Quote> quoteHandle(ext::make_shared<SimpleQuote>(args: q.basis * basisPoint)); |
| 74 | Period tenor(q.n, q.units); |
| 75 | return ext::shared_ptr<RateHelper>(new ConstNotionalCrossCurrencyBasisSwapRateHelper( |
| 76 | quoteHandle, tenor, settlementDays, calendar, businessConvention, endOfMonth, |
| 77 | baseCcyIdx, quoteCcyIdx, collateralHandle, isFxBaseCurrencyCollateralCurrency, |
| 78 | isBasisOnFxBaseCurrencyLeg)); |
| 79 | } |
| 80 | |
| 81 | std::vector<ext::shared_ptr<RateHelper> > |
| 82 | buildConstantNotionalXccyRateHelpers(const std::vector<XccyTestDatum>& xccyData, |
| 83 | const Handle<YieldTermStructure>& collateralHandle, |
| 84 | bool isFxBaseCurrencyCollateralCurrency, |
| 85 | bool isBasisOnFxBaseCurrencyLeg) const { |
| 86 | std::vector<ext::shared_ptr<RateHelper> > instruments; |
| 87 | instruments.reserve(n: xccyData.size()); |
| 88 | for (const auto& i : xccyData) { |
| 89 | instruments.push_back(x: constantNotionalXccyRateHelper( |
| 90 | q: i, collateralHandle, isFxBaseCurrencyCollateralCurrency, |
| 91 | isBasisOnFxBaseCurrencyLeg)); |
| 92 | } |
| 93 | |
| 94 | return instruments; |
| 95 | } |
| 96 | |
| 97 | ext::shared_ptr<RateHelper> |
| 98 | resettingXccyRateHelper(const XccyTestDatum& q, |
| 99 | const Handle<YieldTermStructure>& collateralHandle, |
| 100 | bool isFxBaseCurrencyCollateralCurrency, |
| 101 | bool isBasisOnFxBaseCurrencyLeg, |
| 102 | bool isFxBaseCurrencyLegResettable) const { |
| 103 | Handle<Quote> quoteHandle(ext::make_shared<SimpleQuote>(args: q.basis * basisPoint)); |
| 104 | Period tenor(q.n, q.units); |
| 105 | return ext::shared_ptr<RateHelper>(new MtMCrossCurrencyBasisSwapRateHelper( |
| 106 | quoteHandle, tenor, settlementDays, calendar, businessConvention, endOfMonth, |
| 107 | baseCcyIdx, quoteCcyIdx, collateralHandle, isFxBaseCurrencyCollateralCurrency, |
| 108 | isBasisOnFxBaseCurrencyLeg, isFxBaseCurrencyLegResettable)); |
| 109 | } |
| 110 | |
| 111 | std::vector<ext::shared_ptr<RateHelper> > |
| 112 | buildResettingXccyRateHelpers(const std::vector<XccyTestDatum>& xccyData, |
| 113 | const Handle<YieldTermStructure>& collateralHandle, |
| 114 | bool isFxBaseCurrencyCollateralCurrency, |
| 115 | bool isBasisOnFxBaseCurrencyLeg, |
| 116 | bool isFxBaseCurrencyLegResettable) const { |
| 117 | std::vector<ext::shared_ptr<RateHelper> > instruments; |
| 118 | instruments.reserve(n: xccyData.size()); |
| 119 | for (const auto& i : xccyData) { |
| 120 | instruments.push_back(x: resettingXccyRateHelper( |
| 121 | q: i, collateralHandle, isFxBaseCurrencyCollateralCurrency, |
| 122 | isBasisOnFxBaseCurrencyLeg, isFxBaseCurrencyLegResettable)); |
| 123 | } |
| 124 | |
| 125 | return instruments; |
| 126 | } |
| 127 | |
| 128 | Schedule legSchedule(const Period& tenor, |
| 129 | const ext::shared_ptr<IborIndex>& idx) const { |
| 130 | return MakeSchedule() |
| 131 | .from(effectiveDate: settlement) |
| 132 | .to(terminationDate: settlement + tenor) |
| 133 | .withTenor(idx->tenor()) |
| 134 | .withCalendar(calendar) |
| 135 | .withConvention(businessConvention) |
| 136 | .endOfMonth(flag: endOfMonth) |
| 137 | .backwards(); |
| 138 | } |
| 139 | |
| 140 | Leg constantNotionalLeg(const Schedule& schedule, |
| 141 | const ext::shared_ptr<IborIndex>& idx, |
| 142 | Real notional, |
| 143 | Spread basis) const { |
| 144 | Leg leg = IborLeg(schedule, idx).withNotionals(notional).withSpreads(spread: basis); |
| 145 | Date lastPaymentDate = leg.back()->date(); |
| 146 | leg.push_back(x: ext::make_shared<SimpleCashFlow>(args&: notional, args&: lastPaymentDate)); |
| 147 | return leg; |
| 148 | } |
| 149 | |
| 150 | std::vector<ext::shared_ptr<Swap> > |
| 151 | buildXccyBasisSwap(const XccyTestDatum& q, |
| 152 | Real fxSpot, |
| 153 | bool isFxBaseCurrencyCollateralCurrency, |
| 154 | bool isBasisOnFxBaseCurrencyLeg) const { |
| 155 | const Real baseCcyLegNotional = 1.0; |
| 156 | Real quoteCcyLegNotional = baseCcyLegNotional * fxSpot; |
| 157 | |
| 158 | Spread baseCcyLegBasis = isBasisOnFxBaseCurrencyLeg ? Real(q.basis * basisPoint) : 0.0; |
| 159 | Spread quoteCcyLegBasis = isBasisOnFxBaseCurrencyLeg ? 0.0 : Real(q.basis * basisPoint); |
| 160 | |
| 161 | std::vector<ext::shared_ptr<Swap> > legs; |
| 162 | bool payer = true; |
| 163 | |
| 164 | Leg baseCcyLeg = constantNotionalLeg(schedule: legSchedule(tenor: Period(q.n, q.units), idx: baseCcyIdx), |
| 165 | idx: baseCcyIdx, notional: baseCcyLegNotional, basis: baseCcyLegBasis); |
| 166 | legs.push_back(x: ext::make_shared<Swap>(args: std::vector<Leg>(1, baseCcyLeg), |
| 167 | args: std::vector<bool>(1, !payer))); |
| 168 | |
| 169 | Leg quoteCcyLeg = |
| 170 | constantNotionalLeg(schedule: legSchedule(tenor: Period(q.n, q.units), idx: quoteCcyIdx), idx: quoteCcyIdx, |
| 171 | notional: quoteCcyLegNotional, basis: quoteCcyLegBasis); |
| 172 | legs.push_back(x: ext::make_shared<Swap>(args: std::vector<Leg>(1, quoteCcyLeg), |
| 173 | args: std::vector<bool>(1, payer))); |
| 174 | return legs; |
| 175 | } |
| 176 | |
| 177 | CommonVars() { |
| 178 | settlementDays = 2; |
| 179 | businessConvention = Following; |
| 180 | calendar = TARGET(); |
| 181 | dayCount = Actual365Fixed(); |
| 182 | endOfMonth = false; |
| 183 | |
| 184 | basisPoint = 1.0e-4; |
| 185 | fxSpot = 1.25; |
| 186 | |
| 187 | baseCcyIdx = ext::shared_ptr<IborIndex>(new Euribor3M(baseCcyIdxHandle)); |
| 188 | quoteCcyIdx = ext::shared_ptr<IborIndex>(new USDLibor(3 * Months, quoteCcyIdxHandle)); |
| 189 | |
| 190 | /* Data source: |
| 191 | N. Moreni, A. Pallavicini (2015) |
| 192 | FX Modelling in Collateralized Markets: foreign measures, basis curves |
| 193 | and pricing formulae. |
| 194 | |
| 195 | section 4.2.1, Table 2. |
| 196 | */ |
| 197 | basisData.emplace_back(args: 1, args: Years, args: -14.5); |
| 198 | basisData.emplace_back(args: 18, args: Months, args: -18.5); |
| 199 | basisData.emplace_back(args: 2, args: Years, args: -20.5); |
| 200 | basisData.emplace_back(args: 3, args: Years, args: -23.75); |
| 201 | basisData.emplace_back(args: 4, args: Years, args: -25.5); |
| 202 | basisData.emplace_back(args: 5, args: Years, args: -26.5); |
| 203 | basisData.emplace_back(args: 7, args: Years, args: -26.75); |
| 204 | basisData.emplace_back(args: 10, args: Years, args: -26.25); |
| 205 | basisData.emplace_back(args: 15, args: Years, args: -24.75); |
| 206 | basisData.emplace_back(args: 20, args: Years, args: -23.25); |
| 207 | basisData.emplace_back(args: 30, args: Years, args: -20.50); |
| 208 | |
| 209 | today = calendar.adjust(Date(6, September, 2013)); |
| 210 | Settings::instance().evaluationDate() = today; |
| 211 | settlement = calendar.advance(today, n: settlementDays, unit: Days); |
| 212 | |
| 213 | baseCcyIdxHandle.linkTo(h: flatRate(today: settlement, forward: 0.007, dc: dayCount)); |
| 214 | quoteCcyIdxHandle.linkTo(h: flatRate(today: settlement, forward: 0.015, dc: dayCount)); |
| 215 | } |
| 216 | }; |
| 217 | } |
| 218 | |
| 219 | void testConstantNotionalCrossCurrencySwapsNPV(bool isFxBaseCurrencyCollateralCurrency, |
| 220 | bool isBasisOnFxBaseCurrencyLeg) { |
| 221 | |
| 222 | using namespace crosscurrencyratehelpers_test; |
| 223 | |
| 224 | CommonVars vars; |
| 225 | |
| 226 | Handle<YieldTermStructure> collateralHandle = |
| 227 | isFxBaseCurrencyCollateralCurrency ? vars.baseCcyIdxHandle : vars.quoteCcyIdxHandle; |
| 228 | |
| 229 | ext::shared_ptr<DiscountingSwapEngine> collateralCcyLegEngine( |
| 230 | new DiscountingSwapEngine(collateralHandle)); |
| 231 | |
| 232 | std::vector<ext::shared_ptr<RateHelper> > instruments = |
| 233 | vars.buildConstantNotionalXccyRateHelpers(xccyData: vars.basisData, collateralHandle, |
| 234 | isFxBaseCurrencyCollateralCurrency, |
| 235 | isBasisOnFxBaseCurrencyLeg); |
| 236 | ext::shared_ptr<YieldTermStructure> foreignCcyCurve( |
| 237 | new PiecewiseYieldCurve<Discount, LogLinear>(vars.settlement, instruments, vars.dayCount)); |
| 238 | foreignCcyCurve->enableExtrapolation(); |
| 239 | Handle<YieldTermStructure> foreignCcyHandle(foreignCcyCurve); |
| 240 | ext::shared_ptr<DiscountingSwapEngine> foreignCcyLegEngine( |
| 241 | new DiscountingSwapEngine(foreignCcyHandle)); |
| 242 | |
| 243 | Real tolerance = 1.0e-12; |
| 244 | |
| 245 | for (Size i = 0; i < vars.basisData.size(); ++i) { |
| 246 | |
| 247 | XccyTestDatum quote = vars.basisData[i]; |
| 248 | std::vector<ext::shared_ptr<Swap> > xccySwapProxy = vars.buildXccyBasisSwap( |
| 249 | q: quote, fxSpot: vars.fxSpot, isFxBaseCurrencyCollateralCurrency, isBasisOnFxBaseCurrencyLeg); |
| 250 | |
| 251 | if (isFxBaseCurrencyCollateralCurrency) { |
| 252 | xccySwapProxy[0]->setPricingEngine(collateralCcyLegEngine); |
| 253 | xccySwapProxy[1]->setPricingEngine(foreignCcyLegEngine); |
| 254 | } else { |
| 255 | xccySwapProxy[0]->setPricingEngine(foreignCcyLegEngine); |
| 256 | xccySwapProxy[1]->setPricingEngine(collateralCcyLegEngine); |
| 257 | } |
| 258 | |
| 259 | Period p = quote.n * quote.units; |
| 260 | |
| 261 | Real baseCcyLegNpv = vars.fxSpot * xccySwapProxy[0]->NPV(); |
| 262 | Real quoteCcyLegNpv = xccySwapProxy[1]->NPV(); |
| 263 | Real npv = baseCcyLegNpv + quoteCcyLegNpv; |
| 264 | |
| 265 | if (std::fabs(x: npv) > tolerance) |
| 266 | BOOST_ERROR("unable to price the cross currency basis swap to par\n" |
| 267 | << std::setprecision(5) << " calculated NPV: " << npv << "\n" |
| 268 | << " expected: " << 0.0 << "\n" |
| 269 | << " implied basis: " << quote.basis << "\n" |
| 270 | << " tenor: " << p << "\n" ); |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | void testResettingCrossCurrencySwaps(bool isFxBaseCurrencyCollateralCurrency, |
| 275 | bool isBasisOnFxBaseCurrencyLeg, |
| 276 | bool isFxBaseCurrencyLegResettable) { |
| 277 | |
| 278 | using namespace crosscurrencyratehelpers_test; |
| 279 | |
| 280 | CommonVars vars; |
| 281 | |
| 282 | Handle<YieldTermStructure> collateralHandle = |
| 283 | isFxBaseCurrencyCollateralCurrency ? vars.baseCcyIdxHandle : vars.quoteCcyIdxHandle; |
| 284 | |
| 285 | std::vector<ext::shared_ptr<RateHelper> > resettingInstruments = |
| 286 | vars.buildResettingXccyRateHelpers( |
| 287 | xccyData: vars.basisData, collateralHandle, isFxBaseCurrencyCollateralCurrency, |
| 288 | isBasisOnFxBaseCurrencyLeg, isFxBaseCurrencyLegResettable); |
| 289 | |
| 290 | std::vector<ext::shared_ptr<RateHelper> > constNotionalInstruments = |
| 291 | vars.buildConstantNotionalXccyRateHelpers(xccyData: vars.basisData, collateralHandle, |
| 292 | isFxBaseCurrencyCollateralCurrency, |
| 293 | isBasisOnFxBaseCurrencyLeg); |
| 294 | |
| 295 | ext::shared_ptr<YieldTermStructure> resettingCurve( |
| 296 | new PiecewiseYieldCurve<Discount, LogLinear>(vars.settlement, resettingInstruments, vars.dayCount)); |
| 297 | resettingCurve->enableExtrapolation(); |
| 298 | |
| 299 | ext::shared_ptr<YieldTermStructure> constNotionalCurve( |
| 300 | new PiecewiseYieldCurve<Discount, LogLinear>(vars.settlement, constNotionalInstruments, |
| 301 | vars.dayCount)); |
| 302 | constNotionalCurve->enableExtrapolation(); |
| 303 | |
| 304 | Real tolerance = 1.0e-4 * 5; |
| 305 | Size numberOfInstruments = vars.basisData.size(); |
| 306 | |
| 307 | for (Size i = 0; i < numberOfInstruments; ++i) { |
| 308 | |
| 309 | Date maturity = resettingInstruments[i]->maturityDate(); |
| 310 | Rate resettingZero = resettingCurve->zeroRate(d: maturity, resultDayCounter: vars.dayCount, comp: Continuous); |
| 311 | Rate constNotionalZero = constNotionalCurve->zeroRate(d: maturity, resultDayCounter: vars.dayCount, comp: Continuous); |
| 312 | |
| 313 | // The difference between resetting and constant notional curves |
| 314 | // is not expected to be substantial. With the current setup it should |
| 315 | // amount to only a few basis points - hence the tolerance level was |
| 316 | // set at 5 bps. |
| 317 | if (std::fabs(x: resettingZero - constNotionalZero) > tolerance) |
| 318 | BOOST_ERROR("too large difference between resetting and constant notional curve \n" |
| 319 | << std::setprecision(5) |
| 320 | << " zero from resetting curve: " << resettingZero << "\n" |
| 321 | << " zero from const notional curve: " << constNotionalZero << "\n" |
| 322 | << " maturity: " << maturity << "\n" ); |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | void CrossCurrencyRateHelpersTest:: |
| 327 | testConstNotionalBasisSwapsWithCollateralInQuoteAndBasisInBaseCcy() { |
| 328 | BOOST_TEST_MESSAGE("Testing constant notional basis swaps with collateral in quote ccy and " |
| 329 | "basis in base ccy..." ); |
| 330 | |
| 331 | bool isFxBaseCurrencyCollateralCurrency = false; |
| 332 | bool isBasisOnFxBaseCurrencyLeg = true; |
| 333 | |
| 334 | testConstantNotionalCrossCurrencySwapsNPV(isFxBaseCurrencyCollateralCurrency, |
| 335 | isBasisOnFxBaseCurrencyLeg); |
| 336 | } |
| 337 | |
| 338 | void CrossCurrencyRateHelpersTest::testConstNotionalBasisSwapsWithCollateralInBaseAndBasisInQuoteCcy() { |
| 339 | BOOST_TEST_MESSAGE( |
| 340 | "Testing constant notional basis swaps with collateral in base ccy and basis in quote ccy..." ); |
| 341 | |
| 342 | bool isFxBaseCurrencyCollateralCurrency = true; |
| 343 | bool isBasisOnFxBaseCurrencyLeg = false; |
| 344 | |
| 345 | testConstantNotionalCrossCurrencySwapsNPV(isFxBaseCurrencyCollateralCurrency, |
| 346 | isBasisOnFxBaseCurrencyLeg); |
| 347 | } |
| 348 | |
| 349 | void CrossCurrencyRateHelpersTest::testConstNotionalBasisSwapsWithCollateralAndBasisInBaseCcy() { |
| 350 | BOOST_TEST_MESSAGE( |
| 351 | "Testing constant notional basis swaps with collateral and basis in base ccy..." ); |
| 352 | |
| 353 | bool isFxBaseCurrencyCollateralCurrency = true; |
| 354 | bool isBasisOnFxBaseCurrencyLeg = true; |
| 355 | |
| 356 | testConstantNotionalCrossCurrencySwapsNPV(isFxBaseCurrencyCollateralCurrency, |
| 357 | isBasisOnFxBaseCurrencyLeg); |
| 358 | } |
| 359 | |
| 360 | void CrossCurrencyRateHelpersTest::testConstNotionalBasisSwapsWithCollateralAndBasisInQuoteCcy() { |
| 361 | BOOST_TEST_MESSAGE("Testing constant notional basis swaps with collateral and basis in quote ccy..." ); |
| 362 | |
| 363 | bool isFxBaseCurrencyCollateralCurrency = false; |
| 364 | bool isBasisOnFxBaseCurrencyLeg = false; |
| 365 | |
| 366 | testConstantNotionalCrossCurrencySwapsNPV(isFxBaseCurrencyCollateralCurrency, |
| 367 | isBasisOnFxBaseCurrencyLeg); |
| 368 | } |
| 369 | |
| 370 | void CrossCurrencyRateHelpersTest:: |
| 371 | testResettingBasisSwapsWithCollateralInQuoteAndBasisInBaseCcy() { |
| 372 | BOOST_TEST_MESSAGE( |
| 373 | "Testing resetting basis swaps with collateral in quote ccy and basis in base ccy..." ); |
| 374 | |
| 375 | bool isFxBaseCurrencyCollateralCurrency = false; |
| 376 | bool isFxBaseCurrencyLegResettable = false; |
| 377 | bool isBasisOnFxBaseCurrencyLeg = true; |
| 378 | |
| 379 | testResettingCrossCurrencySwaps(isFxBaseCurrencyCollateralCurrency, isBasisOnFxBaseCurrencyLeg, |
| 380 | isFxBaseCurrencyLegResettable); |
| 381 | } |
| 382 | |
| 383 | void CrossCurrencyRateHelpersTest:: |
| 384 | testResettingBasisSwapsWithCollateralInBaseAndBasisInQuoteCcy() { |
| 385 | BOOST_TEST_MESSAGE( |
| 386 | "Testing resetting basis swaps with collateral in base ccy and basis in quote ccy..." ); |
| 387 | |
| 388 | bool isFxBaseCurrencyCollateralCurrency = true; |
| 389 | bool isFxBaseCurrencyLegResettable = true; |
| 390 | bool isBasisOnFxBaseCurrencyLeg = false; |
| 391 | |
| 392 | testResettingCrossCurrencySwaps(isFxBaseCurrencyCollateralCurrency, isBasisOnFxBaseCurrencyLeg, |
| 393 | isFxBaseCurrencyLegResettable); |
| 394 | } |
| 395 | |
| 396 | void CrossCurrencyRateHelpersTest::testResettingBasisSwapsWithCollateralAndBasisInBaseCcy() { |
| 397 | BOOST_TEST_MESSAGE("Testing resetting basis swaps with collateral and basis in base ccy..." ); |
| 398 | |
| 399 | bool isFxBaseCurrencyCollateralCurrency = true; |
| 400 | bool isFxBaseCurrencyLegResettable = true; |
| 401 | bool isBasisOnFxBaseCurrencyLeg = true; |
| 402 | |
| 403 | testResettingCrossCurrencySwaps(isFxBaseCurrencyCollateralCurrency, isBasisOnFxBaseCurrencyLeg, |
| 404 | isFxBaseCurrencyLegResettable); |
| 405 | } |
| 406 | |
| 407 | void CrossCurrencyRateHelpersTest::testResettingBasisSwapsWithCollateralAndBasisInQuoteCcy() { |
| 408 | BOOST_TEST_MESSAGE("Testing resetting basis swaps with collateral and basis in quote ccy..." ); |
| 409 | |
| 410 | bool isFxBaseCurrencyCollateralCurrency = false; |
| 411 | bool isFxBaseCurrencyLegResettable = false; |
| 412 | bool isBasisOnFxBaseCurrencyLeg = false; |
| 413 | |
| 414 | testResettingCrossCurrencySwaps(isFxBaseCurrencyCollateralCurrency, isBasisOnFxBaseCurrencyLeg, |
| 415 | isFxBaseCurrencyLegResettable); |
| 416 | } |
| 417 | |
| 418 | void CrossCurrencyRateHelpersTest::testExceptionWhenInstrumentTenorShorterThanIndexFrequency() { |
| 419 | BOOST_TEST_MESSAGE( |
| 420 | "Testing exception when instrument tenor is shorter than index frequency..." ); |
| 421 | |
| 422 | using namespace crosscurrencyratehelpers_test; |
| 423 | |
| 424 | CommonVars vars; |
| 425 | |
| 426 | std::vector<XccyTestDatum> data{{1, Months, 10.0}}; |
| 427 | Handle<YieldTermStructure> collateralHandle; |
| 428 | |
| 429 | BOOST_CHECK_THROW( |
| 430 | std::vector<ext::shared_ptr<RateHelper> > resettingInstruments = |
| 431 | vars.buildConstantNotionalXccyRateHelpers(data, collateralHandle, true, true), |
| 432 | Error); |
| 433 | } |
| 434 | |
| 435 | test_suite* CrossCurrencyRateHelpersTest::suite() { |
| 436 | auto* suite = BOOST_TEST_SUITE("Cross currency rate helpers tests" ); |
| 437 | |
| 438 | suite->add( |
| 439 | QUANTLIB_TEST_CASE(&CrossCurrencyRateHelpersTest:: |
| 440 | testConstNotionalBasisSwapsWithCollateralInQuoteAndBasisInBaseCcy)); |
| 441 | suite->add( |
| 442 | QUANTLIB_TEST_CASE(&CrossCurrencyRateHelpersTest:: |
| 443 | testConstNotionalBasisSwapsWithCollateralInBaseAndBasisInQuoteCcy)); |
| 444 | suite->add(QUANTLIB_TEST_CASE( |
| 445 | &CrossCurrencyRateHelpersTest::testConstNotionalBasisSwapsWithCollateralAndBasisInBaseCcy)); |
| 446 | suite->add(QUANTLIB_TEST_CASE(&CrossCurrencyRateHelpersTest:: |
| 447 | testConstNotionalBasisSwapsWithCollateralAndBasisInQuoteCcy)); |
| 448 | |
| 449 | suite->add( |
| 450 | QUANTLIB_TEST_CASE(&CrossCurrencyRateHelpersTest:: |
| 451 | testResettingBasisSwapsWithCollateralInQuoteAndBasisInBaseCcy)); |
| 452 | suite->add( |
| 453 | QUANTLIB_TEST_CASE(&CrossCurrencyRateHelpersTest:: |
| 454 | testResettingBasisSwapsWithCollateralInBaseAndBasisInQuoteCcy)); |
| 455 | suite->add(QUANTLIB_TEST_CASE( |
| 456 | &CrossCurrencyRateHelpersTest::testResettingBasisSwapsWithCollateralAndBasisInBaseCcy)); |
| 457 | suite->add(QUANTLIB_TEST_CASE( |
| 458 | &CrossCurrencyRateHelpersTest::testResettingBasisSwapsWithCollateralAndBasisInQuoteCcy)); |
| 459 | |
| 460 | suite->add(QUANTLIB_TEST_CASE( |
| 461 | &CrossCurrencyRateHelpersTest::testExceptionWhenInstrumentTenorShorterThanIndexFrequency)); |
| 462 | return suite; |
| 463 | } |
| 464 | |