Build optionlet surface from term vol.
305 {
306
307
308 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> cftvs =
capSurface(asof, config, loader);
309
310
311 bool includeAtm = config.includeAtm();
312 Handle<QuantExt::CapFloorTermVolCurve> cftvc;
313 if (includeAtm) {
314 cftvc = Handle<QuantExt::CapFloorTermVolCurve>(
atmCurve(asof, config, loader));
315 }
316
317
318 bool flatFirstPeriod = true;
319 VolatilityType optVolType = Normal;
320 Real optDisplacement = 0.0;
321
322
323 Real accuracy = config.bootstrapConfig().accuracy();
324 Real globalAccuracy = config.bootstrapConfig().globalAccuracy();
325 bool dontThrow = config.bootstrapConfig().dontThrow();
326 Size maxAttempts = config.bootstrapConfig().maxAttempts();
327 Real maxFactor = config.bootstrapConfig().maxFactor();
328 Real minFactor = config.bootstrapConfig().minFactor();
329 Size dontThrowSteps = config.bootstrapConfig().dontThrowSteps();
330
331
332 std::vector<std::vector<std::pair<Real, bool>>> initialModelParameters;
333 Size maxCalibrationAttempts = 10;
334 Real exitEarlyErrorThreshold = 0.005;
335 Real maxAcceptableError = 0.05;
336 if (config.parametricSmileConfiguration()) {
337 auto alpha = config.parametricSmileConfiguration()->parameter("alpha");
338 auto beta = config.parametricSmileConfiguration()->parameter("beta");
339 auto nu = config.parametricSmileConfiguration()->parameter("nu");
340 auto rho = config.parametricSmileConfiguration()->parameter("rho");
341 QL_REQUIRE(alpha.initialValue.size() == beta.initialValue.size() &&
342 alpha.initialValue.size() == nu.initialValue.size() &&
343 alpha.initialValue.size() == rho.initialValue.size(),
344 "CapFloorVolCurve: parametric smile config: alpha size ("
345 << alpha.initialValue.size() << ") beta size (" << beta.initialValue.size() << ") nu size ("
346 << nu.initialValue.size() << ") rho size (" << rho.initialValue.size() << ") must match");
347 for (Size i = 0; i < alpha.initialValue.size(); ++i) {
348 initialModelParameters.push_back(std::vector<std::pair<Real, bool>>());
349 initialModelParameters.back().push_back(std::make_pair(alpha.initialValue[i], alpha.isFixed));
350 initialModelParameters.back().push_back(std::make_pair(beta.initialValue[i], beta.isFixed));
351 initialModelParameters.back().push_back(std::make_pair(nu.initialValue[i], nu.isFixed));
352 initialModelParameters.back().push_back(std::make_pair(rho.initialValue[i], rho.isFixed));
353 }
354 maxCalibrationAttempts = config.parametricSmileConfiguration()->calibration().maxCalibrationAttempts;
355 exitEarlyErrorThreshold = config.parametricSmileConfiguration()->calibration().exitEarlyErrorThreshold;
356 maxAcceptableError = config.parametricSmileConfiguration()->calibration().maxAcceptableError;
357 }
358
359
360 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> optionletStripper;
363 SabrParametricVolatility::ModelVariant sabrModelVariant;
364 if (onOpt) {
365
366 if (config.timeInterpolation() == "Linear") {
367 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<Linear>>(
368 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
369 Linear(),
372 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
373 config.rateComputationPeriod(), config.onCapSettlementDays());
374 if (config.strikeInterpolation() == "Linear") {
375 if (includeAtm) {
376 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, Linear>>(
377 optionletStripper, cftvc, discountCurve, volType, shift);
378 }
379 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, Linear>>(
381 } else if (config.strikeInterpolation() == "LinearFlat") {
382 if (includeAtm) {
383 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, LinearFlat>>(
384 optionletStripper, cftvc, discountCurve, volType, shift);
385 }
386 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, LinearFlat>>(
388 } else if (config.strikeInterpolation() == "Cubic") {
389 if (includeAtm) {
390 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, Cubic>>(
391 optionletStripper, cftvc, discountCurve, volType, shift);
392 }
393 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, Cubic>>(
395 } else if (config.strikeInterpolation() == "CubicFlat") {
396 if (includeAtm) {
397 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, CubicFlat>>(
398 optionletStripper, cftvc, discountCurve, volType, shift);
399 }
400 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, CubicFlat>>(
403 config.strikeInterpolation(), sabrModelVariant,
404 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
405 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
406 if (includeAtm) {
407 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, Linear>>(
408 optionletStripper, cftvc, discountCurve, volType, shift);
409 }
410 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<Linear>>(
411 asof,
transform(*optionletStripper), sabrModelVariant, Linear(), boost::none,
412 initialModelParameters, maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
413 } else {
414 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected strike interpolation "
415 << config.strikeInterpolation());
416 }
417 } else if (config.timeInterpolation() == "LinearFlat") {
418 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<LinearFlat>>(
419 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
423 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
424 config.rateComputationPeriod(), config.onCapSettlementDays());
425 if (config.strikeInterpolation() == "Linear") {
426 if (includeAtm) {
427 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, Linear>>(
428 optionletStripper, cftvc, discountCurve, volType, shift);
429 }
430 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, Linear>>(
432 } else if (config.strikeInterpolation() == "LinearFlat") {
433 if (includeAtm) {
434 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, LinearFlat>>(
435 optionletStripper, cftvc, discountCurve, volType, shift);
436 }
437 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, LinearFlat>>(
439 } else if (config.strikeInterpolation() == "Cubic") {
440 if (includeAtm) {
441 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, Cubic>>(
442 optionletStripper, cftvc, discountCurve, volType, shift);
443 }
444 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, Cubic>>(
446 } else if (config.strikeInterpolation() == "CubicFlat") {
447 if (includeAtm) {
448 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, CubicFlat>>(
449 optionletStripper, cftvc, discountCurve, volType, shift);
450 }
451 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, CubicFlat>>(
454 config.strikeInterpolation(), sabrModelVariant,
455 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
456 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
457 if (includeAtm) {
458 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, Linear>>(
459 optionletStripper, cftvc, discountCurve, volType, shift);
460 }
461 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<LinearFlat>>(
463 initialModelParameters, maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
464 } else {
465 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected strike interpolation "
466 << config.strikeInterpolation());
467 }
468 } else if (config.timeInterpolation() == "BackwardFlat") {
469 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<BackwardFlat>>(
470 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
471 BackwardFlat(),
474 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
475 config.rateComputationPeriod(), config.onCapSettlementDays());
476 if (config.strikeInterpolation() == "Linear") {
477 if (includeAtm) {
478 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<BackwardFlat, Linear>>(
479 optionletStripper, cftvc, discountCurve, volType, shift);
480 }
481 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, Linear>>(
483 } else if (config.strikeInterpolation() == "LinearFlat") {
484 if (includeAtm) {
485 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<BackwardFlat, LinearFlat>>(
486 optionletStripper, cftvc, discountCurve, volType, shift);
487 }
488 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, LinearFlat>>(
490 } else if (config.strikeInterpolation() == "Cubic") {
491 if (includeAtm) {
492 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<BackwardFlat, Cubic>>(
493 optionletStripper, cftvc, discountCurve, volType, shift);
494 }
495 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, Cubic>>(
497 } else if (config.strikeInterpolation() == "CubicFlat") {
498 if (includeAtm) {
499 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<BackwardFlat, CubicFlat>>(
500 optionletStripper, cftvc, discountCurve, volType, shift);
501 }
502 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, CubicFlat>>(
505 config.strikeInterpolation(), sabrModelVariant,
506 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
507 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
508 if (includeAtm) {
509 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<BackwardFlat, Linear>>(
510 optionletStripper, cftvc, discountCurve, volType, shift);
511 }
512 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<Linear>>(
513 asof,
transform(*optionletStripper), sabrModelVariant, Linear(), boost::none,
514 initialModelParameters, maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
515 } else {
516 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected strike interpolation "
517 << config.strikeInterpolation());
518 }
519 } else if (config.timeInterpolation() == "Cubic") {
520 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<Cubic>>(
521 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
522 Cubic(),
525 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
526 config.rateComputationPeriod(), config.onCapSettlementDays());
527 if (config.strikeInterpolation() == "Linear") {
528 if (includeAtm) {
529 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, Linear>>(
530 optionletStripper, cftvc, discountCurve, volType, shift);
531 }
532 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, Linear>>(
534 } else if (config.strikeInterpolation() == "LinearFlat") {
535 if (includeAtm) {
536 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, LinearFlat>>(
537 optionletStripper, cftvc, discountCurve, volType, shift);
538 }
539 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, LinearFlat>>(
541 } else if (config.strikeInterpolation() == "Cubic") {
542 if (includeAtm) {
543 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, Cubic>>(
544 optionletStripper, cftvc, discountCurve, volType, shift);
545 }
546 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, Cubic>>(
548 } else if (config.strikeInterpolation() == "CubicFlat") {
549 if (includeAtm) {
550 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, CubicFlat>>(
551 optionletStripper, cftvc, discountCurve, volType, shift);
552 }
553 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, CubicFlat>>(
556 config.strikeInterpolation(), sabrModelVariant,
557 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
558 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
559 if (includeAtm) {
560 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, Linear>>(
561 optionletStripper, cftvc, discountCurve, volType, shift);
562 }
563 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<Cubic>>(
564 asof,
transform(*optionletStripper), sabrModelVariant, Cubic(), boost::none, initialModelParameters,
565 maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
566 }
567 {
568 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected strike interpolation "
569 << config.strikeInterpolation());
570 }
571 } else if (config.timeInterpolation() == "CubicFlat") {
572 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<CubicFlat>>(
573 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
577 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
578 config.rateComputationPeriod(), config.onCapSettlementDays());
579 if (config.strikeInterpolation() == "Linear") {
580 if (includeAtm) {
581 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, Linear>>(
582 optionletStripper, cftvc, discountCurve, volType, shift);
583 }
584 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, Linear>>(
586 } else if (config.strikeInterpolation() == "LinearFlat") {
587 if (includeAtm) {
588 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, LinearFlat>>(
589 optionletStripper, cftvc, discountCurve, volType, shift);
590 }
591 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, LinearFlat>>(
593 } else if (config.strikeInterpolation() == "Cubic") {
594 if (includeAtm) {
595 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, Cubic>>(
596 optionletStripper, cftvc, discountCurve, volType, shift);
597 }
598 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, Cubic>>(
600 } else if (config.strikeInterpolation() == "CubicFlat") {
601 if (includeAtm) {
602 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, CubicFlat>>(
603 optionletStripper, cftvc, discountCurve, volType, shift);
604 }
605 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, CubicFlat>>(
608 config.strikeInterpolation(), sabrModelVariant,
609 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
610 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
611 if (includeAtm) {
612 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, Linear>>(
613 optionletStripper, cftvc, discountCurve, volType, shift);
614 }
615 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<CubicFlat>>(
617 initialModelParameters, maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
618 } else {
619 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected strike interpolation "
620 << config.strikeInterpolation());
621 }
622 } else {
623 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected time interpolation "
624 << config.timeInterpolation());
625 }
626 } else {
627
628
629 if (config.interpolationMethod() == CftvsInterp::BicubicSpline) {
630 if (config.flatExtrapolation()) {
631 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<CubicFlat>>(
632 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
636 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
637 config.rateComputationPeriod(), config.onCapSettlementDays());
638 if (includeAtm) {
639 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, CubicFlat>>(
640 optionletStripper, cftvc, discountCurve, volType, shift);
641 }
642 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, CubicFlat>>(
644 } else {
645 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<Cubic>>(
646 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
647 Cubic(),
650 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
651 config.rateComputationPeriod(), config.onCapSettlementDays());
652 if (includeAtm) {
653 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, Cubic>>(
654 optionletStripper, cftvc, discountCurve, volType, shift);
655 }
656 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, Cubic>>(
658 }
659 } else if (config.interpolationMethod() == CftvsInterp::Bilinear) {
660 if (config.flatExtrapolation()) {
661 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<LinearFlat>>(
662 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
666 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
667 config.rateComputationPeriod(), config.onCapSettlementDays());
668 if (includeAtm) {
669 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, LinearFlat>>(
670 optionletStripper, cftvc, discountCurve, volType, shift);
671 }
672 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, LinearFlat>>(
674 } else {
675 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<Linear>>(
676 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
677 Linear(),
680 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
681 config.rateComputationPeriod(), config.onCapSettlementDays());
682 if (includeAtm) {
683 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, Linear>>(
684 optionletStripper, cftvc, discountCurve, volType, shift);
685 }
686 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, Linear>>(
688 }
689 } else {
690 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected interpolation method "
691 << static_cast<int>(config.interpolationMethod()));
692 }
693 }
694}
QuantLib::ext::shared_ptr< QuantExt::CapFloorTermVolSurface > capSurface(const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, const Loader &loader) const
Build a cap floor term volatility surface.
bool tryParse(const std::string &str, T &obj, std::function< T(const std::string &)> parser)