Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
crossccyfixfloatswap.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/usdlibor.hpp>
24#include <ql/quotes/simplequote.hpp>
25#include <ql/termstructures/yield/discountcurve.hpp>
26#include <ql/time/calendars/all.hpp>
27#include <ql/time/daycounters/actual360.hpp>
28#include <ql/types.hpp>
31
32using namespace std;
33using namespace boost::unit_test_framework;
34using namespace QuantLib;
35using namespace QuantExt;
36
37namespace {
38
39Handle<YieldTermStructure> usdDiscountCurve() {
40
41 vector<Date> dates(27);
42 vector<DiscountFactor> dfs(27);
43 Actual365Fixed dayCounter;
44
45 dates[0] = Date(11, Sep, 2018);
46 dfs[0] = 1;
47 dates[1] = Date(14, Sep, 2018);
48 dfs[1] = 0.99994666951096;
49 dates[2] = Date(20, Sep, 2018);
50 dfs[2] = 0.999627719221066;
51 dates[3] = Date(27, Sep, 2018);
52 dfs[3] = 0.999254084816959;
53 dates[4] = Date(04, Oct, 2018);
54 dfs[4] = 0.998837020905631;
55 dates[5] = Date(15, Oct, 2018);
56 dfs[5] = 0.998176132423265;
57 dates[6] = Date(13, Nov, 2018);
58 dfs[6] = 0.99644587210048;
59 dates[7] = Date(13, Dec, 2018);
60 dfs[7] = 0.994644668243218;
61 dates[8] = Date(14, Jan, 2019);
62 dfs[8] = 0.992596634984033;
63 dates[9] = Date(13, Feb, 2019);
64 dfs[9] = 0.990636503861861;
65 dates[10] = Date(13, Mar, 2019);
66 dfs[10] = 0.988809127958345;
67 dates[11] = Date(13, Jun, 2019);
68 dfs[11] = 0.982417991680868;
69 dates[12] = Date(13, Sep, 2019);
70 dfs[12] = 0.975723193871552;
71 dates[13] = Date(13, Mar, 2020);
72 dfs[13] = 0.96219213956104;
73 dates[14] = Date(14, Sep, 2020);
74 dfs[14] = 0.948588232418325;
75 dates[15] = Date(13, Sep, 2021);
76 dfs[15] = 0.92279636773464;
77 dates[16] = Date(13, Sep, 2022);
78 dfs[16] = 0.898345201557914;
79 dates[17] = Date(13, Sep, 2023);
80 dfs[17] = 0.874715322269088;
81 dates[18] = Date(15, Sep, 2025);
82 dfs[18] = 0.828658611114833;
83 dates[19] = Date(13, Sep, 2028);
84 dfs[19] = 0.763030152740947;
85 dates[20] = Date(13, Sep, 2030);
86 dfs[20] = 0.722238847877756;
87 dates[21] = Date(13, Sep, 2033);
88 dfs[21] = 0.664460629674362;
89 dates[22] = Date(13, Sep, 2038);
90 dfs[22] = 0.580288693473926;
91 dates[23] = Date(14, Sep, 2043);
92 dfs[23] = 0.510857007600479;
93 dates[24] = Date(14, Sep, 2048);
94 dfs[24] = 0.44941525649436;
95 dates[25] = Date(13, Sep, 2058);
96 dfs[25] = 0.352389176933952;
97 dates[26] = Date(13, Sep, 2068);
98 dfs[26] = 0.28183300653329;
99
100 return Handle<YieldTermStructure>(QuantLib::ext::make_shared<DiscountCurve>(dates, dfs, dayCounter));
101}
102
103Handle<YieldTermStructure> usdProjectionCurve() {
104
105 vector<Date> dates(25);
106 vector<DiscountFactor> dfs(25);
107 Actual365Fixed dayCounter;
108
109 dates[0] = Date(11, Sep, 2018);
110 dfs[0] = 1;
111 dates[1] = Date(13, Dec, 2018);
112 dfs[1] = 0.994134145990132;
113 dates[2] = Date(19, Dec, 2018);
114 dfs[2] = 0.993695776146116;
115 dates[3] = Date(20, Mar, 2019);
116 dfs[3] = 0.987047992958673;
117 dates[4] = Date(19, Jun, 2019);
118 dfs[4] = 0.980016364694049;
119 dates[5] = Date(18, Sep, 2019);
120 dfs[5] = 0.972708376777628;
121 dates[6] = Date(18, Dec, 2019);
122 dfs[6] = 0.965277162951128;
123 dates[7] = Date(18, Mar, 2020);
124 dfs[7] = 0.957799302363697;
125 dates[8] = Date(14, Sep, 2020);
126 dfs[8] = 0.943264331984248;
127 dates[9] = Date(13, Sep, 2021);
128 dfs[9] = 0.914816470778467;
129 dates[10] = Date(13, Sep, 2022);
130 dfs[10] = 0.88764714641623;
131 dates[11] = Date(13, Sep, 2023);
132 dfs[11] = 0.861475671008934;
133 dates[12] = Date(13, Sep, 2024);
134 dfs[12] = 0.835944798717806;
135 dates[13] = Date(15, Sep, 2025);
136 dfs[13] = 0.810833947617338;
137 dates[14] = Date(14, Sep, 2026);
138 dfs[14] = 0.78631849267276;
139 dates[15] = Date(13, Sep, 2027);
140 dfs[15] = 0.762267648509673;
141 dates[16] = Date(13, Sep, 2028);
142 dfs[16] = 0.738613627359076;
143 dates[17] = Date(13, Sep, 2029);
144 dfs[17] = 0.715502378943932;
145 dates[18] = Date(13, Sep, 2030);
146 dfs[18] = 0.693380472578176;
147 dates[19] = Date(13, Sep, 2033);
148 dfs[19] = 0.631097994110912;
149 dates[20] = Date(13, Sep, 2038);
150 dfs[20] = 0.540797634630251;
151 dates[21] = Date(14, Sep, 2043);
152 dfs[21] = 0.465599237331079;
153 dates[22] = Date(14, Sep, 2048);
154 dfs[22] = 0.402119473746341;
155 dates[23] = Date(13, Sep, 2058);
156 dfs[23] = 0.303129773289934;
157 dates[24] = Date(13, Sep, 2068);
158 dfs[24] = 0.23210070222569;
159
160 return Handle<YieldTermStructure>(QuantLib::ext::make_shared<DiscountCurve>(dates, dfs, dayCounter));
161}
162
163Handle<YieldTermStructure> tryDiscountCurve() {
164
165 vector<Date> dates(18);
166 vector<DiscountFactor> dfs(18);
167 Actual365Fixed dayCounter;
168
169 dates[0] = Date(11, Sep, 2018);
170 dfs[0] = 1;
171 dates[1] = Date(15, Oct, 2018);
172 dfs[1] = 0.979316826759248;
173 dates[2] = Date(13, Nov, 2018);
174 dfs[2] = 0.959997676372812;
175 dates[3] = Date(13, Dec, 2018);
176 dfs[3] = 0.939987819768341;
177 dates[4] = Date(14, Jan, 2019);
178 dfs[4] = 0.917879348095857;
179 dates[5] = Date(13, Feb, 2019);
180 dfs[5] = 0.897309447005875;
181 dates[6] = Date(13, Mar, 2019);
182 dfs[6] = 0.878377243062539;
183 dates[7] = Date(13, Sep, 2019);
184 dfs[7] = 0.76374502801031;
185 dates[8] = Date(14, Sep, 2020);
186 dfs[8] = 0.595566112318217;
187 dates[9] = Date(13, Sep, 2021);
188 dfs[9] = 0.483132147134316;
189 dates[10] = Date(13, Sep, 2022);
190 dfs[10] = 0.402466076327945;
191 dates[11] = Date(13, Sep, 2023);
192 dfs[11] = 0.345531820837392;
193 dates[12] = Date(13, Sep, 2024);
194 dfs[12] = 0.298070398810781;
195 dates[13] = Date(13, Sep, 2025);
196 dfs[13] = 0.264039803303106;
197 dates[14] = Date(13, Sep, 2026);
198 dfs[14] = 0.237813130821584;
199 dates[15] = Date(13, Sep, 2027);
200 dfs[15] = 0.216456097559999;
201 dates[16] = Date(13, Sep, 2028);
202 dfs[16] = 0.200289181912326;
203 dates[17] = Date(13, Sep, 2033);
204 dfs[17] = 0.122659501286113;
205
206 return Handle<YieldTermStructure>(QuantLib::ext::make_shared<DiscountCurve>(dates, dfs, dayCounter));
207}
208
209QuantLib::ext::shared_ptr<CrossCcyFixFloatSwap> makeTestSwap(Rate spotFx, Rate rate, Spread spread) {
210
211 // USD nominal
212 Real usdNominal = 10000000.0;
213
214 // Shared settlement conventions
215 BusinessDayConvention payConvention = Following;
216 Natural payLag = 0;
217 JointCalendar payCalendar = JointCalendar(UnitedStates(UnitedStates::Settlement), UnitedKingdom(), Turkey());
218
219 // Swap start and end date
220 Date referenceDate = Settings::instance().evaluationDate();
221 referenceDate = payCalendar.adjust(referenceDate);
222 Date start = payCalendar.advance(referenceDate, 2 * Days);
223 Date end = start + 5 * Years;
224
225 // Fixed TRY schedule
226 Schedule fixedSchedule(start, end, 1 * Years, payCalendar, ModifiedFollowing, ModifiedFollowing,
227 DateGeneration::Backward, false);
228
229 // Float USD schedule
230 Schedule floatSchedule(start, end, 3 * Months, payCalendar, ModifiedFollowing, ModifiedFollowing,
231 DateGeneration::Backward, false);
232
233 QuantLib::ext::shared_ptr<IborIndex> index = QuantLib::ext::make_shared<USDLibor>(3 * Months, usdProjectionCurve());
234
235 // Create swap
236 return QuantLib::ext::shared_ptr<CrossCcyFixFloatSwap>(
237 new CrossCcyFixFloatSwap(CrossCcyFixFloatSwap::Payer, usdNominal * spotFx, TRYCurrency(), fixedSchedule, rate,
238 Actual360(), payConvention, payLag, payCalendar, usdNominal, USDCurrency(),
239 floatSchedule, index, spread, payConvention, payLag, payCalendar));
240}
241
242} // namespace
243
244BOOST_FIXTURE_TEST_SUITE(QuantExtTestSuite, qle::test::TopLevelFixture)
245
246BOOST_AUTO_TEST_SUITE(CrossCurrencyFixFloatSwapTest)
247
248BOOST_AUTO_TEST_CASE(testSwapPricing) {
249
250 BOOST_TEST_MESSAGE("Test cross currency fix float swap pricing against known results");
251
252 SavedSettings backup;
253
254 Settings::instance().evaluationDate() = Date(11, Sep, 2018);
255
256 // Create swap, USD 3M Libor vs TRY annual fixed
257 Rate spotFx = 6.4304;
258 Rate rate = 0.249;
259 Spread spread = 0.0;
260 QuantLib::ext::shared_ptr<CrossCcyFixFloatSwap> swap = makeTestSwap(spotFx, rate, spread);
261
262 // Attach pricing engine
263 Handle<Quote> fxSpotQuote = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.0 / spotFx));
264 QuantLib::ext::shared_ptr<PricingEngine> engine = QuantLib::ext::make_shared<CrossCcySwapEngine>(
265 USDCurrency(), usdDiscountCurve(), TRYCurrency(), tryDiscountCurve(), fxSpotQuote);
266 swap->setPricingEngine(engine);
267
268 // Check values
269 Real usdTolerance = 0.01;
270
271 Real expNpv = 129777.91;
272 BOOST_CHECK_SMALL(swap->NPV() - expNpv, usdTolerance);
273
274 Real expPayLegNpv = -12286.45;
275 Real expPayLegBps = -2628.39;
276 BOOST_CHECK_SMALL(swap->legNPV(0) - expPayLegNpv, usdTolerance);
277 BOOST_CHECK_SMALL(swap->legBPS(0) - expPayLegBps, usdTolerance);
278 BOOST_CHECK_SMALL(swap->inCcyLegNPV(0) - expPayLegNpv * spotFx, usdTolerance * spotFx);
279 BOOST_CHECK_SMALL(swap->inCcyLegBPS(0) - expPayLegBps * spotFx, usdTolerance * spotFx);
280
281 Real expRecLegNpv = 142064.36;
282 Real expRecLegBps = 4735.03;
283 BOOST_CHECK_SMALL(swap->legNPV(1) - expRecLegNpv, usdTolerance);
284 BOOST_CHECK_SMALL(swap->legBPS(1) - expRecLegBps, usdTolerance);
285 BOOST_CHECK_SMALL(swap->inCcyLegNPV(1) - expRecLegNpv, usdTolerance);
286 BOOST_CHECK_SMALL(swap->inCcyLegBPS(1) - expRecLegBps, usdTolerance);
287
288 BOOST_CHECK_SMALL(0.253937551076 - swap->fairFixedRate(), 1e-10);
289 BOOST_CHECK_SMALL(-0.002740802104 - swap->fairSpread(), 1e-10);
290}
291
292BOOST_AUTO_TEST_SUITE_END()
293
294BOOST_AUTO_TEST_SUITE_END()
Cross currency fixed vs float swap instrument.
Cross currency swap engine.
BOOST_AUTO_TEST_CASE(testSwapPricing)
Fixture that can be used at top level.