[go: up one dir, main page]

1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2006 Cristina Duminuco
5 Copyright (C) 2008 StatPro Italia srl
6
7 This file is part of QuantLib, a free-software/open-source library
8 for financial quantitative analysts and developers - http://quantlib.org/
9
10 QuantLib is free software: you can redistribute it and/or modify it
11 under the terms of the QuantLib license. You should have received a
12 copy of the license along with this program; if not, please email
13 <quantlib-dev@lists.sf.net>. The license is also available online at
14 <http://quantlib.org/license.shtml>.
15
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the license for more details.
19*/
20
21#include "capflooredcoupon.hpp"
22#include "utilities.hpp"
23#include <ql/instruments/capfloor.hpp>
24#include <ql/instruments/vanillaswap.hpp>
25#include <ql/cashflows/cashflowvectors.hpp>
26#include <ql/termstructures/yield/flatforward.hpp>
27#include <ql/indexes/ibor/euribor.hpp>
28#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
29#include <ql/pricingengines/swap/discountingswapengine.hpp>
30#include <ql/math/matrix.hpp>
31#include <ql/termstructures/volatility/optionlet/constantoptionletvol.hpp>
32#include <ql/time/daycounters/thirty360.hpp>
33#include <ql/time/daycounters/actualactual.hpp>
34#include <ql/time/schedule.hpp>
35#include <ql/utilities/dataformatters.hpp>
36#include <ql/cashflows/cashflows.hpp>
37#include <ql/cashflows/couponpricer.hpp>
38#include <ql/quotes/simplequote.hpp>
39
40using namespace QuantLib;
41using namespace boost::unit_test_framework;
42
43namespace capfloored_coupon_test {
44
45 struct CommonVars {
46 // global data
47 Date today, settlement, startDate;
48 Calendar calendar;
49 Real nominal;
50 std::vector<Real> nominals;
51 BusinessDayConvention convention;
52 Frequency frequency;
53 ext::shared_ptr<IborIndex> index;
54 Natural settlementDays, fixingDays;
55 RelinkableHandle<YieldTermStructure> termStructure;
56 std::vector<Rate> caps;
57 std::vector<Rate> floors;
58 Integer length;
59 Volatility volatility;
60
61 // setup
62 CommonVars() {
63 length = 20; //years
64 volatility = 0.20;
65 nominal = 100.;
66 nominals = std::vector<Real>(length,nominal);
67 frequency = Annual;
68 index = ext::shared_ptr<IborIndex>(new Euribor1Y(termStructure));
69 calendar = index->fixingCalendar();
70 convention = ModifiedFollowing;
71 today = calendar.adjust(Date::todaysDate());
72 Settings::instance().evaluationDate() = today;
73 settlementDays = 2;
74 fixingDays = 2;
75 settlement = calendar.advance(today,n: settlementDays,unit: Days);
76 startDate = settlement;
77 termStructure.linkTo(h: flatRate(today: settlement,forward: 0.05,
78 dc: ActualActual(ActualActual::ISDA)));
79 }
80
81 // utilities
82 Leg makeFixedLeg(const Date& startDate, Integer length) const {
83
84 Date endDate = calendar.advance(startDate, n: length, unit: Years,
85 convention);
86 Schedule schedule(startDate, endDate, Period(frequency), calendar,
87 convention, convention,
88 DateGeneration::Forward, false);
89 std::vector<Rate> coupons(length, 0.0);
90 return FixedRateLeg(schedule)
91 .withNotionals(nominals)
92 .withCouponRates(coupons, paymentDayCounter: Thirty360(Thirty360::BondBasis));
93 }
94
95 Leg makeFloatingLeg(const Date& startDate,
96 Integer length,
97 const Rate gearing = 1.0,
98 const Rate spread = 0.0) const {
99
100 Date endDate = calendar.advance(startDate,n: length,unit: Years,convention);
101 Schedule schedule(startDate,endDate,Period(frequency),calendar,
102 convention,convention,
103 DateGeneration::Forward,false);
104 std::vector<Real> gearingVector(length, gearing);
105 std::vector<Spread> spreadVector(length, spread);
106 return IborLeg(schedule, index)
107 .withNotionals(notionals: nominals)
108 .withPaymentDayCounter(index->dayCounter())
109 .withPaymentAdjustment(convention)
110 .withFixingDays(fixingDays)
111 .withGearings(gearings: gearingVector)
112 .withSpreads(spreads: spreadVector);
113 }
114
115 Leg makeCapFlooredLeg(const Date& startDate,
116 Integer length,
117 const std::vector<Rate>& caps,
118 const std::vector<Rate>& floors,
119 Volatility volatility,
120 const Rate gearing = 1.0,
121 const Rate spread = 0.0) const {
122
123 Date endDate = calendar.advance(startDate,n: length,unit: Years,convention);
124 Schedule schedule(startDate,endDate,Period(frequency),calendar,
125 convention,convention,
126 DateGeneration::Forward,false);
127 Handle<OptionletVolatilityStructure> vol(
128 ext::shared_ptr<OptionletVolatilityStructure>(new
129 ConstantOptionletVolatility(0, calendar, Following,
130 volatility,Actual365Fixed())));
131
132 ext::shared_ptr<IborCouponPricer> pricer(new
133 BlackIborCouponPricer(vol));
134 std::vector<Rate> gearingVector(length, gearing);
135 std::vector<Spread> spreadVector(length, spread);
136
137 Leg iborLeg = IborLeg(schedule, index)
138 .withNotionals(notionals: nominals)
139 .withPaymentDayCounter(index->dayCounter())
140 .withPaymentAdjustment(convention)
141 .withFixingDays(fixingDays)
142 .withGearings(gearings: gearingVector)
143 .withSpreads(spreads: spreadVector)
144 .withCaps(caps)
145 .withFloors(floors);
146 setCouponPricer(leg: iborLeg, pricer);
147 return iborLeg;
148 }
149
150 ext::shared_ptr<PricingEngine> makeEngine(Volatility volatility) const {
151 Handle<Quote> vol(ext::shared_ptr<Quote>(
152 new SimpleQuote(volatility)));
153 return ext::shared_ptr<PricingEngine>(
154 new BlackCapFloorEngine(termStructure, vol));
155 }
156
157 ext::shared_ptr<CapFloor> makeCapFloor(CapFloor::Type type,
158 const Leg& leg,
159 Rate capStrike,
160 Rate floorStrike,
161 Volatility volatility) const {
162 ext::shared_ptr<CapFloor> result;
163 switch (type) {
164 case CapFloor::Cap:
165 result = ext::shared_ptr<CapFloor>(
166 new Cap(leg, std::vector<Rate>(1, capStrike)));
167 break;
168 case CapFloor::Floor:
169 result = ext::shared_ptr<CapFloor>(
170 new Floor(leg, std::vector<Rate>(1, floorStrike)));
171 break;
172 case CapFloor::Collar:
173 result = ext::shared_ptr<CapFloor>(
174 new Collar(leg,
175 std::vector<Rate>(1, capStrike),
176 std::vector<Rate>(1, floorStrike)));
177 break;
178 default:
179 QL_FAIL("unknown cap/floor type");
180 }
181 result->setPricingEngine(makeEngine(volatility));
182 return result;
183 }
184 };
185
186}
187
188
189void CapFlooredCouponTest::testLargeRates() {
190
191 BOOST_TEST_MESSAGE("Testing degenerate collared coupon...");
192
193 using namespace capfloored_coupon_test;
194
195 CommonVars vars;
196
197 /* A vanilla floating leg and a capped floating leg with strike
198 equal to 100 and floor equal to 0 must have (about) the same NPV
199 (depending on variance: option expiry and volatility)
200 */
201
202 std::vector<Rate> caps(vars.length,100.0);
203 std::vector<Rate> floors(vars.length,0.0);
204 Real tolerance = 1e-10;
205
206 // fixed leg with zero rate
207 Leg fixedLeg =
208 vars.makeFixedLeg(startDate: vars.startDate,length: vars.length);
209 Leg floatLeg =
210 vars.makeFloatingLeg(startDate: vars.startDate,length: vars.length);
211 Leg collaredLeg =
212 vars.makeCapFlooredLeg(startDate: vars.startDate,length: vars.length,
213 caps,floors,volatility: vars.volatility);
214
215 ext::shared_ptr<PricingEngine> engine(
216 new DiscountingSwapEngine(vars.termStructure));
217 Swap vanillaLeg(fixedLeg,floatLeg);
218 Swap collarLeg(fixedLeg,collaredLeg);
219 vanillaLeg.setPricingEngine(engine);
220 collarLeg.setPricingEngine(engine);
221
222 if (std::abs(x: vanillaLeg.NPV()-collarLeg.NPV())>tolerance) {
223 BOOST_ERROR("Length: " << vars.length << " y" << "\n" <<
224 "Volatility: " << vars.volatility*100 << "%\n" <<
225 "Notional: " << vars.nominal << "\n" <<
226 "Vanilla floating leg NPV: " << vanillaLeg.NPV()
227 << "\n" <<
228 "Collared floating leg NPV (strikes 0 and 100): "
229 << collarLeg.NPV()
230 << "\n" <<
231 "Diff: " << std::abs(vanillaLeg.NPV()-collarLeg.NPV()));
232 }
233}
234
235void CapFlooredCouponTest::testDecomposition() {
236
237 BOOST_TEST_MESSAGE("Testing collared coupon against its decomposition...");
238
239 using namespace capfloored_coupon_test;
240
241 CommonVars vars;
242
243 Real tolerance = 1e-12;
244 Real npvVanilla,npvCappedLeg,npvFlooredLeg,npvCollaredLeg,npvCap,npvFloor,npvCollar;
245 Real error;
246 Rate floorstrike = 0.05;
247 Rate capstrike = 0.10;
248 std::vector<Rate> caps(vars.length,capstrike);
249 std::vector<Rate> caps0 = std::vector<Rate>();
250 std::vector<Rate> floors(vars.length,floorstrike);
251 std::vector<Rate> floors0 = std::vector<Rate>();
252 Rate gearing_p = Rate(0.5);
253 auto spread_p = Spread(0.002);
254 Rate gearing_n = Rate(-1.5);
255 auto spread_n = Spread(0.12);
256 // fixed leg with zero rate
257 Leg fixedLeg =
258 vars.makeFixedLeg(startDate: vars.startDate,length: vars.length);
259 // floating leg with gearing=1 and spread=0
260 Leg floatLeg =
261 vars.makeFloatingLeg(startDate: vars.startDate,length: vars.length);
262 // floating leg with positive gearing (gearing_p) and spread<>0
263 Leg floatLeg_p =
264 vars.makeFloatingLeg(startDate: vars.startDate,length: vars.length,gearing: gearing_p,spread: spread_p);
265 // floating leg with negative gearing (gearing_n) and spread<>0
266 Leg floatLeg_n =
267 vars.makeFloatingLeg(startDate: vars.startDate,length: vars.length,gearing: gearing_n,spread: spread_n);
268 // Swap with null fixed leg and floating leg with gearing=1 and spread=0
269 Swap vanillaLeg(fixedLeg,floatLeg);
270 // Swap with null fixed leg and floating leg with positive gearing and spread<>0
271 Swap vanillaLeg_p(fixedLeg,floatLeg_p);
272 // Swap with null fixed leg and floating leg with negative gearing and spread<>0
273 Swap vanillaLeg_n(fixedLeg,floatLeg_n);
274
275 ext::shared_ptr<PricingEngine> engine(
276 new DiscountingSwapEngine(vars.termStructure));
277 vanillaLeg.setPricingEngine(engine);
278 vanillaLeg_p.setPricingEngine(engine);
279 vanillaLeg_n.setPricingEngine(engine);
280
281 /* CAPPED coupon - Decomposition of payoff
282 Payoff = Nom * Min(rate,strike) * accrualperiod =
283 = Nom * [rate + Min(0,strike-rate)] * accrualperiod =
284 = Nom * rate * accrualperiod - Nom * Max(rate-strike,0) * accrualperiod =
285 = VanillaFloatingLeg - Call
286 */
287
288 // Case gearing = 1 and spread = 0
289 Leg cappedLeg =
290 vars.makeCapFlooredLeg(startDate: vars.startDate,length: vars.length,
291 caps,floors: floors0,volatility: vars.volatility);
292 Swap capLeg(fixedLeg,cappedLeg);
293 capLeg.setPricingEngine(engine);
294 Cap cap(floatLeg, std::vector<Rate>(1, capstrike));
295 cap.setPricingEngine(vars.makeEngine(volatility: vars.volatility));
296 npvVanilla = vanillaLeg.NPV();
297 npvCappedLeg = capLeg.NPV();
298 npvCap = cap.NPV();
299 error = std::abs(x: npvCappedLeg - (npvVanilla-npvCap));
300 if (error>tolerance) {
301 BOOST_ERROR("\nCapped Leg: gearing=1, spread=0%, strike=" << capstrike*100 <<
302 "%\n" <<
303 " Capped Floating Leg NPV: " << npvCappedLeg << "\n" <<
304 " Floating Leg NPV - Cap NPV: " << npvVanilla - npvCap << "\n" <<
305 " Diff: " << error );
306 }
307
308 /* gearing = 1 and spread = 0
309 FLOORED coupon - Decomposition of payoff
310 Payoff = Nom * Max(rate,strike) * accrualperiod =
311 = Nom * [rate + Max(0,strike-rate)] * accrualperiod =
312 = Nom * rate * accrualperiod + Nom * Max(strike-rate,0) * accrualperiod =
313 = VanillaFloatingLeg + Put
314 */
315
316 Leg flooredLeg =
317 vars.makeCapFlooredLeg(startDate: vars.startDate,length: vars.length,
318 caps: caps0,floors,volatility: vars.volatility);
319 Swap floorLeg(fixedLeg,flooredLeg);
320 floorLeg.setPricingEngine(engine);
321 Floor floor(floatLeg, std::vector<Rate>(1, floorstrike));
322 floor.setPricingEngine(vars.makeEngine(volatility: vars.volatility));
323 npvFlooredLeg = floorLeg.NPV();
324 npvFloor = floor.NPV();
325 error = std::abs(x: npvFlooredLeg-(npvVanilla + npvFloor));
326 if (error>tolerance) {
327 BOOST_ERROR("Floored Leg: gearing=1, spread=0%, strike=" << floorstrike *100 <<
328 "%\n" <<
329 " Floored Floating Leg NPV: " << npvFlooredLeg << "\n" <<
330 " Floating Leg NPV + Floor NPV: " << npvVanilla + npvFloor << "\n" <<
331 " Diff: " << error );
332 }
333
334 /* gearing = 1 and spread = 0
335 COLLARED coupon - Decomposition of payoff
336 Payoff = Nom * Min(strikem,Max(rate,strikeM)) * accrualperiod =
337 = VanillaFloatingLeg - Collar
338 */
339
340 Leg collaredLeg =
341 vars.makeCapFlooredLeg(startDate: vars.startDate,length: vars.length,
342 caps,floors,volatility: vars.volatility);
343 Swap collarLeg(fixedLeg,collaredLeg);
344 collarLeg.setPricingEngine(engine);
345 Collar collar(floatLeg,
346 std::vector<Rate>(1, capstrike),
347 std::vector<Rate>(1, floorstrike));
348 collar.setPricingEngine(vars.makeEngine(volatility: vars.volatility));
349 npvCollaredLeg = collarLeg.NPV();
350 npvCollar = collar.NPV();
351 error = std::abs(x: npvCollaredLeg -(npvVanilla - npvCollar));
352 if (error>tolerance) {
353 BOOST_ERROR("\nCollared Leg: gearing=1, spread=0%, strike=" <<
354 floorstrike*100 << "% and " << capstrike*100 << "%\n" <<
355 " Collared Floating Leg NPV: " << npvCollaredLeg << "\n" <<
356 " Floating Leg NPV - Collar NPV: " << npvVanilla - npvCollar << "\n" <<
357 " Diff: " << error );
358 }
359
360 /* gearing = a and spread = b
361 CAPPED coupon - Decomposition of payoff
362 Payoff
363 = Nom * Min(a*rate+b,strike) * accrualperiod =
364 = Nom * [a*rate+b + Min(0,strike-a*rate-b)] * accrualperiod =
365 = Nom * a*rate+b * accrualperiod + Nom * Min(strike-b-a*rate,0) * accrualperiod
366 --> If a>0 (assuming positive effective strike):
367 Payoff = VanillaFloatingLeg - Call(a*rate+b,strike)
368 --> If a<0 (assuming positive effective strike):
369 Payoff = VanillaFloatingLeg + Nom * Min(strike-b+|a|*rate+,0) * accrualperiod =
370 = VanillaFloatingLeg + Put(|a|*rate+b,strike)
371 */
372
373 // Positive gearing
374 Leg cappedLeg_p =
375 vars.makeCapFlooredLeg(startDate: vars.startDate,length: vars.length,caps,floors: floors0,
376 volatility: vars.volatility,gearing: gearing_p,spread: spread_p);
377 Swap capLeg_p(fixedLeg,cappedLeg_p);
378 capLeg_p.setPricingEngine(engine);
379 Cap cap_p(floatLeg_p,std::vector<Rate>(1,capstrike));
380 cap_p.setPricingEngine(vars.makeEngine(volatility: vars.volatility));
381 npvVanilla = vanillaLeg_p.NPV();
382 npvCappedLeg = capLeg_p.NPV();
383 npvCap = cap_p.NPV();
384 error = std::abs(x: npvCappedLeg - (npvVanilla-npvCap));
385 if (error>tolerance) {
386 BOOST_ERROR("\nCapped Leg: gearing=" << gearing_p << ", " <<
387 "spread= " << spread_p *100 <<
388 "%, strike=" << capstrike*100 << "%, " <<
389 "effective strike= " << (capstrike-spread_p)/gearing_p*100 <<
390 "%\n" <<
391 " Capped Floating Leg NPV: " << npvCappedLeg << "\n" <<
392 " Vanilla Leg NPV: " << npvVanilla << "\n" <<
393 " Cap NPV: " << npvCap << "\n" <<
394 " Floating Leg NPV - Cap NPV: " << npvVanilla - npvCap << "\n" <<
395 " Diff: " << error );
396 }
397
398 // Negative gearing
399 Leg cappedLeg_n =
400 vars.makeCapFlooredLeg(startDate: vars.startDate,length: vars.length,caps,floors: floors0,
401 volatility: vars.volatility,gearing: gearing_n,spread: spread_n);
402 Swap capLeg_n(fixedLeg,cappedLeg_n);
403 capLeg_n.setPricingEngine(engine);
404 Floor floor_n(floatLeg,std::vector<Rate>(1,(capstrike-spread_n)/gearing_n));
405 floor_n.setPricingEngine(vars.makeEngine(volatility: vars.volatility));
406 npvVanilla = vanillaLeg_n.NPV();
407 npvCappedLeg = capLeg_n.NPV();
408 npvFloor = floor_n.NPV();
409 error = std::abs(x: npvCappedLeg - (npvVanilla+ gearing_n*npvFloor));
410 if (error>tolerance) {
411 BOOST_ERROR("\nCapped Leg: gearing=" << gearing_n << ", " <<
412 "spread= " << spread_n *100 <<
413 "%, strike=" << capstrike*100 << "%, " <<
414 "effective strike= " << (capstrike-spread_n)/gearing_n*100 <<
415 "%\n" <<
416 " Capped Floating Leg NPV: " << npvCappedLeg << "\n" <<
417 " npv Vanilla: " << npvVanilla << "\n" <<
418 " npvFloor: " << npvFloor << "\n" <<
419 " Floating Leg NPV - Cap NPV: " << npvVanilla + gearing_n*npvFloor << "\n" <<
420 " Diff: " << error );
421 }
422
423 /* gearing = a and spread = b
424 FLOORED coupon - Decomposition of payoff
425 Payoff
426 = Nom * Max(a*rate+b,strike) * accrualperiod =
427 = Nom * [a*rate+b + Max(0,strike-a*rate-b)] * accrualperiod =
428 = Nom * a*rate+b * accrualperiod + Nom * Max(strike-b-a*rate,0) * accrualperiod
429 --> If a>0 (assuming positive effective strike):
430 Payoff = VanillaFloatingLeg + Put(a*rate+b,strike)
431 --> If a<0 (assuming positive effective strike):
432 Payoff = VanillaFloatingLeg + Nom * Max(strike-b+|a|*rate+,0) * accrualperiod =
433 = VanillaFloatingLeg - Call(|a|*rate+b,strike)
434 */
435
436 // Positive gearing
437 Leg flooredLeg_p1 =
438 vars.makeCapFlooredLeg(startDate: vars.startDate,length: vars.length,caps: caps0,floors,
439 volatility: vars.volatility,gearing: gearing_p,spread: spread_p);
440 Swap floorLeg_p1(fixedLeg,flooredLeg_p1);
441 floorLeg_p1.setPricingEngine(engine);
442 Floor floor_p1(floatLeg_p,std::vector<Rate>(1,floorstrike));
443 floor_p1.setPricingEngine(vars.makeEngine(volatility: vars.volatility));
444 npvVanilla = vanillaLeg_p.NPV();
445 npvFlooredLeg = floorLeg_p1.NPV();
446 npvFloor = floor_p1.NPV();
447 error = std::abs(x: npvFlooredLeg - (npvVanilla+npvFloor));
448 if (error>tolerance) {
449 BOOST_ERROR("\nFloored Leg: gearing=" << gearing_p << ", "
450 << "spread= " << spread_p *100<< "%, strike=" << floorstrike *100 << "%, "
451 << "effective strike= " << (floorstrike-spread_p)/gearing_p*100
452 << "%\n" <<
453 " Floored Floating Leg NPV: " << npvFlooredLeg
454 << "\n" <<
455 " Floating Leg NPV + Floor NPV: " << npvVanilla + npvFloor
456 << "\n" <<
457 " Diff: " << error );
458 }
459 // Negative gearing
460 Leg flooredLeg_n =
461 vars.makeCapFlooredLeg(startDate: vars.startDate,length: vars.length,caps: caps0,floors,
462 volatility: vars.volatility,gearing: gearing_n,spread: spread_n);
463 Swap floorLeg_n(fixedLeg,flooredLeg_n);
464 floorLeg_n.setPricingEngine(engine);
465 Cap cap_n(floatLeg,std::vector<Rate>(1,(floorstrike-spread_n)/gearing_n));
466 cap_n.setPricingEngine(vars.makeEngine(volatility: vars.volatility));
467 npvVanilla = vanillaLeg_n.NPV();
468 npvFlooredLeg = floorLeg_n.NPV();
469 npvCap = cap_n.NPV();
470 error = std::abs(x: npvFlooredLeg - (npvVanilla - gearing_n*npvCap));
471 if (error>tolerance) {
472 BOOST_ERROR("\nCapped Leg: gearing=" << gearing_n << ", " <<
473 "spread= " << spread_n *100 <<
474 "%, strike=" << floorstrike*100 << "%, " <<
475 "effective strike= " << (floorstrike-spread_n)/gearing_n*100 <<
476 "%\n" <<
477 " Capped Floating Leg NPV: " << npvFlooredLeg << "\n" <<
478 " Floating Leg NPV - Cap NPV: " << npvVanilla - gearing_n*npvCap << "\n" <<
479 " Diff: " << error );
480 }
481 /* gearing = a and spread = b
482 COLLARED coupon - Decomposition of payoff
483 Payoff = Nom * Min(caprate,Max(a*rate+b,floorrate)) * accrualperiod
484 --> If a>0 (assuming positive effective strike):
485 Payoff = VanillaFloatingLeg - Collar(a*rate+b, floorrate, caprate)
486 --> If a<0 (assuming positive effective strike):
487 Payoff = VanillaFloatingLeg + Collar(|a|*rate+b, caprate, floorrate)
488 */
489 // Positive gearing
490 Leg collaredLeg_p =
491 vars.makeCapFlooredLeg(startDate: vars.startDate,length: vars.length,caps,floors,
492 volatility: vars.volatility,gearing: gearing_p,spread: spread_p);
493 Swap collarLeg_p1(fixedLeg,collaredLeg_p);
494 collarLeg_p1.setPricingEngine(engine);
495 Collar collar_p(floatLeg_p,
496 std::vector<Rate>(1,capstrike),
497 std::vector<Rate>(1,floorstrike));
498 collar_p.setPricingEngine(vars.makeEngine(volatility: vars.volatility));
499 npvVanilla = vanillaLeg_p.NPV();
500 npvCollaredLeg = collarLeg_p1.NPV();
501 npvCollar = collar_p.NPV();
502 error = std::abs(x: npvCollaredLeg - (npvVanilla - npvCollar));
503 if (error>tolerance) {
504 BOOST_ERROR("\nCollared Leg: gearing=" << gearing_p << ", "
505 << "spread= " << spread_p*100 << "%, strike="
506 << floorstrike*100 << "% and " << capstrike*100
507 << "%, "
508 << "effective strike=" << (floorstrike-spread_p)/gearing_p*100
509 << "% and " << (capstrike-spread_p)/gearing_p*100
510 << "%\n" <<
511 " Collared Floating Leg NPV: " << npvCollaredLeg
512 << "\n" <<
513 " Floating Leg NPV - Collar NPV: " << npvVanilla - npvCollar
514 << "\n" <<
515 " Diff: " << error );
516 }
517 // Negative gearing
518 Leg collaredLeg_n =
519 vars.makeCapFlooredLeg(startDate: vars.startDate,length: vars.length,caps,floors,
520 volatility: vars.volatility,gearing: gearing_n,spread: spread_n);
521 Swap collarLeg_n1(fixedLeg,collaredLeg_n);
522 collarLeg_n1.setPricingEngine(engine);
523 Collar collar_n(floatLeg,
524 std::vector<Rate>(1,(floorstrike-spread_n)/gearing_n),
525 std::vector<Rate>(1,(capstrike-spread_n)/gearing_n));
526 collar_n.setPricingEngine(vars.makeEngine(volatility: vars.volatility));
527 npvVanilla = vanillaLeg_n.NPV();
528 npvCollaredLeg = collarLeg_n1.NPV();
529 npvCollar = collar_n.NPV();
530 error = std::abs(x: npvCollaredLeg - (npvVanilla - gearing_n*npvCollar));
531 if (error>tolerance) {
532 BOOST_ERROR("\nCollared Leg: gearing=" << gearing_n << ", "
533 << "spread= " << spread_n*100 << "%, strike="
534 << floorstrike*100 << "% and " << capstrike*100
535 << "%, "
536 << "effective strike=" << (floorstrike-spread_n)/gearing_n*100
537 << "% and " << (capstrike-spread_n)/gearing_n*100
538 << "%\n" <<
539 " Collared Floating Leg NPV: " << npvCollaredLeg
540 << "\n" <<
541 " Floating Leg NPV - Collar NPV: " << npvVanilla - gearing_n*npvCollar
542 << "\n" <<
543 " Diff: " << error );
544 }
545}
546
547test_suite* CapFlooredCouponTest::suite() {
548 auto* suite = BOOST_TEST_SUITE("Capped and floored coupon tests");
549 suite->add(QUANTLIB_TEST_CASE(&CapFlooredCouponTest::testLargeRates));
550 suite->add(QUANTLIB_TEST_CASE(&CapFlooredCouponTest::testDecomposition));
551 return suite;
552}
553
554

source code of quantlib/test-suite/capflooredcoupon.cpp