1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3 /*!
4 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
5 Copyright (C) 2003, 2004, 2005, 2006, 2007 StatPro Italia srl
6 Copyright (C) 2004 Ferdinando Ametrano
7 Copyright (C) 2018 Jose Garcia
8
9 This file is part of QuantLib, a free-software/open-source library
10 for financial quantitative analysts and developers - http://quantlib.org/
11
12 QuantLib is free software: you can redistribute it and/or modify it
13 under the terms of the QuantLib license. You should have received a
14 copy of the license along with this program; if not, please email
15 <quantlib-dev@lists.sf.net>. The license is also available online at
16 <http://quantlib.org/license.shtml>.
17
18 This program is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20 FOR A PARTICULAR PURPOSE. See the license for more details.
21 */
22
23 /* This example shows how to set up a term structure with OIS discounting
24 and then price a simple 5 year swap.
25
26 Example based on market data in paper by F. M. Ametrano and M. Bianchetti,
27 Everything You Always Wanted to Know About Multiple Interest Rate Curve Boostrapping
28 but Were Afraid to Ask (April 2, 2013).
29 http://ssrn.com/abstract=2219548
30 Eonia curve was taken from Figure 25 and Euribor 6m from figure 31.
31 */
32
33 #include <ql/qldefines.hpp>
34 #if !defined(BOOST_ALL_NO_LIB) && defined(BOOST_MSVC)
35 #include <ql/auto_link.hpp>
36 #endif
37 #include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
38 #include <ql/termstructures/yieldtermstructure.hpp>
39 #include <ql/termstructures/yield/ratehelpers.hpp>
40 #include <ql/termstructures/yield/oisratehelper.hpp>
41 #include <ql/pricingengines/swap/discountingswapengine.hpp>
42 #include <ql/indexes/ibor/eonia.hpp>
43 #include <ql/indexes/ibor/euribor.hpp>
44 #include <ql/time/imm.hpp>
45 #include <ql/time/calendars/target.hpp>
46 #include <ql/time/daycounters/actual360.hpp>
47 #include <ql/time/daycounters/thirty360.hpp>
48 #include <ql/time/daycounters/actualactual.hpp>
49 #include <ql/math/interpolations/cubicinterpolation.hpp>
50 #include <ql/math/interpolations/loginterpolation.hpp>
51
52 #include <iostream>
53 #include <iomanip>
54
55 using namespace QuantLib;
56
57 int main(int, char* []) {
58
59 try {
60
61 std::cout << std::endl;
62
63 /*************************
64 *** GLOBAL SETTINGS ***
65 *************************/
66
67 Calendar calendar = TARGET();
68
69 Date todaysDate(11, December, 2012);
70 Settings::instance().evaluationDate() = todaysDate;
71 todaysDate = Settings::instance().evaluationDate();
72
73 Integer fixingDays = 2;
74 Date settlementDate = calendar.advance(todaysDate, fixingDays, Days);
75 // must be a business day
76 settlementDate = calendar.adjust(settlementDate);
77
78
79 std::cout << "Today: " << todaysDate.weekday()
80 << ", " << todaysDate << std::endl;
81
82 std::cout << "Settlement date: " << settlementDate.weekday()
83 << ", " << settlementDate << std::endl;
84
85 /*********************
86 ** EONIA CURVE **
87 *********************/
88
89 DayCounter termStructureDayCounter = Actual365Fixed();
90 std::vector<ext::shared_ptr<RateHelper>> eoniaInstruments;
91
92 auto eonia = ext::make_shared<Eonia>();
93
94 // a SimpleQuote instance stores a value which can be manually changed;
95 // other Quote subclasses could read the value from a database
96 // or some kind of data feed.
97
98 // RateHelpers are built from the quotes, together with
99 // other info depending on the instrument. Quotes are passed in
100 // relinkable handles which could be relinked to some other
101 // data source later.
102
103 // deposits
104
105 std::map<Natural, ext::shared_ptr<Quote>> depoQuotes = {
106 // settlement days, quote
107 {0, ext::make_shared<SimpleQuote>(0.0004)},
108 {1, ext::make_shared<SimpleQuote>(0.0004)},
109 {2, ext::make_shared<SimpleQuote>(0.0004)}
110 };
111
112 DayCounter depositDayCounter = Actual360();
113
114 for (const auto& q : depoQuotes) {
115 auto settlementDays = q.first;
116 auto quote = q.second;
117 auto helper = ext::make_shared<DepositRateHelper>(
118 Handle<Quote>(quote),
119 1 * Days, settlementDays,
120 calendar, Following,
121 false, depositDayCounter);
122 eoniaInstruments.push_back(helper);
123 }
124
125 // short-term OIS
126
127 std::map<Period, ext::shared_ptr<Quote>> shortOisQuotes = {
128 {1 * Weeks, ext::make_shared<SimpleQuote>(0.00070)},
129 {2 * Weeks, ext::make_shared<SimpleQuote>(0.00069)},
130 {3 * Weeks, ext::make_shared<SimpleQuote>(0.00078)},
131 {1 * Months, ext::make_shared<SimpleQuote>(0.00074)}
132 };
133
134 for (const auto& q : shortOisQuotes) {
135 auto tenor = q.first;
136 auto quote = q.second;
137 auto helper = ext::make_shared<OISRateHelper>(
138 2, tenor, Handle<Quote>(quote), eonia);
139 eoniaInstruments.push_back(helper);
140 }
141
142 // Dated OIS
143
144 std::map<std::pair<Date, Date>, ext::shared_ptr<Quote>> datedOisQuotes = {
145 {{Date(16, January, 2013), Date(13, February, 2013)}, ext::make_shared<SimpleQuote>( 0.000460)},
146 {{Date(13, February, 2013), Date(13, March, 2013)}, ext::make_shared<SimpleQuote>( 0.000160)},
147 {{Date(13, March, 2013), Date(10, April, 2013)}, ext::make_shared<SimpleQuote>(-0.000070)},
148 {{Date(10, April, 2013), Date(8, May, 2013)}, ext::make_shared<SimpleQuote>(-0.000130)},
149 {{Date(8, May, 2013), Date(12, June, 2013)}, ext::make_shared<SimpleQuote>(-0.000140)},
150 };
151
152 for (const auto& q : datedOisQuotes) {
153 auto startDate = q.first.first;
154 auto endDate = q.first.second;
155 auto quote = q.second;
156 auto helper = ext::make_shared<DatedOISRateHelper>(
157 startDate, endDate, Handle<Quote>(quote), eonia);
158 eoniaInstruments.push_back(helper);
159 }
160
161 // long-term OIS
162
163 std::map<Period, ext::shared_ptr<Quote>> longOisQuotes = {
164 {15 * Months, ext::make_shared<SimpleQuote>(0.00002)},
165 {18 * Months, ext::make_shared<SimpleQuote>(0.00008)},
166 {21 * Months, ext::make_shared<SimpleQuote>(0.00021)},
167 {2 * Years, ext::make_shared<SimpleQuote>(0.00036)},
168 {3 * Years, ext::make_shared<SimpleQuote>(0.00127)},
169 {4 * Years, ext::make_shared<SimpleQuote>(0.00274)},
170 {5 * Years, ext::make_shared<SimpleQuote>(0.00456)},
171 {6 * Years, ext::make_shared<SimpleQuote>(0.00647)},
172 {7 * Years, ext::make_shared<SimpleQuote>(0.00827)},
173 {8 * Years, ext::make_shared<SimpleQuote>(0.00996)},
174 {9 * Years, ext::make_shared<SimpleQuote>(0.01147)},
175 {10 * Years, ext::make_shared<SimpleQuote>(0.0128)},
176 {11 * Years, ext::make_shared<SimpleQuote>(0.01404)},
177 {12 * Years, ext::make_shared<SimpleQuote>(0.01516)},
178 {15 * Years, ext::make_shared<SimpleQuote>(0.01764)},
179 {20 * Years, ext::make_shared<SimpleQuote>(0.01939)},
180 {25 * Years, ext::make_shared<SimpleQuote>(0.02003)},
181 {30 * Years, ext::make_shared<SimpleQuote>(0.02038)}
182 };
183
184 for (const auto& q : longOisQuotes) {
185 auto tenor = q.first;
186 auto quote = q.second;
187 auto helper = ext::make_shared<OISRateHelper>(
188 2, tenor, Handle<Quote>(quote), eonia);
189 eoniaInstruments.push_back(helper);
190 }
191
192 // curve
193
194 auto eoniaTermStructure = ext::make_shared<PiecewiseYieldCurve<Discount, Cubic>>(
195 todaysDate, eoniaInstruments, termStructureDayCounter);
196
197 eoniaTermStructure->enableExtrapolation();
198
199 // This curve will be used for discounting cash flows
200 RelinkableHandle<YieldTermStructure> discountingTermStructure;
201 discountingTermStructure.linkTo(eoniaTermStructure);
202
203
204 /**************************
205 ** EURIBOR 6M CURVE **
206 ***************************/
207
208 std::vector<ext::shared_ptr<RateHelper>> euribor6MInstruments;
209
210 auto euribor6M = ext::make_shared<Euribor6M>();
211
212 // deposits
213
214 auto d6MRate = ext::make_shared<SimpleQuote>(0.00312);
215
216 auto d6M = ext::make_shared<DepositRateHelper>(
217 Handle<Quote>(d6MRate),
218 6 * Months, 3,
219 calendar, Following,
220 false, depositDayCounter);
221
222 euribor6MInstruments.push_back(d6M);
223
224 // FRAs
225
226 std::map<Natural, ext::shared_ptr<Quote>> fraQuotes = {
227 {1, ext::make_shared<SimpleQuote>(0.002930)},
228 {2, ext::make_shared<SimpleQuote>(0.002720)},
229 {3, ext::make_shared<SimpleQuote>(0.002600)},
230 {4, ext::make_shared<SimpleQuote>(0.002560)},
231 {5, ext::make_shared<SimpleQuote>(0.002520)},
232 {6, ext::make_shared<SimpleQuote>(0.002480)},
233 {7, ext::make_shared<SimpleQuote>(0.002540)},
234 {8, ext::make_shared<SimpleQuote>(0.002610)},
235 {9, ext::make_shared<SimpleQuote>(0.002670)},
236 {10, ext::make_shared<SimpleQuote>(0.002790)},
237 {11, ext::make_shared<SimpleQuote>(0.002910)},
238 {12, ext::make_shared<SimpleQuote>(0.003030)},
239 {13, ext::make_shared<SimpleQuote>(0.003180)},
240 {14, ext::make_shared<SimpleQuote>(0.003350)},
241 {15, ext::make_shared<SimpleQuote>(0.003520)},
242 {16, ext::make_shared<SimpleQuote>(0.003710)},
243 {17, ext::make_shared<SimpleQuote>(0.003890)},
244 {18, ext::make_shared<SimpleQuote>(0.004090)}
245 };
246
247 for (const auto& q : fraQuotes) {
248 auto monthsToStart = q.first;
249 auto quote = q.second;
250 auto helper = ext::make_shared<FraRateHelper>(
251 Handle<Quote>(quote),
252 monthsToStart, euribor6M);
253 euribor6MInstruments.push_back(helper);
254 }
255
256 // swaps
257
258 std::map<Period, ext::shared_ptr<Quote>> swapQuotes = {
259 {3 * Years, ext::make_shared<SimpleQuote>(0.004240)},
260 {4 * Years, ext::make_shared<SimpleQuote>(0.005760)},
261 {5 * Years, ext::make_shared<SimpleQuote>(0.007620)},
262 {6 * Years, ext::make_shared<SimpleQuote>(0.009540)},
263 {7 * Years, ext::make_shared<SimpleQuote>(0.011350)},
264 {8 * Years, ext::make_shared<SimpleQuote>(0.013030)},
265 {9 * Years, ext::make_shared<SimpleQuote>(0.014520)},
266 {10 * Years, ext::make_shared<SimpleQuote>(0.015840)},
267 {12 * Years, ext::make_shared<SimpleQuote>(0.018090)},
268 {15 * Years, ext::make_shared<SimpleQuote>(0.020370)},
269 {20 * Years, ext::make_shared<SimpleQuote>(0.021870)},
270 {25 * Years, ext::make_shared<SimpleQuote>(0.022340)},
271 {30 * Years, ext::make_shared<SimpleQuote>(0.022560)},
272 {35 * Years, ext::make_shared<SimpleQuote>(0.022950)},
273 {40 * Years, ext::make_shared<SimpleQuote>(0.023480)},
274 {50 * Years, ext::make_shared<SimpleQuote>(0.024210)},
275 {60 * Years, ext::make_shared<SimpleQuote>(0.024630)}
276 };
277
278 Frequency swFixedLegFrequency = Annual;
279 BusinessDayConvention swFixedLegConvention = Unadjusted;
280 DayCounter swFixedLegDayCounter = Thirty360(Thirty360::European);
281
282 for (const auto& q : swapQuotes) {
283 auto tenor = q.first;
284 auto quote = q.second;
285 auto helper = ext::make_shared<SwapRateHelper>(
286 Handle<Quote>(quote), tenor,
287 calendar, swFixedLegFrequency,
288 swFixedLegConvention, swFixedLegDayCounter,
289 euribor6M,
290 Handle<Quote>(), 0 * Days,
291 discountingTermStructure); // the Eonia curve is used for discounting
292 euribor6MInstruments.push_back(helper);
293 }
294
295 // If needed, it's possible to change the tolerance; the default is 1.0e-12.
296 // The tolerance is passed in an explicit bootstrap object. Depending on
297 // the bootstrap algorithm, it's possible to pass other parameters.
298 double tolerance = 1.0e-15;
299 auto euribor6MTermStructure = ext::make_shared<PiecewiseYieldCurve<Discount, Cubic>>(
300 settlementDate, euribor6MInstruments,
301 termStructureDayCounter,
302 PiecewiseYieldCurve<Discount, Cubic>::bootstrap_type(tolerance));
303
304 // This curve will be used for forward-rate forecasting
305
306 RelinkableHandle<YieldTermStructure> forecastingTermStructure;
307 forecastingTermStructure.linkTo(euribor6MTermStructure);
308
309 /*********************
310 * SWAPS TO BE PRICED *
311 **********************/
312
313 // constant nominal 1,000,000 Euro
314 Real nominal = 1000000.0;
315 // fixed leg
316 Frequency fixedLegFrequency = Annual;
317 BusinessDayConvention fixedLegConvention = Unadjusted;
318 BusinessDayConvention floatingLegConvention = ModifiedFollowing;
319 DayCounter fixedLegDayCounter = Thirty360(Thirty360::European);
320 Rate fixedRate = 0.007;
321 DayCounter floatingLegDayCounter = Actual360();
322
323 // floating leg
324 Frequency floatingLegFrequency = Semiannual;
325 auto euriborIndex = ext::make_shared<Euribor6M>(forecastingTermStructure);
326 Spread spread = 0.0;
327
328 Integer lengthInYears = 5;
329 Swap::Type swapType = Swap::Payer;
330
331 Date maturity = settlementDate + lengthInYears*Years;
332 Schedule fixedSchedule(settlementDate, maturity,
333 Period(fixedLegFrequency),
334 calendar, fixedLegConvention,
335 fixedLegConvention,
336 DateGeneration::Forward, false);
337 Schedule floatSchedule(settlementDate, maturity,
338 Period(floatingLegFrequency),
339 calendar, floatingLegConvention,
340 floatingLegConvention,
341 DateGeneration::Forward, false);
342 VanillaSwap spot5YearSwap(swapType, nominal,
343 fixedSchedule, fixedRate, fixedLegDayCounter,
344 floatSchedule, euriborIndex, spread,
345 floatingLegDayCounter);
346
347 Date fwdStart = calendar.advance(settlementDate, 1, Years);
348 Date fwdMaturity = fwdStart + lengthInYears*Years;
349 Schedule fwdFixedSchedule(fwdStart, fwdMaturity,
350 Period(fixedLegFrequency),
351 calendar, fixedLegConvention,
352 fixedLegConvention,
353 DateGeneration::Forward, false);
354 Schedule fwdFloatSchedule(fwdStart, fwdMaturity,
355 Period(floatingLegFrequency),
356 calendar, floatingLegConvention,
357 floatingLegConvention,
358 DateGeneration::Forward, false);
359 VanillaSwap oneYearForward5YearSwap(swapType, nominal,
360 fwdFixedSchedule, fixedRate, fixedLegDayCounter,
361 fwdFloatSchedule, euriborIndex, spread,
362 floatingLegDayCounter);
363
364
365 /***************
366 * SWAP PRICING *
367 ****************/
368
369 // utilities for formatting the report
370
371 std::ostringstream s1;
372 s1 << "5-years swap paying " << std::setprecision(2) << io::rate(fixedRate);
373 std::string case1 = s1.str();
374
375 std::ostringstream s2;
376 s2 << "5-years, 1-year forward swap paying " << std::setprecision(2) << io::rate(fixedRate);
377 std::string case2 = s2.str();
378
379 std::vector<std::string> headers(4);
380 headers[0] = std::string(std::max(case1.size(), case2.size()) + 1, ' ');
381 headers[1] = "net present value";
382 headers[2] = "fair spread";
383 headers[3] = "fair fixed rate";
384 std::string separator = " | ";
385 std::string header = headers[0] + separator + headers[1] + separator + headers[2] + separator + headers[3];
386 Size width = header.size();
387 std::string rule(width, '-'), dblrule(width, '=');
388
389 // calculations
390
391 auto s5yRate = swapQuotes[5 * Years];
392
393 std::cout << dblrule << std::endl;
394 std::cout << " With 5-year market swap-rate = "
395 << std::setprecision(2) << io::rate(s5yRate->value())
396 << std::endl;
397 std::cout << rule << std::endl;
398
399 std::cout << header << std::endl;
400 std::cout << rule << std::endl;
401
402 Real NPV;
403 Rate fairRate;
404 Spread fairSpread;
405
406 auto swapEngine = ext::make_shared<DiscountingSwapEngine>(discountingTermStructure);
407
408 spot5YearSwap.setPricingEngine(swapEngine);
409 oneYearForward5YearSwap.setPricingEngine(swapEngine);
410
411 NPV = spot5YearSwap.NPV();
412 fairSpread = spot5YearSwap.fairSpread();
413 fairRate = spot5YearSwap.fairRate();
414
415 std::cout << std::setw(headers[0].size())
416 << case1 << separator;
417 std::cout << std::setw(headers[1].size())
418 << std::fixed << std::setprecision(2) << NPV << separator;
419 std::cout << std::setw(headers[2].size())
420 << io::rate(fairSpread) << separator;
421 std::cout << std::setw(headers[3].size())
422 << io::rate(fairRate);
423 std::cout << std::endl;
424
425 std::cout << rule << std::endl;
426
427 // let's check that the 5 years swap has been correctly re-priced
428 QL_REQUIRE(std::fabs(fairRate-s5yRate->value())<1e-8,
429 "5-years swap mispriced by "
430 << io::rate(std::fabs(fairRate-s5yRate->value())));
431
432 // now let's price the 1Y forward 5Y swap
433
434 NPV = oneYearForward5YearSwap.NPV();
435 fairSpread = oneYearForward5YearSwap.fairSpread();
436 fairRate = oneYearForward5YearSwap.fairRate();
437
438 std::cout << std::setw(headers[0].size())
439 << case2 << separator;
440 std::cout << std::setw(headers[1].size())
441 << std::fixed << std::setprecision(2) << NPV << separator;
442 std::cout << std::setw(headers[2].size())
443 << io::rate(fairSpread) << separator;
444 std::cout << std::setw(headers[3].size())
445 << io::rate(fairRate);
446 std::cout << std::endl;
447
448 // now let's say that the 5-years swap rate goes up to 0.90%.
449 // A smarter market element--say, connected to a data source-- would
450 // notice the change itself. Since we're using SimpleQuotes,
451 // we'll have to change the value manually--which forces us to
452 // downcast the handle and use the SimpleQuote
453 // interface. In any case, the point here is that a change in the
454 // value contained in the Quote triggers a new bootstrapping
455 // of the curve and a repricing of the swap.
456
457 auto fiveYearsRate = ext::dynamic_pointer_cast<SimpleQuote>(s5yRate);
458 fiveYearsRate->setValue(0.0090);
459
460 std::cout << dblrule << std::endl;
461 std::cout << " With 5-year market swap-rate = "
462 << io::rate(s5yRate->value()) << std::endl;
463 std::cout << rule << std::endl;
464
465 std::cout << header << std::endl;
466 std::cout << rule << std::endl;
467
468 // now get the updated results
469
470 NPV = spot5YearSwap.NPV();
471 fairSpread = spot5YearSwap.fairSpread();
472 fairRate = spot5YearSwap.fairRate();
473
474 std::cout << std::setw(headers[0].size())
475 << case1 << separator;
476 std::cout << std::setw(headers[1].size())
477 << std::fixed << std::setprecision(2) << NPV << separator;
478 std::cout << std::setw(headers[2].size())
479 << io::rate(fairSpread) << separator;
480 std::cout << std::setw(headers[3].size())
481 << io::rate(fairRate);
482 std::cout << std::endl;
483
484 QL_REQUIRE(std::fabs(fairRate-s5yRate->value())<1e-8,
485 "5-years swap mispriced!");
486
487 std::cout << rule << std::endl;
488
489 // the 1Y forward 5Y swap doesn't change;
490 // it depends on the 1-year and 6-years rates, which didn't move
491
492 NPV = oneYearForward5YearSwap.NPV();
493 fairSpread = oneYearForward5YearSwap.fairSpread();
494 fairRate = oneYearForward5YearSwap.fairRate();
495
496 std::cout << std::setw(headers[0].size())
497 << case2 << separator;
498 std::cout << std::setw(headers[1].size())
499 << std::fixed << std::setprecision(2) << NPV << separator;
500 std::cout << std::setw(headers[2].size())
501 << io::rate(fairSpread) << separator;
502 std::cout << std::setw(headers[3].size())
503 << io::rate(fairRate);
504 std::cout << std::endl;
505
506 std::cout << dblrule << std::endl;
507
508 return 0;
509
510 } catch (std::exception& e) {
511 std::cerr << e.what() << std::endl;
512 return 1;
513 } catch (...) {
514 std::cerr << "unknown error" << std::endl;
515 return 1;
516 }
517 }