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