23#include <boost/test/unit_test.hpp>
27#include <ql/instruments/makecapfloor.hpp>
28#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
29#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
30#include <ql/termstructures/volatility/capfloor/capfloortermvolcurve.hpp>
31#include <ql/termstructures/volatility/optionlet/strippedoptionletadapter.hpp>
32#include <ql/termstructures/yield/zerospreadedtermstructure.hpp>
36using namespace boost::unit_test_framework;
43 : referenceDate(5, Feb, 2016), settlementDays(0), calendar(TARGET()), bdc(Following),
44 dayCounter(Actual365Fixed()), accuracy(1.0e-6), maxIter(100) {
46 Settings::instance().evaluationDate() = referenceDate;
48 iborIndex = QuantLib::ext::make_shared<Euribor6M>(yieldCurves.forward6M);
55 Natural settlementDays;
57 BusinessDayConvention bdc;
58 DayCounter dayCounter;
64 QuantLib::ext::shared_ptr<IborIndex> iborIndex;
74BOOST_AUTO_TEST_SUITE(OptionletStripperTest)
77 BOOST_TEST_MESSAGE(
"Testing standard stripping of normal capfloor vols...");
82 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
83 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
84 vars.vols.tenors, vars.vols.strikes, vars.vols.nVols,
88 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
89 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
90 vars.yieldCurves.discountEonia, Normal));
91 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
92 Handle<OptionletVolatilityStructure> ovs(adapter);
93 ovs->enableExtrapolation();
94 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> engine =
95 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
98 QuantLib::ext::shared_ptr<CapFloor> cap;
100 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
101 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> flatEngine =
102 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, quote, vars.dayCounter);
104 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
105 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
106 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
107 vars.settlementDays * Days);
109 cap->setPricingEngine(engine);
110 Real strippedPrice = cap->NPV();
112 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nVols[i][j]));
113 cap->setPricingEngine(flatEngine);
114 Real flatPrice = cap->NPV();
116 Real error = std::fabs(strippedPrice - flatPrice);
117 BOOST_CHECK_MESSAGE(error < vars.accuracy,
118 "\noption tenor: " << vars.vols.tenors[i]
119 <<
"\nstrike: " << io::rate(vars.vols.strikes[j])
120 <<
"\nstripped vol price: " << io::rate(strippedPrice)
121 <<
"\nconstant vol price: " << io::rate(flatPrice)
122 <<
"\nerror: " << io::rate(error)
123 <<
"\ntolerance: " << io::rate(vars.accuracy));
129 BOOST_TEST_MESSAGE(
"Testing standard stripping of shifted lognormal capfloor vols...");
134 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
135 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
136 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_1,
140 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
142 vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_1));
143 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
144 Handle<OptionletVolatilityStructure> ovs(adapter);
145 ovs->enableExtrapolation();
146 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
147 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
150 QuantLib::ext::shared_ptr<CapFloor> cap;
152 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
153 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
154 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_1);
156 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
157 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
158 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
159 vars.settlementDays * Days);
161 cap->setPricingEngine(engine);
162 Real strippedPrice = cap->NPV();
164 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_1[i][j]));
165 cap->setPricingEngine(flatEngine);
166 Real flatPrice = cap->NPV();
168 Real error = std::fabs(strippedPrice - flatPrice);
169 BOOST_CHECK_MESSAGE(error < vars.accuracy,
170 "\noption tenor: " << vars.vols.tenors[i]
171 <<
"\nstrike: " << io::rate(vars.vols.strikes[j])
172 <<
"\nstripped vol price: " << io::rate(strippedPrice)
173 <<
"\nconstant vol price: " << io::rate(flatPrice)
174 <<
"\nerror: " << io::rate(error)
175 <<
"\ntolerance: " << io::rate(vars.accuracy));
181 BOOST_TEST_MESSAGE(
"Testing stripping of normal capfloor vols to give shifted lognormal optionlet vols...");
186 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
187 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
188 vars.vols.tenors, vars.vols.strikes, vars.vols.nVols,
192 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
193 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
194 vars.yieldCurves.discountEonia, Normal, 0.0, ShiftedLognormal, vars.vols.shift_1));
195 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
196 Handle<OptionletVolatilityStructure> ovs(adapter);
197 ovs->enableExtrapolation();
198 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
199 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
202 QuantLib::ext::shared_ptr<CapFloor> cap;
204 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
205 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> flatEngine =
206 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, quote, vars.dayCounter);
208 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
209 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
210 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
211 vars.settlementDays * Days);
213 cap->setPricingEngine(engine);
214 Real strippedPrice = cap->NPV();
216 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nVols[i][j]));
217 cap->setPricingEngine(flatEngine);
218 Real flatPrice = cap->NPV();
220 Real error = std::fabs(strippedPrice - flatPrice);
221 BOOST_CHECK_MESSAGE(error < vars.accuracy,
222 "\noption tenor: " << vars.vols.tenors[i]
223 <<
"\nstrike: " << io::rate(vars.vols.strikes[j])
224 <<
"\nstripped vol price: " << io::rate(strippedPrice)
225 <<
"\nconstant vol price: " << io::rate(flatPrice)
226 <<
"\nerror: " << io::rate(error)
227 <<
"\ntolerance: " << io::rate(vars.accuracy));
233 BOOST_TEST_MESSAGE(
"Testing stripping of shifted lognormal capfloor vols to give normal optionlet vols...");
238 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
239 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
240 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_2,
244 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
245 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
246 vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_2, Normal, 0.0));
247 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
248 Handle<OptionletVolatilityStructure> ovs(adapter);
249 ovs->enableExtrapolation();
250 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> engine =
251 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
254 QuantLib::ext::shared_ptr<CapFloor> cap;
256 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
257 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
258 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_2);
260 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
261 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
262 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
263 vars.settlementDays * Days);
265 cap->setPricingEngine(engine);
266 Real strippedPrice = cap->NPV();
268 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_2[i][j]));
269 cap->setPricingEngine(flatEngine);
270 Real flatPrice = cap->NPV();
272 Real error = std::fabs(strippedPrice - flatPrice);
273 BOOST_CHECK_MESSAGE(error < vars.accuracy,
274 "\noption tenor: " << vars.vols.tenors[i]
275 <<
"\nstrike: " << io::rate(vars.vols.strikes[j])
276 <<
"\nstripped vol price: " << io::rate(strippedPrice)
277 <<
"\nconstant vol price: " << io::rate(flatPrice)
278 <<
"\nerror: " << io::rate(error)
279 <<
"\ntolerance: " << io::rate(vars.accuracy));
285 BOOST_TEST_MESSAGE(
"Testing stripping with shifted lognormal vols with different shifts...");
290 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
291 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
292 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_2,
296 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper =
298 volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter, vars.yieldCurves.discountEonia,
299 ShiftedLognormal, vars.vols.shift_2, ShiftedLognormal, vars.vols.shift_1));
300 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
301 Handle<OptionletVolatilityStructure> ovs(adapter);
302 ovs->enableExtrapolation();
303 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
304 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
307 QuantLib::ext::shared_ptr<CapFloor> cap;
309 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
310 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
311 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_2);
313 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
314 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
315 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
316 vars.settlementDays * Days);
318 cap->setPricingEngine(engine);
319 Real strippedPrice = cap->NPV();
321 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_2[i][j]));
322 cap->setPricingEngine(flatEngine);
323 Real flatPrice = cap->NPV();
325 Real error = std::fabs(strippedPrice - flatPrice);
326 BOOST_CHECK_MESSAGE(error < vars.accuracy,
327 "\noption tenor: " << vars.vols.tenors[i]
328 <<
"\nstrike: " << io::rate(vars.vols.strikes[j])
329 <<
"\nstripped vol price: " << io::rate(strippedPrice)
330 <<
"\nconstant vol price: " << io::rate(flatPrice)
331 <<
"\nerror: " << io::rate(error)
332 <<
"\ntolerance: " << io::rate(vars.accuracy));
338 BOOST_TEST_MESSAGE(
"Testing standard stripping of normal capfloor vols with overlayed ATM curve...");
343 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
344 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
345 vars.vols.tenors, vars.vols.strikes, vars.vols.nVols,
349 Handle<CapFloorTermVolCurve> atmVolCurve(QuantLib::ext::make_shared<CapFloorTermVolCurve>(
350 vars.settlementDays, vars.calendar, vars.bdc, vars.vols.atmTenors, vars.vols.nAtmVols, vars.dayCounter));
353 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper1> tempStripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
354 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
355 vars.yieldCurves.discountEonia, Normal));
358 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::make_shared<QuantExt::OptionletStripper2>(
359 tempStripper, atmVolCurve, vars.yieldCurves.discountEonia, Normal);
361 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
362 Handle<OptionletVolatilityStructure> ovs(adapter);
363 ovs->enableExtrapolation();
366 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> engine =
367 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
370 QuantLib::ext::shared_ptr<CapFloor> cap;
372 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
373 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> flatEngine =
374 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, quote, vars.dayCounter);
377 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
378 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
379 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
380 vars.settlementDays * Days);
382 cap->setPricingEngine(engine);
383 Real strippedPrice = cap->NPV();
385 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nVols[i][j]));
386 cap->setPricingEngine(flatEngine);
387 Real flatPrice = cap->NPV();
389 Real error = std::fabs(strippedPrice - flatPrice);
390 BOOST_CHECK_MESSAGE(error < vars.accuracy,
391 "\noption tenor: " << vars.vols.tenors[i]
392 <<
"\nstrike: " << io::rate(vars.vols.strikes[j])
393 <<
"\nstripped vol price: " << io::rate(strippedPrice)
394 <<
"\nconstant vol price: " << io::rate(flatPrice)
395 <<
"\nerror: " << io::rate(error)
396 <<
"\ntolerance: " << io::rate(vars.accuracy));
401 Volatility dummyVol = 0.10;
402 QuantLib::ext::shared_ptr<BlackCapFloorEngine> tempEngine =
403 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, dummyVol);
405 for (Size i = 0; i < vars.vols.atmTenors.size(); ++i) {
408 cap = MakeCapFloor(CapFloor::Cap, vars.vols.atmTenors[i], vars.iborIndex, Null<Rate>(),
409 vars.settlementDays * Days)
410 .withPricingEngine(tempEngine);
412 cap->setPricingEngine(engine);
413 Real strippedPrice = cap->NPV();
415 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nAtmVols[i]));
416 cap->setPricingEngine(flatEngine);
417 Real flatPrice = cap->NPV();
419 Real error = std::fabs(strippedPrice - flatPrice);
420 BOOST_CHECK_MESSAGE(error < vars.accuracy,
421 "\noption tenor: " << vars.vols.atmTenors[i] <<
"\natm strike: "
422 << io::rate(cap->atmRate(**vars.yieldCurves.discountEonia))
423 <<
"\nstripped vol price: " << io::rate(strippedPrice)
424 <<
"\nconstant vol price: " << io::rate(flatPrice)
425 <<
"\nerror: " << io::rate(error)
426 <<
"\ntolerance: " << io::rate(vars.accuracy));
431 BOOST_TEST_MESSAGE(
"Testing standard stripping of shifted lognormal capfloor vols with overlayed ATM curve...");
436 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
437 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
438 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_2,
442 Handle<CapFloorTermVolCurve> atmVolCurve(QuantLib::ext::make_shared<CapFloorTermVolCurve>(
443 vars.settlementDays, vars.calendar, vars.bdc, vars.vols.atmTenors, vars.vols.slnAtmVols_2, vars.dayCounter));
446 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper1> tempStripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
448 vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_2));
451 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::make_shared<QuantExt::OptionletStripper2>(
452 tempStripper, atmVolCurve, vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_2);
454 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
455 Handle<OptionletVolatilityStructure> ovs(adapter);
456 ovs->enableExtrapolation();
459 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
460 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
463 QuantLib::ext::shared_ptr<CapFloor> cap;
465 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
466 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
467 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_2);
470 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
471 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
472 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
473 vars.settlementDays * Days);
475 cap->setPricingEngine(engine);
476 Real strippedPrice = cap->NPV();
478 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_2[i][j]));
479 cap->setPricingEngine(flatEngine);
480 Real flatPrice = cap->NPV();
482 Real error = std::fabs(strippedPrice - flatPrice);
483 BOOST_CHECK_MESSAGE(error < vars.accuracy,
484 "\noption tenor: " << vars.vols.tenors[i]
485 <<
"\nstrike: " << io::rate(vars.vols.strikes[j])
486 <<
"\nstripped vol price: " << io::rate(strippedPrice)
487 <<
"\nconstant vol price: " << io::rate(flatPrice)
488 <<
"\nerror: " << io::rate(error)
489 <<
"\ntolerance: " << io::rate(vars.accuracy));
494 for (Size i = 0; i < vars.vols.atmTenors.size(); ++i) {
496 cap = MakeCapFloor(CapFloor::Cap, vars.vols.atmTenors[i], vars.iborIndex, Null<Rate>(),
497 vars.settlementDays * Days)
498 .withPricingEngine(engine);
499 Real strippedPrice = cap->NPV();
501 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnAtmVols_2[i]));
502 cap->setPricingEngine(flatEngine);
503 Real flatPrice = cap->NPV();
505 Real error = std::fabs(strippedPrice - flatPrice);
506 BOOST_CHECK_MESSAGE(error < vars.accuracy,
507 "\noption tenor: " << vars.vols.atmTenors[i] <<
"\natm strike: "
508 << io::rate(cap->atmRate(**vars.yieldCurves.discountEonia))
509 <<
"\nstripped vol price: " << io::rate(strippedPrice)
510 <<
"\nconstant vol price: " << io::rate(flatPrice)
511 <<
"\nerror: " << io::rate(error)
512 <<
"\ntolerance: " << io::rate(vars.accuracy));
517 BOOST_TEST_MESSAGE(
"Testing stripping of normal capfloor vols with ATM to give shifted lognormal...");
522 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
523 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
524 vars.vols.tenors, vars.vols.strikes, vars.vols.nVols,
528 Handle<CapFloorTermVolCurve> atmVolCurve(QuantLib::ext::make_shared<CapFloorTermVolCurve>(
529 vars.settlementDays, vars.calendar, vars.bdc, vars.vols.atmTenors, vars.vols.nAtmVols, vars.dayCounter));
532 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper1> tempStripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
533 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
534 vars.yieldCurves.discountEonia, Normal, 0.0, ShiftedLognormal, vars.vols.shift_1));
537 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::make_shared<QuantExt::OptionletStripper2>(
538 tempStripper, atmVolCurve, vars.yieldCurves.discountEonia, Normal);
540 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
541 Handle<OptionletVolatilityStructure> ovs(adapter);
542 ovs->enableExtrapolation();
545 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
546 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
549 QuantLib::ext::shared_ptr<CapFloor> cap;
551 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
552 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> flatEngine =
553 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, quote, vars.dayCounter);
556 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
557 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
558 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
559 vars.settlementDays * Days);
561 cap->setPricingEngine(engine);
562 Real strippedPrice = cap->NPV();
564 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nVols[i][j]));
565 cap->setPricingEngine(flatEngine);
566 Real flatPrice = cap->NPV();
568 Real error = std::fabs(strippedPrice - flatPrice);
569 BOOST_CHECK_MESSAGE(error < vars.accuracy,
570 "\noption tenor: " << vars.vols.tenors[i]
571 <<
"\nstrike: " << io::rate(vars.vols.strikes[j])
572 <<
"\nstripped vol price: " << io::rate(strippedPrice)
573 <<
"\nconstant vol price: " << io::rate(flatPrice)
574 <<
"\nerror: " << io::rate(error)
575 <<
"\ntolerance: " << io::rate(vars.accuracy));
580 for (Size i = 0; i < vars.vols.atmTenors.size(); ++i) {
582 cap = MakeCapFloor(CapFloor::Cap, vars.vols.atmTenors[i], vars.iborIndex, Null<Rate>(),
583 vars.settlementDays * Days)
584 .withPricingEngine(engine);
585 Real strippedPrice = cap->NPV();
587 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nAtmVols[i]));
588 cap->setPricingEngine(flatEngine);
589 Real flatPrice = cap->NPV();
591 Real error = std::fabs(strippedPrice - flatPrice);
592 BOOST_CHECK_MESSAGE(error < vars.accuracy,
593 "\noption tenor: " << vars.vols.atmTenors[i] <<
"\natm strike: "
594 << io::rate(cap->atmRate(**vars.yieldCurves.discountEonia))
595 <<
"\nstripped vol price: " << io::rate(strippedPrice)
596 <<
"\nconstant vol price: " << io::rate(flatPrice)
597 <<
"\nerror: " << io::rate(error)
598 <<
"\ntolerance: " << io::rate(vars.accuracy));
603 BOOST_TEST_MESSAGE(
"Testing stripping of shifted lognormal capfloor vols with ATM to give normal...");
608 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
609 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
610 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_1,
614 Handle<CapFloorTermVolCurve> atmVolCurve(QuantLib::ext::make_shared<CapFloorTermVolCurve>(
615 vars.settlementDays, vars.calendar, vars.bdc, vars.vols.atmTenors, vars.vols.slnAtmVols_1, vars.dayCounter));
618 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper1> tempStripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
619 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
620 vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_1, Normal, 0.0));
623 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::make_shared<QuantExt::OptionletStripper2>(
624 tempStripper, atmVolCurve, vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_1);
626 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
627 Handle<OptionletVolatilityStructure> ovs(adapter);
628 ovs->enableExtrapolation();
631 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> engine =
632 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
635 QuantLib::ext::shared_ptr<CapFloor> cap;
637 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
638 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
639 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_1);
642 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
643 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
644 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
645 vars.settlementDays * Days);
647 cap->setPricingEngine(engine);
648 Real strippedPrice = cap->NPV();
650 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_1[i][j]));
651 cap->setPricingEngine(flatEngine);
652 Real flatPrice = cap->NPV();
654 Real error = std::fabs(strippedPrice - flatPrice);
655 BOOST_CHECK_MESSAGE(error < vars.accuracy,
656 "\noption tenor: " << vars.vols.tenors[i]
657 <<
"\nstrike: " << io::rate(vars.vols.strikes[j])
658 <<
"\nstripped vol price: " << io::rate(strippedPrice)
659 <<
"\nconstant vol price: " << io::rate(flatPrice)
660 <<
"\nerror: " << io::rate(error)
661 <<
"\ntolerance: " << io::rate(vars.accuracy));
666 for (Size i = 0; i < vars.vols.atmTenors.size(); ++i) {
667 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnAtmVols_1[i]));
669 cap = MakeCapFloor(CapFloor::Cap, vars.vols.atmTenors[i], vars.iborIndex, Null<Rate>(),
670 vars.settlementDays * Days)
671 .withPricingEngine(flatEngine);
672 Real flatPrice = cap->NPV();
674 cap->setPricingEngine(engine);
675 Real strippedPrice = cap->NPV();
677 Real error = std::fabs(strippedPrice - flatPrice);
678 BOOST_CHECK_MESSAGE(error < vars.accuracy,
679 "\noption tenor: " << vars.vols.atmTenors[i] <<
"\natm strike: "
680 << io::rate(cap->atmRate(**vars.yieldCurves.discountEonia))
681 <<
"\nstripped vol price: " << io::rate(strippedPrice)
682 <<
"\nconstant vol price: " << io::rate(flatPrice)
683 <<
"\nerror: " << io::rate(error)
684 <<
"\ntolerance: " << io::rate(vars.accuracy));
689 BOOST_TEST_MESSAGE(
"Testing stripping with shifted lognormal vols with ATM with different shifts...");
694 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
695 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
696 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_1,
700 Handle<CapFloorTermVolCurve> atmVolCurve(QuantLib::ext::make_shared<CapFloorTermVolCurve>(
701 vars.settlementDays, vars.calendar, vars.bdc, vars.vols.atmTenors, vars.vols.slnAtmVols_1, vars.dayCounter));
704 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper1> tempStripper =
706 volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter, vars.yieldCurves.discountEonia,
707 ShiftedLognormal, vars.vols.shift_1, ShiftedLognormal, vars.vols.shift_2));
710 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::make_shared<QuantExt::OptionletStripper2>(
711 tempStripper, atmVolCurve, vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_1);
713 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
714 Handle<OptionletVolatilityStructure> ovs(adapter);
715 ovs->enableExtrapolation();
718 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
719 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
722 QuantLib::ext::shared_ptr<CapFloor> cap;
724 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
725 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
726 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_1);
729 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
730 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
731 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
732 vars.settlementDays * Days);
734 cap->setPricingEngine(engine);
735 Real strippedPrice = cap->NPV();
737 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_1[i][j]));
738 cap->setPricingEngine(flatEngine);
739 Real flatPrice = cap->NPV();
741 Real error = std::fabs(strippedPrice - flatPrice);
742 BOOST_CHECK_MESSAGE(error < vars.accuracy,
743 "\noption tenor: " << vars.vols.tenors[i]
744 <<
"\nstrike: " << io::rate(vars.vols.strikes[j])
745 <<
"\nstripped vol price: " << io::rate(strippedPrice)
746 <<
"\nconstant vol price: " << io::rate(flatPrice)
747 <<
"\nerror: " << io::rate(error)
748 <<
"\ntolerance: " << io::rate(vars.accuracy));
753 for (Size i = 0; i < vars.vols.atmTenors.size(); ++i) {
754 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnAtmVols_1[i]));
756 cap = MakeCapFloor(CapFloor::Cap, vars.vols.atmTenors[i], vars.iborIndex, Null<Rate>(),
757 vars.settlementDays * Days)
758 .withPricingEngine(flatEngine);
759 Real flatPrice = cap->NPV();
761 cap->setPricingEngine(engine);
762 Real strippedPrice = cap->NPV();
764 Real error = std::fabs(strippedPrice - flatPrice);
765 BOOST_CHECK_MESSAGE(error < vars.accuracy,
766 "\noption tenor: " << vars.vols.atmTenors[i] <<
"\natm strike: "
767 << io::rate(cap->atmRate(**vars.yieldCurves.discountEonia))
768 <<
"\nstripped vol price: " << io::rate(strippedPrice)
769 <<
"\nconstant vol price: " << io::rate(flatPrice)
770 <<
"\nerror: " << io::rate(error)
771 <<
"\ntolerance: " << io::rate(vars.accuracy));
776 BOOST_TEST_MESSAGE(
"Testing stripping of normal to give lognormal gives error (due to negative strike)...");
781 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
782 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
783 vars.vols.tenors, vars.vols.strikes, vars.vols.nVols,
787 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
789 vars.yieldCurves.discountEonia, Normal, 0.0, ShiftedLognormal, 0.0));
792 BOOST_CHECK_THROW(stripper->recalculate(), QuantLib::Error);
796 BOOST_TEST_MESSAGE(
"Testing stripping of normal to give lognormal gives error (due to negative forward)...");
801 vector<Volatility>
strikes(vars.vols.strikes.begin() + 2, vars.vols.strikes.end());
802 Matrix vols(vars.vols.tenors.size(),
strikes.size());
803 for (Size i = 0; i < vols.rows(); ++i)
804 for (Size j = 0; j < vols.columns(); ++j)
805 vols[i][j] = vars.vols.nVols[i][j + 2];
808 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
809 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
810 vars.vols.tenors,
strikes, vols, vars.dayCounter);
813 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
815 vars.yieldCurves.discountEonia, Normal, 0.0, ShiftedLognormal, 0.0));
818 BOOST_CHECK_THROW(stripper->recalculate(), QuantLib::Error);
822 BOOST_TEST_MESSAGE(
"Testing stripping of normal to give lognormal when forwards are positive...");
827 vector<Volatility>
strikes(vars.vols.strikes.begin() + 2, vars.vols.strikes.end());
828 Matrix vols(vars.vols.tenors.size(),
strikes.size());
829 for (Size i = 0; i < vols.rows(); ++i)
830 for (Size j = 0; j < vols.columns(); ++j)
831 vols[i][j] = vars.vols.nVols[i][j + 2];
834 Handle<Quote> spread(QuantLib::ext::make_shared<SimpleQuote>(0.015));
835 Handle<YieldTermStructure> shiftedForward(
836 QuantLib::ext::make_shared<ZeroSpreadedTermStructure>(vars.yieldCurves.forward6M, spread));
837 QuantLib::ext::shared_ptr<IborIndex> iborIndex = vars.iborIndex->clone(shiftedForward);
840 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
841 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
842 vars.vols.tenors,
strikes, vols, vars.dayCounter);
845 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
847 vars.yieldCurves.discountEonia, Normal, 0.0, ShiftedLognormal, 0.0));
848 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
849 Handle<OptionletVolatilityStructure> ovs(adapter);
850 ovs->enableExtrapolation();
851 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
852 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
855 QuantLib::ext::shared_ptr<CapFloor> cap;
857 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
858 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> flatEngine =
859 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, quote, vars.dayCounter);
861 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
862 for (Size j = 0; j <
strikes.size(); ++j) {
863 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], iborIndex,
strikes[j], vars.settlementDays * Days);
865 cap->setPricingEngine(engine);
866 Real strippedPrice = cap->NPV();
868 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vols[i][j]));
869 cap->setPricingEngine(flatEngine);
870 Real flatPrice = cap->NPV();
872 Real error = std::fabs(strippedPrice - flatPrice);
873 BOOST_CHECK_MESSAGE(error < vars.accuracy,
874 "\noption tenor: " << vars.vols.tenors[i]
875 <<
"\nstrike: " << io::rate(
strikes[j])
876 <<
"\nstripped vol price: " << io::rate(strippedPrice)
877 <<
"\nconstant vol price: " << io::rate(flatPrice)
878 <<
"\nerror: " << io::rate(error)
879 <<
"\ntolerance: " << io::rate(vars.accuracy));
884BOOST_AUTO_TEST_SUITE_END()
886BOOST_AUTO_TEST_SUITE_END()
structs containing capfloor market data that can be used in tests
Optionlet (caplet/floorlet) volatility strippers.
ATM optionlet (caplet/floorlet) volatility stripper.
BOOST_AUTO_TEST_CASE(testUsualNormalStripping)
Fixture that can be used at top level.
structs containing yield curve market data that can be used in tests