19#include <boost/assign.hpp>
21#include <boost/test/unit_test.hpp>
22#include <boost/test/data/test_case.hpp>
24#include <boost/variant.hpp>
30#include <ql/instruments/makecapfloor.hpp>
31#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
32#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
42using namespace boost::unit_test_framework;
45typedef QuantLib::BootstrapHelper<QuantLib::OptionletVolatilityStructure>
helper;
47using boost::assign::list_of;
48using boost::assign::map_list_of;
54using std::setprecision;
65 : referenceDate(5, Feb, 2016), settlementDays(0), calendar(TARGET()), bdc(Following),
66 dayCounter(Actual365Fixed()), accuracy(1.0e-12), globalAccuracy(1.0e-10), tolerance(1.0e-10) {
69 Settings::instance().evaluationDate() = referenceDate;
72 iborIndex = QuantLib::ext::make_shared<Euribor6M>(testYieldCurves.forward6M);
79 Natural settlementDays;
81 BusinessDayConvention bdc;
82 DayCounter dayCounter;
94 QuantLib::ext::shared_ptr<IborIndex> iborIndex;
104vector<VolatilityType> volatilityTypes = list_of(Normal)(ShiftedLognormal);
107typedef boost::variant<Linear, BackwardFlat, QuantExt::LinearFlat, Cubic, QuantExt::CubicFlat> InterpolationType;
109vector<InterpolationType> timeInterpolationTypes =
110 list_of(InterpolationType(Linear()))(InterpolationType(BackwardFlat()))(InterpolationType(
QuantExt::LinearFlat()))(
113vector<InterpolationType> smileInterpolationTypes = list_of(InterpolationType(Linear()))(
117vector<bool> flatFirstPeriodValues = list_of(
true)(
false);
120vector<bool> isMovingValues = list_of(
true)(
false);
123vector<bool> addAtmValues = list_of(
true)(
false);
127vector<bool> interpOnOptionletValues = list_of(
true)(
false);
130vector<TermVolSurface::InterpolationMethod> vsInterpMethods =
134string to_string(
const InterpolationType& interpolationType) {
136 switch (interpolationType.which()) {
141 result =
"BackwardFlat";
144 result =
"LinearFlat";
150 result =
"CubicFlat";
153 BOOST_FAIL(
"Unexpected interpolation type");
159template <
class TI,
class SI>
160Handle<OptionletVolatilityStructure> createOvs(VolatilityType volatilityType,
bool flatFirstPeriod,
bool isMoving,
162 bool interpOnOptionlet,
bool withAtm) {
169 Real displacement = 0.0;
170 if (volatilityType == Normal) {
171 vols = vars.testVols.nVols;
173 vols = vars.testVols.slnVols_1;
174 displacement = vars.testVols.shift_1;
178 QuantLib::ext::shared_ptr<TermVolSurface> cfts;
180 cfts = QuantLib::ext::make_shared<TermVolSurface>(vars.settlementDays, vars.calendar, vars.bdc, vars.testVols.tenors,
181 vars.testVols.strikes, vols, vars.dayCounter, vsInterpMethod);
183 cfts = QuantLib::ext::make_shared<TermVolSurface>(vars.referenceDate, vars.calendar, vars.bdc, vars.testVols.tenors,
184 vars.testVols.strikes, vols, vars.dayCounter, vsInterpMethod);
188 VolatilityType ovType = Normal;
189 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> pwos = QuantLib::ext::make_shared<PiecewiseOptionletStripper<TI> >(
190 cfts, vars.iborIndex, vars.testYieldCurves.discountEonia, flatFirstPeriod, volatilityType, displacement, ovType,
191 0.0, interpOnOptionlet, TI(),
194 vars.accuracy, vars.globalAccuracy));
197 vector<Handle<Quote> > atmVolquotes(vars.testVols.atmTenors.size());
200 QuantLib::ext::shared_ptr<CapFloorTermVolCurve> atmVolCurve;
202 if (volatilityType == Normal) {
204 for (Size i = 0; i < atmVolquotes.size(); ++i) {
205 atmVolquotes[i] = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(vars.testVols.nAtmVols[i]));
210 atmVolCurve = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Linear> >(
211 vars.settlementDays, vars.calendar, vars.bdc, vars.testVols.atmTenors, atmVolquotes,
212 vars.dayCounter, flatFirstPeriod);
214 atmVolCurve = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Cubic> >(
215 vars.settlementDays, vars.calendar, vars.bdc, vars.testVols.atmTenors, atmVolquotes,
216 vars.dayCounter, flatFirstPeriod);
220 atmVolCurve = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Linear> >(
221 vars.referenceDate, vars.calendar, vars.bdc, vars.testVols.atmTenors, atmVolquotes,
222 vars.dayCounter, flatFirstPeriod);
224 atmVolCurve = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Cubic> >(
225 vars.referenceDate, vars.calendar, vars.bdc, vars.testVols.atmTenors, atmVolquotes,
226 vars.dayCounter, flatFirstPeriod);
231 for (Size i = 0; i < atmVolquotes.size(); ++i) {
232 atmVolquotes[i] = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(vars.testVols.slnAtmVols_1[i]));
237 atmVolCurve = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Linear> >(
238 vars.settlementDays, vars.calendar, vars.bdc, vars.testVols.atmTenors, atmVolquotes,
239 vars.dayCounter, flatFirstPeriod);
241 atmVolCurve = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Cubic> >(
242 vars.settlementDays, vars.calendar, vars.bdc, vars.testVols.atmTenors, atmVolquotes,
243 vars.dayCounter, flatFirstPeriod);
247 atmVolCurve = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Linear> >(
248 vars.referenceDate, vars.calendar, vars.bdc, vars.testVols.atmTenors, atmVolquotes,
249 vars.dayCounter, flatFirstPeriod);
251 atmVolCurve = QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Cubic> >(
252 vars.referenceDate, vars.calendar, vars.bdc, vars.testVols.atmTenors, atmVolquotes,
253 vars.dayCounter, flatFirstPeriod);
258 Handle<CapFloorTermVolCurve> atmVolCurveH(atmVolCurve);
259 pwos = QuantLib::ext::make_shared<OptionletStripperWithAtm<TI, SI> >(
260 pwos, atmVolCurveH, vars.testYieldCurves.discountEonia, volatilityType, displacement);
264 QuantLib::ext::shared_ptr<OptionletVolatilityStructure> ovs;
266 ovs = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<TI, SI> >(pwos);
268 ovs = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<TI, SI> >(vars.referenceDate, pwos);
271 return Handle<OptionletVolatilityStructure>(ovs);
278vector<Rate> cachedStrikes = list_of(-0.01)(-0.005)(0.015)(0.030)(0.035);
281vector<Date> cachedOptionletFixingDates = list_of(Date(5, Aug, 2016))(Date(7, Feb, 2017))(Date(7, Aug, 2017))(Date(
282 7, Feb, 2018))(Date(7, Aug, 2018))(Date(7, Feb, 2019))(Date(7, Aug, 2019))(Date(6, Feb, 2020))(Date(6, Aug, 2020))(
283 Date(5, Feb, 2021))(Date(5, Aug, 2021))(Date(7, Feb, 2022))(Date(5, Aug, 2022))(Date(7, Feb, 2023))(
284 Date(7, Aug, 2023))(Date(7, Feb, 2024))(Date(7, Aug, 2024))(Date(6, Feb, 2025))(Date(7, Aug, 2025))(
285 Date(5, Feb, 2026))(Date(6, Aug, 2026))(Date(5, Feb, 2027))(Date(5, Aug, 2027))(Date(7, Feb, 2028))(
286 Date(7, Aug, 2028))(Date(7, Feb, 2029))(Date(7, Aug, 2029))(Date(7, Feb, 2030))(Date(7, Aug, 2030))(
287 Date(6, Feb, 2031))(Date(7, Aug, 2031))(Date(5, Feb, 2032))(Date(5, Aug, 2032))(Date(7, Feb, 2033))(
288 Date(5, Aug, 2033))(Date(7, Feb, 2034))(Date(7, Aug, 2034))(Date(7, Feb, 2035))(Date(7, Aug, 2035));
291vector<Real> cachedValues =
292 list_of(0.002457000000)(0.002457000000)(0.006386500000)(0.009938000000)(0.009938000000)(0.002880443155)(
293 0.002880443155)(0.006459363495)(0.009751440414)(0.009751440414)(0.003292503430)(0.003292503430)(0.006530268295)(
294 0.009569895870)(0.009569895870)(0.003711393433)(0.003711393433)(0.006602348312)(0.009385342300)(0.009385342300)(
295 0.004123453708)(0.004123453708)(0.006673253111)(0.009203797756)(0.009203797756)(0.004542343711)(0.004542343711)(
296 0.006745333128)(0.009019244187)(0.009019244187)(0.004954403986)(0.004954403986)(0.006816237927)(0.008837699643)(
297 0.008837699643)(0.005371017413)(0.005371017413)(0.006887926205)(0.008654149082)(0.008654149082)(0.005785354264)(
298 0.005785354264)(0.006959222744)(0.008471601530)(0.008471601530)(0.006092137648)(0.006092137648)(0.006976645818)(
299 0.008397716202)(0.008397716202)(0.006395568208)(0.006395568208)(0.006993878476)(0.008324638363)(0.008324638363)(
300 0.006707380827)(0.006707380827)(0.007011587175)(0.008249541800)(0.008249541800)(0.007007458563)(0.007007458563)(
301 0.007028629417)(0.008177271451)(0.008177271451)(0.006955894311)(0.006955894311)(0.006917620059)(0.007958482968)(
302 0.007958482968)(0.006905716195)(0.006905716195)(0.006809594824)(0.007745575895)(0.007745575895)(0.006854706398)(
303 0.006854706398)(0.006699779115)(0.007529139976)(0.007529139976)(0.006804251054)(0.006804251054)(0.006591157055)(
304 0.007315056621)(0.007315056621)(0.006753518484)(0.006753518484)(0.006481938171)(0.007099796984)(0.007099796984)(
305 0.006703063140)(0.006703063140)(0.006373316111)(0.006885713629)(0.006885713629)(0.006623889360)(0.006623889360)(
306 0.006363776326)(0.006870339773)(0.006870339773)(0.006544715579)(0.006544715579)(0.006354236541)(0.006854965916)(
307 0.006854965916)(0.006465106778)(0.006465106778)(0.006344644340)(0.006839507588)(0.006839507588)(0.006386368018)(
308 0.006386368018)(0.006335156972)(0.006824218203)(0.006824218203)(0.006305454154)(0.006305454154)(0.006325407521)(
309 0.006808506460)(0.006808506460)(0.006226280373)(0.006226280373)(0.006315867737)(0.006793132603)(0.006793132603)(
310 0.006146236551)(0.006146236551)(0.006306223119)(0.006777589803)(0.006777589803)(0.006067497791)(0.006067497791)(
311 0.006296735750)(0.006762300419)(0.006762300419)(0.005987453969)(0.005987453969)(0.006287091133)(0.006746757619)(
312 0.006746757619)(0.005908715209)(0.005908715209)(0.006277603764)(0.006731468234)(0.006731468234)(0.005829106407)(
313 0.005829106407)(0.006268011563)(0.006716009906)(0.006716009906)(0.005749932627)(0.005749932627)(0.006258471778)(
314 0.006700636049)(0.006700636049)(0.005670758846)(0.005670758846)(0.006248931994)(0.006685262193)(0.006685262193)(
315 0.005591585065)(0.005591585065)(0.006239392209)(0.006669888336)(0.006669888336)(0.005510671202)(0.005510671202)(
316 0.006229642758)(0.006654176593)(0.006654176593)(0.005432802483)(0.005432802483)(0.006220260223)(0.006639056152)(
317 0.006639056152)(0.005351888619)(0.005351888619)(0.006210510772)(0.006623344408)(0.006623344408)(0.005273149860)(
318 0.005273149860)(0.006201023404)(0.006608055023)(0.006608055023)(0.005193106037)(0.005193106037)(0.006191378786)(
319 0.006592512223)(0.006592512223)(0.005114367277)(0.005114367277)(0.006181891418)(0.006577222839)(0.006577222839);
322vector<Date> cachedAdHocDates = list_of(Date(5, May, 2016))(Date(5, May, 2026))(Date(5, May, 2036));
325vector<Real> cachedAdHocValues = list_of(0.002457000000)(0.002457000000)(0.006386500000)(0.009938000000)(
326 0.009938000000)(0.006585172511)(0.006585172511)(0.006359111267)(0.006862821788)(0.006862821788)(0.005114367277)(
327 0.005114367277)(0.006181891418)(0.006577222839)(0.006577222839);
333namespace test_tools {
335template <>
struct print_log_value<InterpolationType> {
336 void operator()(ostream& os,
const InterpolationType& interpolationType) { os << to_string(interpolationType); }
344BOOST_AUTO_TEST_SUITE(PiecewiseOptionletStripperTests)
347 bdata::make(volatilityTypes) *
bdata::make(timeInterpolationTypes) *
348 bdata::make(smileInterpolationTypes) *
bdata::make(flatFirstPeriodValues) *
349 bdata::make(isMovingValues) *
bdata::make(vsInterpMethods) *
350 bdata::make(interpOnOptionletValues) *
bdata::make(addAtmValues),
351 volatilityType, timeInterp, smileInterp, flatFirstPeriod, isMoving, vsInterpMethod,
352 interpOnOptionlet, addAtm) {
354 BOOST_TEST_MESSAGE(
"Testing piecewise optionlet stripping of cap floor surface");
356 BOOST_TEST_MESSAGE(
"Test inputs are:");
357 BOOST_TEST_MESSAGE(
" Input volatility type:" << volatilityType);
358 BOOST_TEST_MESSAGE(
" Time Interpolation: " << to_string(timeInterp));
359 BOOST_TEST_MESSAGE(
" Smile Interpolation: " << to_string(smileInterp));
360 BOOST_TEST_MESSAGE(
" Flat first period: " << boolalpha << flatFirstPeriod);
361 BOOST_TEST_MESSAGE(
" Floating reference date: " << boolalpha << isMoving);
362 BOOST_TEST_MESSAGE(
" Cap floor term interpolation: " << vsInterpMethod);
363 BOOST_TEST_MESSAGE(
" Interpolate on optionlets: " << boolalpha << interpOnOptionlet);
364 BOOST_TEST_MESSAGE(
" Add in ATM curve: " << boolalpha << addAtm);
367 Handle<OptionletVolatilityStructure> ovs;
368 switch (timeInterp.which()) {
370 if (smileInterp.which() == 0) {
371 ovs = createOvs<Linear, Linear>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
372 interpOnOptionlet, addAtm);
373 }
else if (smileInterp.which() == 2) {
374 ovs = createOvs<Linear, LinearFlat>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
375 interpOnOptionlet, addAtm);
376 }
else if (smileInterp.which() == 3) {
377 ovs = createOvs<Linear, Cubic>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod, interpOnOptionlet,
379 }
else if (smileInterp.which() == 4) {
380 ovs = createOvs<Linear, CubicFlat>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
381 interpOnOptionlet, addAtm);
383 BOOST_FAIL(
"Unexpected smile interpolation type");
387 if (smileInterp.which() == 0) {
388 ovs = createOvs<BackwardFlat, Linear>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
389 interpOnOptionlet, addAtm);
390 }
else if (smileInterp.which() == 2) {
391 ovs = createOvs<BackwardFlat, LinearFlat>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
392 interpOnOptionlet, addAtm);
393 }
else if (smileInterp.which() == 3) {
394 ovs = createOvs<BackwardFlat, Cubic>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
395 interpOnOptionlet, addAtm);
396 }
else if (smileInterp.which() == 4) {
397 ovs = createOvs<BackwardFlat, CubicFlat>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
398 interpOnOptionlet, addAtm);
400 BOOST_FAIL(
"Unexpected smile interpolation type");
404 if (smileInterp.which() == 0) {
405 ovs = createOvs<LinearFlat, Linear>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
406 interpOnOptionlet, addAtm);
407 }
else if (smileInterp.which() == 2) {
408 ovs = createOvs<LinearFlat, LinearFlat>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
409 interpOnOptionlet, addAtm);
410 }
else if (smileInterp.which() == 3) {
411 ovs = createOvs<LinearFlat, Cubic>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
412 interpOnOptionlet, addAtm);
413 }
else if (smileInterp.which() == 4) {
414 ovs = createOvs<LinearFlat, CubicFlat>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
415 interpOnOptionlet, addAtm);
417 BOOST_FAIL(
"Unexpected smile interpolation type");
421 if (smileInterp.which() == 0) {
422 ovs = createOvs<Cubic, Linear>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod, interpOnOptionlet,
424 }
else if (smileInterp.which() == 2) {
425 ovs = createOvs<Cubic, LinearFlat>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
426 interpOnOptionlet, addAtm);
427 }
else if (smileInterp.which() == 3) {
428 ovs = createOvs<Cubic, Cubic>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod, interpOnOptionlet,
430 }
else if (smileInterp.which() == 4) {
431 ovs = createOvs<Cubic, CubicFlat>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
432 interpOnOptionlet, addAtm);
434 BOOST_FAIL(
"Unexpected smile interpolation type");
438 if (smileInterp.which() == 0) {
439 ovs = createOvs<CubicFlat, Linear>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
440 interpOnOptionlet, addAtm);
441 }
else if (smileInterp.which() == 2) {
442 ovs = createOvs<CubicFlat, LinearFlat>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
443 interpOnOptionlet, addAtm);
444 }
else if (smileInterp.which() == 3) {
445 ovs = createOvs<CubicFlat, Cubic>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
446 interpOnOptionlet, addAtm);
447 }
else if (smileInterp.which() == 4) {
448 ovs = createOvs<CubicFlat, CubicFlat>(volatilityType, flatFirstPeriod, isMoving, vsInterpMethod,
449 interpOnOptionlet, addAtm);
451 BOOST_FAIL(
"Unexpected smile interpolation type");
455 BOOST_FAIL(
"Unexpected time interpolation type");
460 Handle<YieldTermStructure> discount = testYieldCurves.discountEonia;
461 QuantLib::ext::shared_ptr<CapFloor> capFloor;
463 for (Size i = 0; i < testVols.tenors.size(); i++) {
465 for (Size j = 0; j < testVols.strikes.size(); j++) {
468 capFloor = MakeCapFloor(CapFloor::Cap, testVols.tenors[i], iborIndex, testVols.strikes[j]);
469 Rate atm = capFloor->atmRate(**discount);
470 if (testVols.strikes[j] < atm) {
471 capFloor = MakeCapFloor(CapFloor::Floor, testVols.tenors[i], iborIndex, testVols.strikes[j]);
476 if (volatilityType == ShiftedLognormal) {
477 flatVol = testVols.slnVols_1[i][j];
478 capFloor->setPricingEngine(
479 QuantLib::ext::make_shared<BlackCapFloorEngine>(discount, flatVol, dayCounter, testVols.shift_1));
481 flatVol = testVols.nVols[i][j];
482 capFloor->setPricingEngine(QuantLib::ext::make_shared<BachelierCapFloorEngine>(discount, flatVol, dayCounter));
484 Real flatNpv = capFloor->NPV();
487 capFloor->setPricingEngine(QuantLib::ext::make_shared<BachelierCapFloorEngine>(discount, ovs));
488 Real strippedNpv = capFloor->NPV();
491 Real diff = fabs(flatNpv - strippedNpv);
492 BOOST_CHECK_SMALL(diff, tolerance);
494 BOOST_TEST_MESSAGE(
" (Cap/Floor, Tenor, Strike, Volatility, Flat NPV, Stripped NPV, Flat - Stripped) = ("
495 << capFloor->type() <<
", " << testVols.tenors[i] <<
", " << testVols.strikes[j] <<
", "
496 << flatVol <<
", " << flatNpv <<
", " << strippedNpv <<
", " << diff <<
")");
503 for (Size i = 0; i < testVols.atmTenors.size(); i++) {
506 capFloor = MakeCapFloor(CapFloor::Cap, testVols.atmTenors[i], iborIndex, 0.01);
507 Rate atm = capFloor->atmRate(**discount);
508 capFloor = MakeCapFloor(CapFloor::Cap, testVols.atmTenors[i], iborIndex, atm);
512 if (volatilityType == ShiftedLognormal) {
513 flatVol = testVols.slnAtmVols_1[i];
514 capFloor->setPricingEngine(
515 QuantLib::ext::make_shared<BlackCapFloorEngine>(discount, flatVol, dayCounter, testVols.shift_1));
517 flatVol = testVols.nAtmVols[i];
518 capFloor->setPricingEngine(QuantLib::ext::make_shared<BachelierCapFloorEngine>(discount, flatVol, dayCounter));
520 Real flatNpv = capFloor->NPV();
523 capFloor->setPricingEngine(QuantLib::ext::make_shared<BachelierCapFloorEngine>(discount, ovs));
524 Real strippedNpv = capFloor->NPV();
527 Real diff = fabs(flatNpv - strippedNpv);
528 BOOST_CHECK_SMALL(diff, tolerance);
530 BOOST_TEST_MESSAGE(
" (Cap/Floor, Tenor, Strike, Volatility, Flat NPV, Stripped NPV, Flat - Stripped) = ("
531 << capFloor->type() <<
", " << testVols.atmTenors[i] <<
", ATM [" << atm <<
"], "
532 << flatVol <<
", " << flatNpv <<
", " << strippedNpv <<
", " << diff <<
")");
539 BOOST_TEST_MESSAGE(
"Testing extrapolation settings");
545 Handle<OptionletVolatilityStructure> ovs =
549 Date maxDate = ovs->maxDate();
550 Rate minStrike = ovs->minStrike();
551 Rate maxStrike = ovs->maxStrike();
554 BOOST_REQUIRE(maxStrike > minStrike);
557 Date testDate = referenceDate - 1 * Days;
558 Rate testStrike = (maxStrike + minStrike) / 2;
559 BOOST_CHECK_THROW(ovs->volatility(testDate, testStrike,
false), Error);
560 BOOST_CHECK_THROW(ovs->volatility(testDate, testStrike,
true), Error);
563 testDate = referenceDate + 1 * Days;
564 BOOST_CHECK_NO_THROW(ovs->volatility(testDate, testStrike,
false));
566 testDate = maxDate - 1 * Days;
567 BOOST_CHECK_NO_THROW(ovs->volatility(testDate, testStrike,
false));
570 testDate = maxDate + 1 * Days;
571 BOOST_CHECK_THROW(ovs->volatility(testDate, testStrike,
false), Error);
573 testDate = referenceDate + 1 * Days;
574 testStrike = minStrike - shift;
575 BOOST_CHECK_THROW(ovs->volatility(testDate, testStrike,
false), Error);
577 testStrike = maxStrike + shift;
578 BOOST_CHECK_THROW(ovs->volatility(testDate, testStrike,
false), Error);
581 testDate = maxDate + 1 * Days;
582 testStrike = (maxStrike + minStrike) / 2;
583 BOOST_CHECK_NO_THROW(ovs->volatility(testDate, testStrike,
true));
585 testDate = referenceDate + 1 * Days;
586 testStrike = minStrike - shift;
587 BOOST_CHECK_NO_THROW(ovs->volatility(testDate, testStrike,
true));
589 testStrike = maxStrike + shift;
590 BOOST_CHECK_NO_THROW(ovs->volatility(testDate, testStrike,
true));
593 ovs->enableExtrapolation();
595 testDate = maxDate + 1 * Days;
596 testStrike = (maxStrike + minStrike) / 2;
597 BOOST_CHECK_NO_THROW(ovs->volatility(testDate, testStrike,
false));
599 testDate = referenceDate + 1 * Days;
600 testStrike = minStrike - shift;
601 BOOST_CHECK_NO_THROW(ovs->volatility(testDate, testStrike,
false));
603 testStrike = maxStrike + shift;
604 BOOST_CHECK_NO_THROW(ovs->volatility(testDate, testStrike,
false));
610 BOOST_TEST_MESSAGE(
"Testing against cached optionlet volatilities with LinearFlat time and smile interpolation");
613 QuantLib::ext::shared_ptr<TermVolSurface> cfts =
614 QuantLib::ext::make_shared<TermVolSurface>(referenceDate, calendar, bdc, testVols.tenors, testVols.strikes,
618 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> pwos = QuantLib::ext::make_shared<PiecewiseOptionletStripper<LinearFlat> >(
619 cfts, iborIndex, testYieldCurves.discountEonia,
true, Normal, 0.0, Normal);
622 Handle<OptionletVolatilityStructure> ovs = Handle<OptionletVolatilityStructure>(
624 ovs->enableExtrapolation();
627 BOOST_CHECK_EQUAL_COLLECTIONS(cachedOptionletFixingDates.begin(), cachedOptionletFixingDates.end(),
628 pwos->optionletFixingDates().begin(), pwos->optionletFixingDates().end());
631 Matrix cached(cachedOptionletFixingDates.size(), cachedStrikes.size(), cachedValues.begin(), cachedValues.end());
632 BOOST_TEST_MESSAGE(
"Optionlet volatilities at the fixing dates");
633 BOOST_TEST_MESSAGE(
"date,strike,volatility,cached,diff");
634 for (Size i = 0; i < pwos->optionletFixingDates().size(); i++) {
635 for (Size j = 0; j < cachedStrikes.size(); j++) {
636 Date d = pwos->optionletFixingDates()[i];
637 Rate s = cachedStrikes[j];
638 Volatility v = ovs->volatility(d, s);
639 Volatility diff = fabs(v - cached[i][j]);
640 BOOST_CHECK_SMALL(diff, tolerance);
641 BOOST_TEST_MESSAGE(io::iso_date(d) <<
"," << s <<
"," << fixed << setprecision(12) << v <<
","
642 << cached[i][j] <<
"," << diff);
647 cached = Matrix(cachedAdHocDates.size(), cachedStrikes.size(), cachedAdHocValues.begin(), cachedAdHocValues.end());
648 BOOST_TEST_MESSAGE(
"Optionlet volatilities at the ad-hoc dates");
649 BOOST_TEST_MESSAGE(
"date,strike,volatility,cached,diff");
650 for (Size i = 0; i < cachedAdHocDates.size(); i++) {
651 for (Size j = 0; j < cachedStrikes.size(); j++) {
652 Date d = cachedAdHocDates[i];
653 Rate s = cachedStrikes[j];
654 Volatility v = ovs->volatility(d, s);
655 Volatility diff = fabs(v - cached[i][j]);
656 BOOST_CHECK_SMALL(diff, tolerance);
657 BOOST_TEST_MESSAGE(io::iso_date(d) <<
"," << s <<
"," << fixed << setprecision(12) << v <<
","
658 << cached[i][j] <<
"," << diff);
665 BOOST_TEST_MESSAGE(
"Testing changing the input cap floor surface");
668 Size lastTenorIdx = testVols.tenors.size() - 1;
669 Size lastStrikeIdx = testVols.strikes.size() - 1;
671 vector<Period> tenors = list_of(testVols.tenors[0])(testVols.tenors[lastTenorIdx]);
672 vector<Rate>
strikes = list_of(testVols.strikes[0])(testVols.strikes[lastStrikeIdx]);
674 vector<vector<QuantLib::ext::shared_ptr<SimpleQuote> > > quotes(2);
675 quotes[0].push_back(QuantLib::ext::make_shared<SimpleQuote>(testVols.nVols[0][0]));
676 quotes[0].push_back(QuantLib::ext::make_shared<SimpleQuote>(testVols.nVols[0][lastStrikeIdx]));
677 quotes[1].push_back(QuantLib::ext::make_shared<SimpleQuote>(testVols.nVols[lastTenorIdx][0]));
678 quotes[1].push_back(QuantLib::ext::make_shared<SimpleQuote>(testVols.nVols[lastTenorIdx][lastStrikeIdx]));
680 vector<vector<Handle<Quote> > > quoteHs(2);
681 quoteHs[0].push_back(Handle<Quote>(quotes[0][0]));
682 quoteHs[0].push_back(Handle<Quote>(quotes[0][1]));
683 quoteHs[1].push_back(Handle<Quote>(quotes[1][0]));
684 quoteHs[1].push_back(Handle<Quote>(quotes[1][1]));
687 QuantLib::ext::shared_ptr<TermVolSurface> cfts = QuantLib::ext::make_shared<TermVolSurface>(
691 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> pwos = QuantLib::ext::make_shared<PiecewiseOptionletStripper<LinearFlat> >(
692 cfts, iborIndex, testYieldCurves.discountEonia,
true, Normal, 0.0, Normal);
695 QuantLib::ext::shared_ptr<OptionletVolatilityStructure> ovs =
696 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, LinearFlat> >(pwos);
697 ovs->enableExtrapolation();
700 Date testDate = pwos->optionletFixingDates().back();
701 Volatility initialVol = ovs->volatility(testDate,
strikes[0]);
702 BOOST_TEST_MESSAGE(
"Test vol before shift is: " << fixed << setprecision(12) << initialVol);
703 Volatility expInitialVol = 0.007941492816;
704 BOOST_CHECK_SMALL(fabs(expInitialVol - initialVol), tolerance);
708 quotes[1][0]->setValue(shift * quotes[1][0]->value());
709 Volatility shiftedVol = ovs->volatility(testDate,
strikes[0]);
710 BOOST_TEST_MESSAGE(
"Test vol after shift is: " << fixed << setprecision(12) << shiftedVol);
711 Volatility expShiftedVol = 0.008926338986;
712 BOOST_CHECK_SMALL(fabs(expShiftedVol - shiftedVol), tolerance);
715 quotes[1][0]->setValue(quotes[1][0]->value() / shift);
716 initialVol = ovs->volatility(testDate,
strikes[0]);
717 BOOST_TEST_MESSAGE(
"Test vol after reset is: " << fixed << setprecision(12) << initialVol);
718 BOOST_CHECK_SMALL(fabs(expInitialVol - initialVol), tolerance);
721 Date newDate = referenceDate + 1 * Weeks;
722 Settings::instance().evaluationDate() = newDate;
726 BOOST_CHECK_EQUAL(ovs->referenceDate(), newDate);
730 Date newLastOptionletDate = pwos->optionletFixingDates().back();
731 BOOST_TEST_MESSAGE(
"Last fixing date moved from " << io::iso_date(testDate) <<
" to "
732 << io::iso_date(newLastOptionletDate));
733 BOOST_CHECK(newLastOptionletDate > testDate);
736 Volatility newVol = ovs->volatility(testDate,
strikes[0]);
737 BOOST_TEST_MESSAGE(
"Test vol after moving evaluation date is: " << fixed << setprecision(12) << newVol);
738 Volatility expNewVol = 0.007932365669;
739 BOOST_CHECK_SMALL(fabs(expNewVol - newVol), tolerance);
742BOOST_AUTO_TEST_SUITE_END()
744BOOST_AUTO_TEST_SUITE_END()
Helper for bootstrapping optionlet volatilities from cap floor volatilities.
structs containing capfloor market data that can be used in tests
Cap floor at-the-money term volatility curve.
Cap/floor smile volatility surface.
Cubic interpolation and flat extrapolation factory and traits.
Linear-interpolation and flat extrapolation factory and traits
Optionlet stripper that amends existing stripped optionlets to incorporate ATM cap floor volatilities...
BOOST_FIXTURE_TEST_CASE(testExtrapolation, CommonVars)
BOOST_DATA_TEST_CASE_F(CommonVars, testPiecewiseOptionletSurfaceStripping, bdata::make(volatilityTypes) *bdata::make(timeInterpolationTypes) *bdata::make(smileInterpolationTypes) *bdata::make(flatFirstPeriodValues) *bdata::make(isMovingValues) *bdata::make(vsInterpMethods) *bdata::make(interpOnOptionletValues) *bdata::make(addAtmValues), volatilityType, timeInterp, smileInterp, flatFirstPeriod, isMoving, vsInterpMethod, interpOnOptionlet, addAtm)
QuantLib::BootstrapHelper< QuantLib::OptionletVolatilityStructure > helper
Strip optionlet volatility surface from cap floor volatility term surface.
Convert a StrippedOptionletBase in to an OptionletVolatilityStructure.
Fixture that can be used at top level.
structs containing yield curve market data that can be used in tests