Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
crossccybasismtmresetswaphelper.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2018 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19#include "toplevelfixture.hpp"
20#include <boost/make_shared.hpp>
21#include <boost/test/unit_test.hpp>
22#include <ql/currencies/all.hpp>
23#include <ql/indexes/ibor/gbplibor.hpp>
24#include <ql/indexes/ibor/usdlibor.hpp>
25#include <ql/quotes/simplequote.hpp>
26#include <ql/termstructures/yield/discountcurve.hpp>
27#include <ql/termstructures/yield/flatforward.hpp>
28#include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
29#include <ql/time/calendars/all.hpp>
30#include <ql/time/daycounters/actual360.hpp>
31#include <ql/types.hpp>
34
35using namespace std;
36using namespace boost::unit_test_framework;
37using namespace QuantLib;
38using namespace QuantExt;
39
40namespace {
41
42struct CommonVars {
43
44 Date asof;
45 Natural settlementDays;
46 Calendar domesticCalendar, foreignCalendar, payCalendar;
47 BusinessDayConvention payConvention;
48 Natural payLag;
49 Period tenor;
50 Currency foreignCurrency, domesticCurrency;
51 DayCounter dayCount;
52 Real foreignNominal;
53 QuantLib::ext::shared_ptr<SimpleQuote> spotFxQuote;
54 QuantLib::ext::shared_ptr<SimpleQuote> spreadQuote;
55 Handle<YieldTermStructure> domesticProjCurve;
56 Handle<YieldTermStructure> domesticDiscCurve;
57 Handle<YieldTermStructure> foreignProjCurve;
58 QuantLib::ext::shared_ptr<IborIndex> domesticIndex;
59 QuantLib::ext::shared_ptr<IborIndex> foreignIndex;
60 QuantLib::ext::shared_ptr<CrossCcyBasisMtMResetSwapHelper> helper;
61
62 CommonVars() {
63 asof = Date(11, Sep, 2018);
64 settlementDays = 2;
65 domesticCalendar = UnitedStates(UnitedStates::Settlement);
66 foreignCalendar = UnitedKingdom();
67 payCalendar = JointCalendar(domesticCalendar, foreignCalendar);
68 payConvention = Following;
69 payLag = 0;
70 tenor = 5 * Years;
71 domesticCurrency = USDCurrency();
72 foreignCurrency = GBPCurrency();
73 dayCount = Actual360();
74 foreignNominal = 10000000.0;
75 spotFxQuote = QuantLib::ext::make_shared<SimpleQuote>(1.2);
76 spreadQuote = QuantLib::ext::make_shared<SimpleQuote>(-0.0015);
77
78 // curves
79 domesticProjCurve =
80 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, domesticCalendar, 0.02, Actual365Fixed()));
81 foreignProjCurve =
82 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, foreignCalendar, 0.03, Actual365Fixed()));
83 domesticDiscCurve =
84 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, domesticCalendar, 0.01, Actual365Fixed()));
85
86 // indices
87 foreignIndex = QuantLib::ext::make_shared<GBPLibor>(3 * Months, foreignProjCurve);
88 domesticIndex = QuantLib::ext::make_shared<USDLibor>(3 * Months, domesticProjCurve);
89 }
90};
91
92QuantLib::ext::shared_ptr<CrossCcyBasisMtMResetSwap> makeTestSwap(const CommonVars& vars,
93 const Handle<YieldTermStructure>& foreignDiscCurve) {
94 // Swap schedule
95 Date referenceDate = Settings::instance().evaluationDate();
96 referenceDate = vars.payCalendar.adjust(referenceDate);
97 Date start = vars.payCalendar.advance(referenceDate, vars.settlementDays * Days);
98 Date end = start + vars.tenor;
99 Schedule schedule(start, end, 3 * Months, vars.payCalendar, vars.payConvention, vars.payConvention,
100 DateGeneration::Backward, false);
101 // Create swap
102 QuantLib::ext::shared_ptr<FxIndex> fxIndex = QuantLib::ext::make_shared<FxIndex>(
103 "dummy", vars.settlementDays, vars.foreignCurrency, vars.domesticCurrency, vars.payCalendar,
104 Handle<Quote>(vars.spotFxQuote), foreignDiscCurve, vars.domesticDiscCurve);
105 QuantLib::ext::shared_ptr<CrossCcyBasisMtMResetSwap> swap(new CrossCcyBasisMtMResetSwap(
106 vars.foreignNominal, vars.foreignCurrency, schedule, vars.foreignIndex, vars.spreadQuote->value(),
107 vars.domesticCurrency, schedule, vars.domesticIndex, 0.0, fxIndex, false));
108 // Attach pricing engine
109 QuantLib::ext::shared_ptr<PricingEngine> engine =
110 QuantLib::ext::make_shared<CrossCcySwapEngine>(vars.domesticCurrency, vars.domesticDiscCurve, vars.foreignCurrency,
111 foreignDiscCurve, Handle<Quote>(vars.spotFxQuote));
112 swap->setPricingEngine(engine);
113 return swap;
114}
115
116// Use helper to build bootstrapped curve
117Handle<YieldTermStructure> bootstrappedCurve(CommonVars& vars) {
118
119 // Create a helper
120 vector<QuantLib::ext::shared_ptr<RateHelper> > helpers(1);
121 vars.helper.reset(new CrossCcyBasisMtMResetSwapHelper(
122 Handle<Quote>(vars.spreadQuote), Handle<Quote>(vars.spotFxQuote), vars.settlementDays, vars.payCalendar,
123 vars.tenor, vars.payConvention, vars.foreignIndex, vars.domesticIndex, Handle<YieldTermStructure>(),
124 vars.domesticDiscCurve));
125 helpers[0] = vars.helper;
126
127 // Build yield curve referencing the helper
128 return Handle<YieldTermStructure>(
129 QuantLib::ext::make_shared<PiecewiseYieldCurve<Discount, LogLinear> >(0, NullCalendar(), helpers, Actual365Fixed()));
130}
131} // namespace
132
133BOOST_FIXTURE_TEST_SUITE(QuantExtTestSuite, qle::test::TopLevelFixture)
134
135BOOST_AUTO_TEST_SUITE(CrossCcyBasisMtMResetSwapHelperTest)
136
137BOOST_AUTO_TEST_CASE(testBootstrap) {
138
139 BOOST_TEST_MESSAGE("Test simple bootstrap against cross currency MtM resetting swap");
140
141 SavedSettings backup;
142 CommonVars vars;
143 Settings::instance().evaluationDate() = vars.asof;
144
145 // Create bootstrapped curve
146 Handle<YieldTermStructure> discCurve = bootstrappedCurve(vars);
147
148 // Create the helper swap manually and price it using curve bootstrapped from helper
149 QuantLib::ext::shared_ptr<CrossCcyBasisMtMResetSwap> swap = makeTestSwap(vars, discCurve);
150
151 // Swap should have NPV = 0.0.
152 Real tol = 1e-5;
153 BOOST_CHECK_SMALL(swap->NPV(), tol);
154
155 // Check fair spreads match. Bootstrap uses 1e-12 accuracy.
156 Real relTol = 1e-10;
157 BOOST_CHECK_CLOSE(vars.spreadQuote->value(), swap->fairForeignSpread(), relTol);
158
159 // Check the 5Y discount factor
160 DiscountFactor expDisc = 0.91155524911218166;
161 BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);
162}
163
164BOOST_AUTO_TEST_CASE(testSpotFxChange) {
165
166 BOOST_TEST_MESSAGE("Test rebootstrap under spot FX change");
167
168 SavedSettings backup;
169
170 CommonVars vars;
171
172 Settings::instance().evaluationDate() = vars.asof;
173
174 // Create a helper and bootstrapped curve
175 Handle<YieldTermStructure> discCurve = bootstrappedCurve(vars);
176
177 // Create the helper swap manually and price it using curve bootstrapped from helper
178 QuantLib::ext::shared_ptr<CrossCcyBasisMtMResetSwap> swap = makeTestSwap(vars, discCurve);
179
180 // Check NPV = 0.0
181 Real absTol = 1e-5;
182 BOOST_CHECK_SMALL(swap->NPV(), absTol);
183
184 // Check fair spreads match. Bootstrap uses 1e-12 accuracy.
185 Real relTol = 1e-10;
186 BOOST_CHECK_CLOSE(vars.spreadQuote->value(), swap->fairForeignSpread(), relTol);
187
188 // Check the 5Y discount factor
189 DiscountFactor expDisc = 0.91155524911218166;
190 BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);
191
192 // Check the nominal of the helper swap
193 BOOST_CHECK_CLOSE(vars.spotFxQuote->value(), std::fabs(vars.helper->swap()->leg(2).front()->amount()), relTol);
194
195 // Bump the spot rate by 10%
196 vars.spotFxQuote->setValue(vars.spotFxQuote->value() * 1.1);
197
198 // Build a new swap using the updated spot FX rate
199 swap = makeTestSwap(vars, discCurve);
200
201 // Check that the new swap's NPV is 0.0
202 BOOST_CHECK_SMALL(swap->NPV(), absTol);
203
204 // Check the 5Y discount factor again. It should be the same.
205 BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);
206
207 // Check the nominal of the helper swap. Should now be the bumped amount
208 BOOST_CHECK_CLOSE(vars.spotFxQuote->value(), std::fabs(vars.helper->swap()->leg(2).front()->amount()), relTol);
209}
210
211BOOST_AUTO_TEST_CASE(testSpreadChange) {
212
213 BOOST_TEST_MESSAGE("Test rebootstrap under helper spread change");
214
215 SavedSettings backup;
216
217 CommonVars vars;
218
219 Settings::instance().evaluationDate() = vars.asof;
220
221 // Create a helper and bootstrapped curve
222 Handle<YieldTermStructure> discCurve = bootstrappedCurve(vars);
223
224 // Create the helper swap manually and price it using curve bootstrapped from helper
225 QuantLib::ext::shared_ptr<CrossCcyBasisMtMResetSwap> swap = makeTestSwap(vars, discCurve);
226
227 // Check NPV = 0.0
228 Real absTol = 1e-5;
229 BOOST_CHECK_SMALL(swap->NPV(), absTol);
230
231 // Check fair spreads match. Bootstrap uses 1e-12 accuracy.
232 Real relTol = 1e-10;
233 BOOST_CHECK_CLOSE(vars.spreadQuote->value(), swap->fairForeignSpread(), relTol);
234
235 // Check the 5Y discount factor
236 DiscountFactor expDisc = 0.91155524911218166;
237 BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);
238
239 // Add a 10bps spread
240 vars.spreadQuote->setValue(0.0015);
241
242 // Build a new swap using the updated spread of 10bps
243 swap = makeTestSwap(vars, discCurve);
244
245 // Check that the new swap's NPV is 0.0
246 BOOST_CHECK_SMALL(swap->NPV(), absTol);
247
248 // Check the 5Y discount factor again. More spread on foreign leg => more significant discount factor.
249 expDisc = 0.89807807922008731;
250 BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);
251
252 // Check the spread of the helper swap. Should now be 15bps.
253 BOOST_CHECK_CLOSE(vars.spreadQuote->value(), swap->fairForeignSpread(), relTol);
254}
255
256BOOST_AUTO_TEST_CASE(testMovingEvaluationDate) {
257
258 BOOST_TEST_MESSAGE("Test rebootstrap after moving evaluation date");
259
260 SavedSettings backup;
261
262 CommonVars vars;
263
264 Settings::instance().evaluationDate() = vars.asof;
265
266 // Create a helper and bootstrapped curve
267 Handle<YieldTermStructure> discCurve = bootstrappedCurve(vars);
268
269 // Create the helper swap manually and price it using curve bootstrapped from helper
270 QuantLib::ext::shared_ptr<CrossCcyBasisMtMResetSwap> swap = makeTestSwap(vars, discCurve);
271
272 // Check NPV = 0.0
273 Real absTol = 1e-5;
274 BOOST_CHECK_SMALL(swap->NPV(), absTol);
275
276 // Check fair spreads match. Bootstrap uses 1e-12 accuracy.
277 Real relTol = 1e-10;
278 BOOST_CHECK_CLOSE(vars.spreadQuote->value(), swap->fairForeignSpread(), relTol);
279
280 // Check the 5Y discount factor
281 DiscountFactor expDisc = 0.91155524911218166;
282 BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);
283
284 // Check the start date of the helper swap
285 BOOST_CHECK_EQUAL(swap->startDate(), vars.helper->swap()->startDate());
286
287 // Move evaluation date forward
288 vars.asof = vars.asof + 1 * Days;
289 Settings::instance().evaluationDate() = vars.asof;
290
291 // Build a new swap using new evaluation date
292 swap = makeTestSwap(vars, discCurve);
293
294 // Check that the new swap's NPV is 0.0
295 BOOST_CHECK_SMALL(swap->NPV(), absTol);
296
297 // Check the 5Y discount factor again. Changes slightly due to helper holidays/weekends.
298 expDisc = 0.91155524848230363;
299 BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);
300
301 // Check the start date of the helper swap. Should be 1 day greater.
302 BOOST_CHECK_EQUAL(swap->startDate(), vars.helper->swap()->startDate());
303}
304
305BOOST_AUTO_TEST_SUITE_END()
306
307BOOST_AUTO_TEST_SUITE_END()
Cross Ccy Basis MtM Reset Swap Rate Helper.
Cross currency basis MtM resettable swap.
Cross currency basis swap helper with MTM reset.
Cross currency swap engine.
QuantLib::BootstrapHelper< QuantLib::OptionletVolatilityStructure > helper
BOOST_AUTO_TEST_CASE(testBootstrap)
Fixture that can be used at top level.