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
40 using namespace QuantLib;
41 using namespace boost::unit_test_framework;
42
43 namespace 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 // cleanup
62 SavedSettings backup;
63
64 // setup
CommonVarscapfloored_coupon_test::CommonVars65 CommonVars() {
66 length = 20; //years
67 volatility = 0.20;
68 nominal = 100.;
69 nominals = std::vector<Real>(length,nominal);
70 frequency = Annual;
71 index = ext::shared_ptr<IborIndex>(new Euribor1Y(termStructure));
72 calendar = index->fixingCalendar();
73 convention = ModifiedFollowing;
74 today = calendar.adjust(Date::todaysDate());
75 Settings::instance().evaluationDate() = today;
76 settlementDays = 2;
77 fixingDays = 2;
78 settlement = calendar.advance(today,settlementDays,Days);
79 startDate = settlement;
80 termStructure.linkTo(flatRate(settlement,0.05,
81 ActualActual(ActualActual::ISDA)));
82 }
83
84 // utilities
makeFixedLegcapfloored_coupon_test::CommonVars85 Leg makeFixedLeg(const Date& startDate, Integer length) const {
86
87 Date endDate = calendar.advance(startDate, length, Years,
88 convention);
89 Schedule schedule(startDate, endDate, Period(frequency), calendar,
90 convention, convention,
91 DateGeneration::Forward, false);
92 std::vector<Rate> coupons(length, 0.0);
93 return FixedRateLeg(schedule)
94 .withNotionals(nominals)
95 .withCouponRates(coupons, Thirty360());
96 }
97
makeFloatingLegcapfloored_coupon_test::CommonVars98 Leg makeFloatingLeg(const Date& startDate,
99 Integer length,
100 const Rate gearing = 1.0,
101 const Rate spread = 0.0) const {
102
103 Date endDate = calendar.advance(startDate,length,Years,convention);
104 Schedule schedule(startDate,endDate,Period(frequency),calendar,
105 convention,convention,
106 DateGeneration::Forward,false);
107 std::vector<Real> gearingVector(length, gearing);
108 std::vector<Spread> spreadVector(length, spread);
109 return IborLeg(schedule, index)
110 .withNotionals(nominals)
111 .withPaymentDayCounter(index->dayCounter())
112 .withPaymentAdjustment(convention)
113 .withFixingDays(fixingDays)
114 .withGearings(gearingVector)
115 .withSpreads(spreadVector);
116 }
117
makeCapFlooredLegcapfloored_coupon_test::CommonVars118 Leg makeCapFlooredLeg(const Date& startDate,
119 Integer length,
120 const std::vector<Rate>& caps,
121 const std::vector<Rate>& floors,
122 Volatility volatility,
123 const Rate gearing = 1.0,
124 const Rate spread = 0.0) const {
125
126 Date endDate = calendar.advance(startDate,length,Years,convention);
127 Schedule schedule(startDate,endDate,Period(frequency),calendar,
128 convention,convention,
129 DateGeneration::Forward,false);
130 Handle<OptionletVolatilityStructure> vol(
131 ext::shared_ptr<OptionletVolatilityStructure>(new
132 ConstantOptionletVolatility(0, calendar, Following,
133 volatility,Actual365Fixed())));
134
135 ext::shared_ptr<IborCouponPricer> pricer(new
136 BlackIborCouponPricer(vol));
137 std::vector<Rate> gearingVector(length, gearing);
138 std::vector<Spread> spreadVector(length, spread);
139
140 Leg iborLeg = IborLeg(schedule, index)
141 .withNotionals(nominals)
142 .withPaymentDayCounter(index->dayCounter())
143 .withPaymentAdjustment(convention)
144 .withFixingDays(fixingDays)
145 .withGearings(gearingVector)
146 .withSpreads(spreadVector)
147 .withCaps(caps)
148 .withFloors(floors);
149 setCouponPricer(iborLeg, pricer);
150 return iborLeg;
151 }
152
makeEnginecapfloored_coupon_test::CommonVars153 ext::shared_ptr<PricingEngine> makeEngine(Volatility volatility) const {
154 Handle<Quote> vol(ext::shared_ptr<Quote>(
155 new SimpleQuote(volatility)));
156 return ext::shared_ptr<PricingEngine>(
157 new BlackCapFloorEngine(termStructure, vol));
158 }
159
makeCapFloorcapfloored_coupon_test::CommonVars160 ext::shared_ptr<CapFloor> makeCapFloor(CapFloor::Type type,
161 const Leg& leg,
162 Rate capStrike,
163 Rate floorStrike,
164 Volatility volatility) const {
165 ext::shared_ptr<CapFloor> result;
166 switch (type) {
167 case CapFloor::Cap:
168 result = ext::shared_ptr<CapFloor>(
169 new Cap(leg, std::vector<Rate>(1, capStrike)));
170 break;
171 case CapFloor::Floor:
172 result = ext::shared_ptr<CapFloor>(
173 new Floor(leg, std::vector<Rate>(1, floorStrike)));
174 break;
175 case CapFloor::Collar:
176 result = ext::shared_ptr<CapFloor>(
177 new Collar(leg,
178 std::vector<Rate>(1, capStrike),
179 std::vector<Rate>(1, floorStrike)));
180 break;
181 default:
182 QL_FAIL("unknown cap/floor type");
183 }
184 result->setPricingEngine(makeEngine(volatility));
185 return result;
186 }
187 };
188
189 }
190
191
testLargeRates()192 void CapFlooredCouponTest::testLargeRates() {
193
194 BOOST_TEST_MESSAGE("Testing degenerate collared coupon...");
195
196 using namespace capfloored_coupon_test;
197
198 CommonVars vars;
199
200 /* A vanilla floating leg and a capped floating leg with strike
201 equal to 100 and floor equal to 0 must have (about) the same NPV
202 (depending on variance: option expiry and volatility)
203 */
204
205 std::vector<Rate> caps(vars.length,100.0);
206 std::vector<Rate> floors(vars.length,0.0);
207 Real tolerance = 1e-10;
208
209 // fixed leg with zero rate
210 Leg fixedLeg =
211 vars.makeFixedLeg(vars.startDate,vars.length);
212 Leg floatLeg =
213 vars.makeFloatingLeg(vars.startDate,vars.length);
214 Leg collaredLeg =
215 vars.makeCapFlooredLeg(vars.startDate,vars.length,
216 caps,floors,vars.volatility);
217
218 ext::shared_ptr<PricingEngine> engine(
219 new DiscountingSwapEngine(vars.termStructure));
220 Swap vanillaLeg(fixedLeg,floatLeg);
221 Swap collarLeg(fixedLeg,collaredLeg);
222 vanillaLeg.setPricingEngine(engine);
223 collarLeg.setPricingEngine(engine);
224
225 if (std::abs(vanillaLeg.NPV()-collarLeg.NPV())>tolerance) {
226 BOOST_ERROR("Length: " << vars.length << " y" << "\n" <<
227 "Volatility: " << vars.volatility*100 << "%\n" <<
228 "Notional: " << vars.nominal << "\n" <<
229 "Vanilla floating leg NPV: " << vanillaLeg.NPV()
230 << "\n" <<
231 "Collared floating leg NPV (strikes 0 and 100): "
232 << collarLeg.NPV()
233 << "\n" <<
234 "Diff: " << std::abs(vanillaLeg.NPV()-collarLeg.NPV()));
235 }
236 }
237
testDecomposition()238 void CapFlooredCouponTest::testDecomposition() {
239
240 BOOST_TEST_MESSAGE("Testing collared coupon against its decomposition...");
241
242 using namespace capfloored_coupon_test;
243
244 CommonVars vars;
245
246 Real tolerance = 1e-12;
247 Real npvVanilla,npvCappedLeg,npvFlooredLeg,npvCollaredLeg,npvCap,npvFloor,npvCollar;
248 Real error;
249 Rate floorstrike = 0.05;
250 Rate capstrike = 0.10;
251 std::vector<Rate> caps(vars.length,capstrike);
252 std::vector<Rate> caps0 = std::vector<Rate>();
253 std::vector<Rate> floors(vars.length,floorstrike);
254 std::vector<Rate> floors0 = std::vector<Rate>();
255 Rate gearing_p = Rate(0.5);
256 Spread spread_p = Spread(0.002);
257 Rate gearing_n = Rate(-1.5);
258 Spread spread_n = Spread(0.12);
259 // fixed leg with zero rate
260 Leg fixedLeg =
261 vars.makeFixedLeg(vars.startDate,vars.length);
262 // floating leg with gearing=1 and spread=0
263 Leg floatLeg =
264 vars.makeFloatingLeg(vars.startDate,vars.length);
265 // floating leg with positive gearing (gearing_p) and spread<>0
266 Leg floatLeg_p =
267 vars.makeFloatingLeg(vars.startDate,vars.length,gearing_p,spread_p);
268 // floating leg with negative gearing (gearing_n) and spread<>0
269 Leg floatLeg_n =
270 vars.makeFloatingLeg(vars.startDate,vars.length,gearing_n,spread_n);
271 // Swap with null fixed leg and floating leg with gearing=1 and spread=0
272 Swap vanillaLeg(fixedLeg,floatLeg);
273 // Swap with null fixed leg and floating leg with positive gearing and spread<>0
274 Swap vanillaLeg_p(fixedLeg,floatLeg_p);
275 // Swap with null fixed leg and floating leg with negative gearing and spread<>0
276 Swap vanillaLeg_n(fixedLeg,floatLeg_n);
277
278 ext::shared_ptr<PricingEngine> engine(
279 new DiscountingSwapEngine(vars.termStructure));
280 vanillaLeg.setPricingEngine(engine);
281 vanillaLeg_p.setPricingEngine(engine);
282 vanillaLeg_n.setPricingEngine(engine);
283
284 /* CAPPED coupon - Decomposition of payoff
285 Payoff = Nom * Min(rate,strike) * accrualperiod =
286 = Nom * [rate + Min(0,strike-rate)] * accrualperiod =
287 = Nom * rate * accrualperiod - Nom * Max(rate-strike,0) * accrualperiod =
288 = VanillaFloatingLeg - Call
289 */
290
291 // Case gearing = 1 and spread = 0
292 Leg cappedLeg =
293 vars.makeCapFlooredLeg(vars.startDate,vars.length,
294 caps,floors0,vars.volatility);
295 Swap capLeg(fixedLeg,cappedLeg);
296 capLeg.setPricingEngine(engine);
297 Cap cap(floatLeg, std::vector<Rate>(1, capstrike));
298 cap.setPricingEngine(vars.makeEngine(vars.volatility));
299 npvVanilla = vanillaLeg.NPV();
300 npvCappedLeg = capLeg.NPV();
301 npvCap = cap.NPV();
302 error = std::abs(npvCappedLeg - (npvVanilla-npvCap));
303 if (error>tolerance) {
304 BOOST_ERROR("\nCapped Leg: gearing=1, spread=0%, strike=" << capstrike*100 <<
305 "%\n" <<
306 " Capped Floating Leg NPV: " << npvCappedLeg << "\n" <<
307 " Floating Leg NPV - Cap NPV: " << npvVanilla - npvCap << "\n" <<
308 " Diff: " << error );
309 }
310
311 /* gearing = 1 and spread = 0
312 FLOORED coupon - Decomposition of payoff
313 Payoff = Nom * Max(rate,strike) * accrualperiod =
314 = Nom * [rate + Max(0,strike-rate)] * accrualperiod =
315 = Nom * rate * accrualperiod + Nom * Max(strike-rate,0) * accrualperiod =
316 = VanillaFloatingLeg + Put
317 */
318
319 Leg flooredLeg =
320 vars.makeCapFlooredLeg(vars.startDate,vars.length,
321 caps0,floors,vars.volatility);
322 Swap floorLeg(fixedLeg,flooredLeg);
323 floorLeg.setPricingEngine(engine);
324 Floor floor(floatLeg, std::vector<Rate>(1, floorstrike));
325 floor.setPricingEngine(vars.makeEngine(vars.volatility));
326 npvFlooredLeg = floorLeg.NPV();
327 npvFloor = floor.NPV();
328 error = std::abs(npvFlooredLeg-(npvVanilla + npvFloor));
329 if (error>tolerance) {
330 BOOST_ERROR("Floored Leg: gearing=1, spread=0%, strike=" << floorstrike *100 <<
331 "%\n" <<
332 " Floored Floating Leg NPV: " << npvFlooredLeg << "\n" <<
333 " Floating Leg NPV + Floor NPV: " << npvVanilla + npvFloor << "\n" <<
334 " Diff: " << error );
335 }
336
337 /* gearing = 1 and spread = 0
338 COLLARED coupon - Decomposition of payoff
339 Payoff = Nom * Min(strikem,Max(rate,strikeM)) * accrualperiod =
340 = VanillaFloatingLeg - Collar
341 */
342
343 Leg collaredLeg =
344 vars.makeCapFlooredLeg(vars.startDate,vars.length,
345 caps,floors,vars.volatility);
346 Swap collarLeg(fixedLeg,collaredLeg);
347 collarLeg.setPricingEngine(engine);
348 Collar collar(floatLeg,
349 std::vector<Rate>(1, capstrike),
350 std::vector<Rate>(1, floorstrike));
351 collar.setPricingEngine(vars.makeEngine(vars.volatility));
352 npvCollaredLeg = collarLeg.NPV();
353 npvCollar = collar.NPV();
354 error = std::abs(npvCollaredLeg -(npvVanilla - npvCollar));
355 if (error>tolerance) {
356 BOOST_ERROR("\nCollared Leg: gearing=1, spread=0%, strike=" <<
357 floorstrike*100 << "% and " << capstrike*100 << "%\n" <<
358 " Collared Floating Leg NPV: " << npvCollaredLeg << "\n" <<
359 " Floating Leg NPV - Collar NPV: " << npvVanilla - npvCollar << "\n" <<
360 " Diff: " << error );
361 }
362
363 /* gearing = a and spread = b
364 CAPPED coupon - Decomposition of payoff
365 Payoff
366 = Nom * Min(a*rate+b,strike) * accrualperiod =
367 = Nom * [a*rate+b + Min(0,strike-a*rate-b)] * accrualperiod =
368 = Nom * a*rate+b * accrualperiod + Nom * Min(strike-b-a*rate,0) * accrualperiod
369 --> If a>0 (assuming positive effective strike):
370 Payoff = VanillaFloatingLeg - Call(a*rate+b,strike)
371 --> If a<0 (assuming positive effective strike):
372 Payoff = VanillaFloatingLeg + Nom * Min(strike-b+|a|*rate+,0) * accrualperiod =
373 = VanillaFloatingLeg + Put(|a|*rate+b,strike)
374 */
375
376 // Positive gearing
377 Leg cappedLeg_p =
378 vars.makeCapFlooredLeg(vars.startDate,vars.length,caps,floors0,
379 vars.volatility,gearing_p,spread_p);
380 Swap capLeg_p(fixedLeg,cappedLeg_p);
381 capLeg_p.setPricingEngine(engine);
382 Cap cap_p(floatLeg_p,std::vector<Rate>(1,capstrike));
383 cap_p.setPricingEngine(vars.makeEngine(vars.volatility));
384 npvVanilla = vanillaLeg_p.NPV();
385 npvCappedLeg = capLeg_p.NPV();
386 npvCap = cap_p.NPV();
387 error = std::abs(npvCappedLeg - (npvVanilla-npvCap));
388 if (error>tolerance) {
389 BOOST_ERROR("\nCapped Leg: gearing=" << gearing_p << ", " <<
390 "spread= " << spread_p *100 <<
391 "%, strike=" << capstrike*100 << "%, " <<
392 "effective strike= " << (capstrike-spread_p)/gearing_p*100 <<
393 "%\n" <<
394 " Capped Floating Leg NPV: " << npvCappedLeg << "\n" <<
395 " Vanilla Leg NPV: " << npvVanilla << "\n" <<
396 " Cap NPV: " << npvCap << "\n" <<
397 " Floating Leg NPV - Cap NPV: " << npvVanilla - npvCap << "\n" <<
398 " Diff: " << error );
399 }
400
401 // Negative gearing
402 Leg cappedLeg_n =
403 vars.makeCapFlooredLeg(vars.startDate,vars.length,caps,floors0,
404 vars.volatility,gearing_n,spread_n);
405 Swap capLeg_n(fixedLeg,cappedLeg_n);
406 capLeg_n.setPricingEngine(engine);
407 Floor floor_n(floatLeg,std::vector<Rate>(1,(capstrike-spread_n)/gearing_n));
408 floor_n.setPricingEngine(vars.makeEngine(vars.volatility));
409 npvVanilla = vanillaLeg_n.NPV();
410 npvCappedLeg = capLeg_n.NPV();
411 npvFloor = floor_n.NPV();
412 error = std::abs(npvCappedLeg - (npvVanilla+ gearing_n*npvFloor));
413 if (error>tolerance) {
414 BOOST_ERROR("\nCapped Leg: gearing=" << gearing_n << ", " <<
415 "spread= " << spread_n *100 <<
416 "%, strike=" << capstrike*100 << "%, " <<
417 "effective strike= " << (capstrike-spread_n)/gearing_n*100 <<
418 "%\n" <<
419 " Capped Floating Leg NPV: " << npvCappedLeg << "\n" <<
420 " npv Vanilla: " << npvVanilla << "\n" <<
421 " npvFloor: " << npvFloor << "\n" <<
422 " Floating Leg NPV - Cap NPV: " << npvVanilla + gearing_n*npvFloor << "\n" <<
423 " Diff: " << error );
424 }
425
426 /* gearing = a and spread = b
427 FLOORED coupon - Decomposition of payoff
428 Payoff
429 = Nom * Max(a*rate+b,strike) * accrualperiod =
430 = Nom * [a*rate+b + Max(0,strike-a*rate-b)] * accrualperiod =
431 = Nom * a*rate+b * accrualperiod + Nom * Max(strike-b-a*rate,0) * accrualperiod
432 --> If a>0 (assuming positive effective strike):
433 Payoff = VanillaFloatingLeg + Put(a*rate+b,strike)
434 --> If a<0 (assuming positive effective strike):
435 Payoff = VanillaFloatingLeg + Nom * Max(strike-b+|a|*rate+,0) * accrualperiod =
436 = VanillaFloatingLeg - Call(|a|*rate+b,strike)
437 */
438
439 // Positive gearing
440 Leg flooredLeg_p1 =
441 vars.makeCapFlooredLeg(vars.startDate,vars.length,caps0,floors,
442 vars.volatility,gearing_p,spread_p);
443 Swap floorLeg_p1(fixedLeg,flooredLeg_p1);
444 floorLeg_p1.setPricingEngine(engine);
445 Floor floor_p1(floatLeg_p,std::vector<Rate>(1,floorstrike));
446 floor_p1.setPricingEngine(vars.makeEngine(vars.volatility));
447 npvVanilla = vanillaLeg_p.NPV();
448 npvFlooredLeg = floorLeg_p1.NPV();
449 npvFloor = floor_p1.NPV();
450 error = std::abs(npvFlooredLeg - (npvVanilla+npvFloor));
451 if (error>tolerance) {
452 BOOST_ERROR("\nFloored Leg: gearing=" << gearing_p << ", "
453 << "spread= " << spread_p *100<< "%, strike=" << floorstrike *100 << "%, "
454 << "effective strike= " << (floorstrike-spread_p)/gearing_p*100
455 << "%\n" <<
456 " Floored Floating Leg NPV: " << npvFlooredLeg
457 << "\n" <<
458 " Floating Leg NPV + Floor NPV: " << npvVanilla + npvFloor
459 << "\n" <<
460 " Diff: " << error );
461 }
462 // Negative gearing
463 Leg flooredLeg_n =
464 vars.makeCapFlooredLeg(vars.startDate,vars.length,caps0,floors,
465 vars.volatility,gearing_n,spread_n);
466 Swap floorLeg_n(fixedLeg,flooredLeg_n);
467 floorLeg_n.setPricingEngine(engine);
468 Cap cap_n(floatLeg,std::vector<Rate>(1,(floorstrike-spread_n)/gearing_n));
469 cap_n.setPricingEngine(vars.makeEngine(vars.volatility));
470 npvVanilla = vanillaLeg_n.NPV();
471 npvFlooredLeg = floorLeg_n.NPV();
472 npvCap = cap_n.NPV();
473 error = std::abs(npvFlooredLeg - (npvVanilla - gearing_n*npvCap));
474 if (error>tolerance) {
475 BOOST_ERROR("\nCapped Leg: gearing=" << gearing_n << ", " <<
476 "spread= " << spread_n *100 <<
477 "%, strike=" << floorstrike*100 << "%, " <<
478 "effective strike= " << (floorstrike-spread_n)/gearing_n*100 <<
479 "%\n" <<
480 " Capped Floating Leg NPV: " << npvFlooredLeg << "\n" <<
481 " Floating Leg NPV - Cap NPV: " << npvVanilla - gearing_n*npvCap << "\n" <<
482 " Diff: " << error );
483 }
484 /* gearing = a and spread = b
485 COLLARED coupon - Decomposition of payoff
486 Payoff = Nom * Min(caprate,Max(a*rate+b,floorrate)) * accrualperiod
487 --> If a>0 (assuming positive effective strike):
488 Payoff = VanillaFloatingLeg - Collar(a*rate+b, floorrate, caprate)
489 --> If a<0 (assuming positive effective strike):
490 Payoff = VanillaFloatingLeg + Collar(|a|*rate+b, caprate, floorrate)
491 */
492 // Positive gearing
493 Leg collaredLeg_p =
494 vars.makeCapFlooredLeg(vars.startDate,vars.length,caps,floors,
495 vars.volatility,gearing_p,spread_p);
496 Swap collarLeg_p1(fixedLeg,collaredLeg_p);
497 collarLeg_p1.setPricingEngine(engine);
498 Collar collar_p(floatLeg_p,
499 std::vector<Rate>(1,capstrike),
500 std::vector<Rate>(1,floorstrike));
501 collar_p.setPricingEngine(vars.makeEngine(vars.volatility));
502 npvVanilla = vanillaLeg_p.NPV();
503 npvCollaredLeg = collarLeg_p1.NPV();
504 npvCollar = collar_p.NPV();
505 error = std::abs(npvCollaredLeg - (npvVanilla - npvCollar));
506 if (error>tolerance) {
507 BOOST_ERROR("\nCollared Leg: gearing=" << gearing_p << ", "
508 << "spread= " << spread_p*100 << "%, strike="
509 << floorstrike*100 << "% and " << capstrike*100
510 << "%, "
511 << "effective strike=" << (floorstrike-spread_p)/gearing_p*100
512 << "% and " << (capstrike-spread_p)/gearing_p*100
513 << "%\n" <<
514 " Collared Floating Leg NPV: " << npvCollaredLeg
515 << "\n" <<
516 " Floating Leg NPV - Collar NPV: " << npvVanilla - npvCollar
517 << "\n" <<
518 " Diff: " << error );
519 }
520 // Negative gearing
521 Leg collaredLeg_n =
522 vars.makeCapFlooredLeg(vars.startDate,vars.length,caps,floors,
523 vars.volatility,gearing_n,spread_n);
524 Swap collarLeg_n1(fixedLeg,collaredLeg_n);
525 collarLeg_n1.setPricingEngine(engine);
526 Collar collar_n(floatLeg,
527 std::vector<Rate>(1,(floorstrike-spread_n)/gearing_n),
528 std::vector<Rate>(1,(capstrike-spread_n)/gearing_n));
529 collar_n.setPricingEngine(vars.makeEngine(vars.volatility));
530 npvVanilla = vanillaLeg_n.NPV();
531 npvCollaredLeg = collarLeg_n1.NPV();
532 npvCollar = collar_n.NPV();
533 error = std::abs(npvCollaredLeg - (npvVanilla - gearing_n*npvCollar));
534 if (error>tolerance) {
535 BOOST_ERROR("\nCollared Leg: gearing=" << gearing_n << ", "
536 << "spread= " << spread_n*100 << "%, strike="
537 << floorstrike*100 << "% and " << capstrike*100
538 << "%, "
539 << "effective strike=" << (floorstrike-spread_n)/gearing_n*100
540 << "% and " << (capstrike-spread_n)/gearing_n*100
541 << "%\n" <<
542 " Collared Floating Leg NPV: " << npvCollaredLeg
543 << "\n" <<
544 " Floating Leg NPV - Collar NPV: " << npvVanilla - gearing_n*npvCollar
545 << "\n" <<
546 " Diff: " << error );
547 }
548 }
549
suite()550 test_suite* CapFlooredCouponTest::suite() {
551 test_suite* suite = BOOST_TEST_SUITE("Capped and floored coupon tests");
552 suite->add(QUANTLIB_TEST_CASE(&CapFlooredCouponTest::testLargeRates));
553 suite->add(QUANTLIB_TEST_CASE(&CapFlooredCouponTest::testDecomposition));
554 return suite;
555 }
556
557