Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Public Types | Public Member Functions | Private Member Functions | Private Attributes | Static Private Attributes | List of all members
SabrParametricVolatility Class Referencefinal

#include <qle/termstructures/sabrparametricvolatility.hpp>

+ Inheritance diagram for SabrParametricVolatility:
+ Collaboration diagram for SabrParametricVolatility:

Public Types

enum class  ModelVariant {
  Hagan2002Lognormal = 0 , Hagan2002Normal = 1 , Hagan2002NormalZeroBeta = 2 , Antonov2015FreeBoundaryNormal = 3 ,
  KienitzLawsonSwaynePde = 4 , FlochKennedy = 5
}
 
- Public Types inherited from ParametricVolatility
enum class  MarketModelType { Black76 }
 
enum class  MarketQuoteType { Price , NormalVolatility , ShiftedLognormalVolatility }
 

Public Member Functions

 SabrParametricVolatility (const ModelVariant modelVariant, const std::vector< MarketSmile > marketSmiles, const MarketModelType marketModelType, const MarketQuoteType inputMarketQuoteType, const QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve, const std::map< std::pair< QuantLib::Real, QuantLib::Real >, std::vector< std::pair< Real, bool > > > modelParameters={}, const QuantLib::Size maxCalibrationAttempts=10, const QuantLib::Real exitEarlyErrorThreshold=0.005, const QuantLib::Real maxAcceptableError=0.05)
 
QuantLib::Real evaluate (const QuantLib::Real timeToExpiry, const QuantLib::Real underlyingLength, const QuantLib::Real strike, const QuantLib::Real forward, const MarketQuoteType outputMarketQuoteType, const QuantLib::Real outputLognormalShift=QuantLib::Null< QuantLib::Real >(), const boost::optional< QuantLib::Option::Type > outputOptionType=boost::none) const override
 
const std::vector< Real > & timeToEpiries () const
 
const std::vector< Real > & underlyingLenghts () const
 
const QuantLib::Matrix & alpha () const
 
const QuantLib::Matrix & beta () const
 
const QuantLib::Matrix & nu () const
 
const QuantLib::Matrix & rho () const
 
const QuantLib::Matrix & lognormalShift () const
 
const QuantLib::Matrix & numberOfCalibrationAttempts () const
 
const QuantLib::Matrix & calibrationError () const
 
const QuantLib::Matrix & isInterpolated () const
 
- Public Member Functions inherited from ParametricVolatility
virtual ~ParametricVolatility ()
 
 ParametricVolatility (const std::vector< MarketSmile > marketSmiles, const MarketModelType marketModelType, const MarketQuoteType inputMarketQuoteType, const QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve)
 
Real convert (const Real inputQuote, const MarketQuoteType inputMarketQuoteType, const QuantLib::Real inputLognormalShift, const boost::optional< QuantLib::Option::Type > inputOptionType, const QuantLib::Real timeToExpiry, const QuantLib::Real strike, const QuantLib::Real forward, const MarketQuoteType outputMarketQuoteType, const QuantLib::Real outputLognormalShift, const boost::optional< QuantLib::Option::Type > outputOptionType=boost::none) const
 
virtual QuantLib::Real evaluate (const QuantLib::Real timeToExpiry, const QuantLib::Real underlyingLength, const QuantLib::Real strike, const QuantLib::Real forward, const MarketQuoteType outputMarketQuoteType, const QuantLib::Real outputLognormalShift=QuantLib::Null< QuantLib::Real >(), const boost::optional< QuantLib::Option::Type > outputOptionType=boost::none) const =0
 

Private Member Functions

void calculate ()
 
std::vector< std::pair< Real, bool > > defaultModelParameters () const
 
std::vector< Real > getGuess (const std::vector< std::pair< Real, bool > > &params, const std::vector< Real > &randomSeq, const Real forward, const Real lognormalShift) const
 
ParametricVolatility::MarketQuoteType preferredOutputQuoteType () const
 
std::vector< Real > direct (const std::vector< Real > &x, const Real forward, const Real lognormalShift) const
 
std::vector< Real > inverse (const std::vector< Real > &y, const Real forward, const Real lognormalShift) const
 
std::vector< Real > evaluateSabr (const std::vector< Real > &params, const Real forward, const Real timeToExpiry, const Real lognormalShift, const std::vector< Real > &strikes) const
 
std::tuple< std::vector< Real >, Real, QuantLib::Size > calibrateModelParameters (const MarketSmile &marketSmile, const std::vector< std::pair< Real, bool > > &params) const
 

Private Attributes

ModelVariant modelVariant_
 
std::map< std::pair< QuantLib::Real, QuantLib::Real >, std::vector< std::pair< Real, bool > > > modelParameters_
 
QuantLib::Size maxCalibrationAttempts_
 
QuantLib::Real exitEarlyErrorThreshold_
 
QuantLib::Real maxAcceptableError_
 
std::map< std::pair< Real, Real >, std::vector< Real > > calibratedSabrParams_
 
std::map< std::pair< Real, Real >, Real > lognormalShifts_
 
std::map< std::pair< Real, Real >, Real > calibrationErrors_
 
std::map< std::pair< Real, Real >, QuantLib::Size > noOfAttempts_
 
std::vector< Real > underlyingLengths_
 
std::vector< Real > timeToExpiries_
 
std::vector< Real > underlyingLengthsForInterpolation_
 
std::vector< Real > timeToExpiriesForInterpolation_
 
QuantLib::Matrix alpha_
 
QuantLib::Matrix beta_
 
QuantLib::Matrix nu_
 
QuantLib::Matrix rho_
 
QuantLib::Matrix lognormalShift_
 
QuantLib::Matrix calibrationError_
 
QuantLib::Matrix isInterpolated_
 
QuantLib::Matrix numberOfCalibrationAttempts_
 
QuantLib::Interpolation2D alphaInterpolation_
 
QuantLib::Interpolation2D betaInterpolation_
 
QuantLib::Interpolation2D nuInterpolation_
 
QuantLib::Interpolation2D rhoInterpolation_
 
QuantLib::Interpolation2D lognormalShiftInterpolation_
 

Static Private Attributes

static constexpr double eps1 = .0000001
 
static constexpr double eps2 = .9999
 
static constexpr double max_nvol_equiv = 0.02
 
static constexpr double max_nu = 2.0
 

Additional Inherited Members

- Protected Attributes inherited from ParametricVolatility
std::vector< MarketSmilemarketSmiles_
 
MarketModelType marketModelType_
 
MarketQuoteType inputMarketQuoteType_
 
QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve_
 

Detailed Description

Definition at line 31 of file sabrparametricvolatility.hpp.

Member Enumeration Documentation

◆ ModelVariant

enum class ModelVariant
strong
Enumerator
Hagan2002Lognormal 
Hagan2002Normal 
Hagan2002NormalZeroBeta 
Antonov2015FreeBoundaryNormal 
KienitzLawsonSwaynePde 
FlochKennedy 

Definition at line 33 of file sabrparametricvolatility.hpp.

Constructor & Destructor Documentation

◆ SabrParametricVolatility()

SabrParametricVolatility ( const ModelVariant  modelVariant,
const std::vector< MarketSmile marketSmiles,
const MarketModelType  marketModelType,
const MarketQuoteType  inputMarketQuoteType,
const QuantLib::Handle< QuantLib::YieldTermStructure >  discountCurve,
const std::map< std::pair< QuantLib::Real, QuantLib::Real >, std::vector< std::pair< Real, bool > > >  modelParameters = {},
const QuantLib::Size  maxCalibrationAttempts = 10,
const QuantLib::Real  exitEarlyErrorThreshold = 0.005,
const QuantLib::Real  maxAcceptableError = 0.05 
)

modelParameters are given by (tte, underlyingLen) as a vector of parameter values and whether the values are fixed

Definition at line 36 of file sabrparametricvolatility.cpp.

41 : ParametricVolatility(marketSmiles, marketModelType, inputMarketQuoteType, discountCurve),
42 modelVariant_(modelVariant), modelParameters_(std::move(modelParameters)),
43 maxCalibrationAttempts_(maxCalibrationAttempts), exitEarlyErrorThreshold_(exitEarlyErrorThreshold),
44 maxAcceptableError_(maxAcceptableError) {
45 calculate();
46}
ParametricVolatility(const std::vector< MarketSmile > marketSmiles, const MarketModelType marketModelType, const MarketQuoteType inputMarketQuoteType, const QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve)
std::map< std::pair< QuantLib::Real, QuantLib::Real >, std::vector< std::pair< Real, bool > > > modelParameters_
+ Here is the call graph for this function:

Member Function Documentation

◆ evaluate()

Real evaluate ( const QuantLib::Real  timeToExpiry,
const QuantLib::Real  underlyingLength,
const QuantLib::Real  strike,
const QuantLib::Real  forward,
const MarketQuoteType  outputMarketQuoteType,
const QuantLib::Real  outputLognormalShift = QuantLib::Null<QuantLib::Real>(),
const boost::optional< QuantLib::Option::Type >  outputOptionType = boost::none 
) const
overridevirtual

Implements ParametricVolatility.

Definition at line 602 of file sabrparametricvolatility.cpp.

605 {
606
607 Real alpha = alphaInterpolation_(timeToExpiry, underlyingLength);
608 Real beta = betaInterpolation_(timeToExpiry, underlyingLength);
609 Real nu = nuInterpolation_(timeToExpiry, underlyingLength);
610 Real rho = rhoInterpolation_(timeToExpiry, underlyingLength);
611 Real lognormalShift = lognormalShiftInterpolation_(timeToExpiry, underlyingLength);
612
613 Real result = evaluateSabr({alpha, beta, nu, rho}, forward, timeToExpiry, lognormalShift, {strike}).front();
614 return convert(result, preferredOutputQuoteType(), lognormalShift, boost::none, timeToExpiry, strike, forward,
615 outputMarketQuoteType, outputLognormalShift == Null<Real>() ? lognormalShift : outputLognormalShift,
616 outputOptionType);
617}
Real convert(const Real inputQuote, const MarketQuoteType inputMarketQuoteType, const QuantLib::Real inputLognormalShift, const boost::optional< QuantLib::Option::Type > inputOptionType, const QuantLib::Real timeToExpiry, const QuantLib::Real strike, const QuantLib::Real forward, const MarketQuoteType outputMarketQuoteType, const QuantLib::Real outputLognormalShift, const boost::optional< QuantLib::Option::Type > outputOptionType=boost::none) const
const QuantLib::Matrix & rho() const
QuantLib::Interpolation2D lognormalShiftInterpolation_
const QuantLib::Matrix & alpha() const
std::vector< Real > evaluateSabr(const std::vector< Real > &params, const Real forward, const Real timeToExpiry, const Real lognormalShift, const std::vector< Real > &strikes) const
const QuantLib::Matrix & beta() const
const QuantLib::Matrix & lognormalShift() const
const QuantLib::Matrix & nu() const
ParametricVolatility::MarketQuoteType preferredOutputQuoteType() const
+ Here is the call graph for this function:

◆ timeToEpiries()

const std::vector< Real > & timeToEpiries ( ) const

◆ underlyingLenghts()

const std::vector< Real > & underlyingLenghts ( ) const

◆ alpha()

const QuantLib::Matrix & alpha ( ) const

Definition at line 63 of file sabrparametricvolatility.hpp.

+ Here is the caller graph for this function:

◆ beta()

const QuantLib::Matrix & beta ( ) const

Definition at line 64 of file sabrparametricvolatility.hpp.

+ Here is the caller graph for this function:

◆ nu()

const QuantLib::Matrix & nu ( ) const

Definition at line 65 of file sabrparametricvolatility.hpp.

+ Here is the caller graph for this function:

◆ rho()

const QuantLib::Matrix & rho ( ) const

Definition at line 66 of file sabrparametricvolatility.hpp.

+ Here is the caller graph for this function:

◆ lognormalShift()

const QuantLib::Matrix & lognormalShift ( ) const

Definition at line 67 of file sabrparametricvolatility.hpp.

+ Here is the caller graph for this function:

◆ numberOfCalibrationAttempts()

const QuantLib::Matrix & numberOfCalibrationAttempts ( ) const

Definition at line 68 of file sabrparametricvolatility.hpp.

◆ calibrationError()

const QuantLib::Matrix & calibrationError ( ) const

Definition at line 70 of file sabrparametricvolatility.hpp.

◆ isInterpolated()

const QuantLib::Matrix & isInterpolated ( ) const

Definition at line 72 of file sabrparametricvolatility.hpp.

◆ calculate()

void calculate ( )
private

Definition at line 426 of file sabrparametricvolatility.cpp.

426 {
427
428 // if no model parameters are given, we provide the default ones
429
430 if (modelParameters_.empty()) {
431 for (auto const& s : marketSmiles_) {
432 modelParameters_[std::make_pair(s.timeToExpiry, s.underlyingLength)] = defaultModelParameters();
433 }
434 }
435
436 // clear stored data
437
438 calibratedSabrParams_.clear();
439 lognormalShifts_.clear();
440 calibrationErrors_.clear();
441
442 // for each market smile calibrate the SABR variant
443
444 for (auto const& s : marketSmiles_) {
445 auto key = std::make_pair(s.timeToExpiry, s.underlyingLength);
446 auto param = modelParameters_.find(key);
447 QL_REQUIRE(param != modelParameters_.end(),
448 "SabrParametricVolatility::performCalculations(): no model parameter given for ("
449 << s.timeToExpiry << ", " << s.underlyingLength
450 << "). All (timeToExpiry, underlyingLength) pairs that are given as market points must be "
451 "covered by the given model parameters.");
452 try {
453 auto [params, error, noOfAttempts] = calibrateModelParameters(s, param->second);
454 if (error < maxAcceptableError_)
455 calibratedSabrParams_[key] = params;
456 calibrationErrors_[key] = error;
457 noOfAttempts_[key] = noOfAttempts;
458 } catch (const std::exception& e) {
459 // all calibration failed -> do not populate params, but interpolate them below
460 }
461 lognormalShifts_[key] = s.lognormalShift;
462 }
463
464 // build the timeToExpiry, underlyingLength vectors
465
466 std::set<Real> tmpTimeToExpiries, tmpUnderlyingLengths;
467
468 for (auto const& s : marketSmiles_) {
469 tmpTimeToExpiries.insert(s.timeToExpiry);
470 tmpUnderlyingLengths.insert(s.underlyingLength);
471 }
472
473 timeToExpiries_ = std::vector<Real>(tmpTimeToExpiries.begin(), tmpTimeToExpiries.end());
474 underlyingLengths_ = std::vector<Real>(tmpUnderlyingLengths.begin(), tmpUnderlyingLengths.end());
475
476 // build a matrix of calibrated SABR parameters, possibly with null values
477
478 Size m = underlyingLengths_.size();
479 Size n = timeToExpiries_.size();
480
481 alpha_ = Matrix(m, n, Null<Real>());
482 beta_ = Matrix(m, n, Null<Real>());
483 nu_ = Matrix(m, n, Null<Real>());
484 rho_ = Matrix(m, n, Null<Real>());
485 lognormalShift_ = Matrix(m, n, Null<Real>());
486 calibrationError_ = Matrix(m, n, Null<Real>());
487 isInterpolated_ = Matrix(m, n, 1.0);
488 numberOfCalibrationAttempts_ = Matrix(m, n, 0.0);
489
490 for (Size i = 0; i < m; ++i) {
491 for (Size j = 0; j < n; ++j) {
492 auto key = std::make_pair(timeToExpiries_[j], underlyingLengths_[i]);
493 if (auto p = calibratedSabrParams_.find(key); p != calibratedSabrParams_.end()) {
494 alpha_(i, j) = p->second[0];
495 beta_(i, j) = p->second[1];
496 nu_(i, j) = p->second[2];
497 rho_(i, j) = p->second[3];
498 isInterpolated_(i, j) = 0.0;
499 }
500 if (auto s = lognormalShifts_.find(key); s != lognormalShifts_.end()) {
501 lognormalShift_(i, j) = s->second;
502 }
503 if (auto e = calibrationErrors_.find(key); e != calibrationErrors_.end()) {
504 calibrationError_(i, j) = e->second;
505 }
506 if (auto e = noOfAttempts_.find(key); e != noOfAttempts_.end()) {
507 numberOfCalibrationAttempts_(i, j) = static_cast<Real>(e->second);
508 }
509 }
510 }
511
512 // interpolate the null values
513
514 laplaceInterpolationWithErrorHandling(alpha_, timeToExpiries_, underlyingLengths_);
515 laplaceInterpolationWithErrorHandling(beta_, timeToExpiries_, underlyingLengths_);
516 laplaceInterpolationWithErrorHandling(nu_, timeToExpiries_, underlyingLengths_);
517 laplaceInterpolationWithErrorHandling(rho_, timeToExpiries_, underlyingLengths_);
518
519 // sanitize values produced by the the interpolation that are not allowed
520
521 for (Size i = 0; i < m; ++i) {
522 for (Size j = 0; j < n; ++j) {
523 alpha_(i, j) = std::max(alpha_(i, j), 0.0);
524 beta_(i, j) = std::max(beta_(i, j), 0.0);
525 nu_(i, j) = std::max(nu_(i, j), 0.0);
526 rho_(i, j) = std::max(std::min(rho_(i, j), 1.0), -1.0);
527 }
528 }
529
530 // workaround because BilinearInterpolation below requires at least two points in each dimension
531
534
535 if (m == 1 || n == 1) {
536
537 auto mNew = m == 1 ? m + 1 : m;
538 auto nNew = n == 1 ? n + 1 : n;
539
540 auto alphaTmp = alpha_;
541 auto betaTmp = beta_;
542 auto nuTmp = nu_;
543 auto rhoTmp = rho_;
544 auto lognormalShiftTmp = lognormalShift_;
545
546 alpha_ = Matrix(mNew, nNew, Null<Real>());
547 beta_ = Matrix(mNew, nNew, Null<Real>());
548 nu_ = Matrix(mNew, nNew, Null<Real>());
549 rho_ = Matrix(mNew, nNew, Null<Real>());
550 lognormalShift_ = Matrix(mNew, nNew, Null<Real>());
551
552 for (Size i = 0; i < mNew; ++i) {
553 for (Size j = 0; j < nNew; ++j) {
554 Size iOld = std::min(i, m - 1);
555 Size jOld = std::min(j, n - 1);
556 alpha_(i, j) = alphaTmp(iOld, jOld);
557 beta_(i, j) = betaTmp(iOld, jOld);
558 nu_(i, j) = nuTmp(iOld, jOld);
559 rho_(i, j) = rhoTmp(iOld, jOld);
560 lognormalShift_(i, j) = lognormalShiftTmp(iOld, jOld);
561 }
562 }
563
564 if (m == 1) {
565 // do not use null value for interpolation grid
566 if (underlyingLengthsForInterpolation_[0] == Null<Real>())
569 }
570 if (n == 1)
572
573 m = mNew;
574 n = nNew;
575 }
576
577 // set up the parameter interpolations
578
579 alphaInterpolation_ = FlatExtrapolator2D(boost::make_shared<BilinearInterpolation>(
582 betaInterpolation_ = FlatExtrapolator2D(boost::make_shared<BilinearInterpolation>(
585 nuInterpolation_ = FlatExtrapolator2D(boost::make_shared<BilinearInterpolation>(
588 rhoInterpolation_ = FlatExtrapolator2D(boost::make_shared<BilinearInterpolation>(
591 lognormalShiftInterpolation_ = FlatExtrapolator2D(boost::make_shared<BilinearInterpolation>(
594
595 alphaInterpolation_.enableExtrapolation();
596 betaInterpolation_.enableExtrapolation();
597 nuInterpolation_.enableExtrapolation();
598 rhoInterpolation_.enableExtrapolation();
599 lognormalShiftInterpolation_.enableExtrapolation();
600}
std::vector< MarketSmile > marketSmiles_
std::vector< std::pair< Real, bool > > defaultModelParameters() const
std::map< std::pair< Real, Real >, Real > calibrationErrors_
std::map< std::pair< Real, Real >, QuantLib::Size > noOfAttempts_
std::map< std::pair< Real, Real >, std::vector< Real > > calibratedSabrParams_
std::tuple< std::vector< Real >, Real, QuantLib::Size > calibrateModelParameters(const MarketSmile &marketSmile, const std::vector< std::pair< Real, bool > > &params) const
std::map< std::pair< Real, Real >, Real > lognormalShifts_
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ defaultModelParameters()

std::vector< std::pair< Real, bool > > defaultModelParameters ( ) const
private

Definition at line 103 of file sabrparametricvolatility.cpp.

103 {
104 switch (modelVariant_) {
106 return {{0.0050, false}, {0.8, false}, {0.30, false}, {0.0, false}};
108 return {{0.0050, false}, {0.8, false}, {0.30, false}, {0.0, false}};
110 return {{0.0050, false}, {0.0, true}, {0.30, false}, {0.0, false}};
112 return {{0.0050, false}, {0.0, true}, {0.30, false}, {0.0, false}};
114 return {{0.0050, false}, {0.8, false}, {0.30, false}, {0.0, false}};
116 return {{0.0050, false}, {0.8, false}, {0.30, false}, {0.0, false}};
117 default:
118 QL_FAIL("SabrParametricVolatility::defaultModelParameters(): model variant (" << static_cast<int>(modelVariant_)
119 << ") not handled.");
120 }
121}
+ Here is the caller graph for this function:

◆ getGuess()

std::vector< Real > getGuess ( const std::vector< std::pair< Real, bool > > &  params,
const std::vector< Real > &  randomSeq,
const Real  forward,
const Real  lognormalShift 
) const
private

Definition at line 68 of file sabrparametricvolatility.cpp.

70 {
71 std::vector<Real> result(4);
72 for (Size i = 0, j = 0; i < 4; ++i) {
73 if (params[i].second) {
74 result[i] = params[i].first;
75 } else {
76 switch (i) {
77 case 0: {
78 Real fbeta = std::pow(forward + lognormalShift, params[1].first);
79 result[0] = (eps1 + randomSeq[j] * 0.01) / fbeta;
80 break;
81 }
82 case 1: {
83 result[1] = eps1 + randomSeq[j] * eps2;
84 break;
85 }
86 case 2: {
87 result[2] = eps1 + randomSeq[j] * 5.0;
88 break;
89 }
90 case 3: {
91 result[3] = (randomSeq[j] * 2.0 - 1.0) * eps2;
92 break;
93 }
94 default:
95 break;
96 }
97 ++j;
98 }
99 }
100 return result;
101}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ preferredOutputQuoteType()

ParametricVolatility::MarketQuoteType preferredOutputQuoteType ( ) const
private

Definition at line 48 of file sabrparametricvolatility.cpp.

+ Here is the caller graph for this function:

◆ direct()

std::vector< Real > direct ( const std::vector< Real > &  x,
const Real  forward,
const Real  lognormalShift 
) const
private

Definition at line 123 of file sabrparametricvolatility.cpp.

124 {
125 std::vector<Real> y(4);
126 y[1] = std::max(eps1, std::min(1.0 - eps1, std::exp(-(x[1] * x[1]))));
127 Real fbeta = std::pow(std::max(forward + lognormalShift, eps1), y[1]);
128 y[0] = std::max(eps1, std::exp(-(x[0] * x[0])) / fbeta * max_nvol_equiv);
129 y[2] = std::max(eps1, std::exp(-(x[2] * x[2])) * max_nu);
130 y[3] = std::fabs(x[3]) < 2.5 * M_PI ? eps2 * std::sin(x[3]) : eps2 * (x[3] > 0.0 ? 1.0 : (-1.0));
131 return y;
132}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ inverse()

std::vector< Real > inverse ( const std::vector< Real > &  y,
const Real  forward,
const Real  lognormalShift 
) const
private

Definition at line 134 of file sabrparametricvolatility.cpp.

135 {
136 std::vector<Real> x(4);
137 x[1] = std::sqrt(-std::log(std::min(1.0 - eps1, std::max(eps1, y[1]))));
138 Real fbeta = std::pow(std::max(forward + lognormalShift, eps1), y[1]);
139 x[0] = std::sqrt(-std::log(std::min(1.0 - eps1, std::max(eps1, y[0] * fbeta / max_nvol_equiv))));
140 x[2] = std::sqrt(-std::log(std::min(1.0 - eps1, std::max(eps1, y[2] / max_nu))));
141 x[3] = std::asin(std::max(-eps2, std::min(eps2, y[3])));
142 return x;
143}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ evaluateSabr()

std::vector< Real > evaluateSabr ( const std::vector< Real > &  params,
const Real  forward,
const Real  timeToExpiry,
const Real  lognormalShift,
const std::vector< Real > &  strikes 
) const
private

Definition at line 145 of file sabrparametricvolatility.cpp.

147 {
148 std::vector<Real> result;
149 switch (modelVariant_) {
151 result.resize(strikes.size());
152 for (Size i = 0; i < strikes.size(); ++i) {
153 try {
154 if (strikes[i] < -lognormalShift || QuantLib::close_enough(strikes[i], 0.0))
155 result[i] = 0.0;
156 else
157 result[i] = unsafeSabrLogNormalVolatility(strikes[i] + lognormalShift, forward + lognormalShift,
158 timeToExpiry, params[0], params[1], params[2], params[3]);
159 } catch (...) {
160 result[i] = 0.0;
161 }
162 }
163 break;
164 }
166 result.resize(strikes.size());
167 for (Size i = 0; i < strikes.size(); ++i) {
168 try {
169 if (strikes[i] < -lognormalShift || QuantLib::close_enough(strikes[i], 0.0))
170 result[i] = 0.0;
171 else
172 result[i] = unsafeSabrNormalVolatility(strikes[i] + lognormalShift, forward + lognormalShift,
173 timeToExpiry, params[0], params[1], params[2], params[3]);
174 } catch (...) {
175 result[i] = 0.0;
176 }
177 }
178 break;
179 }
181 result.resize(strikes.size());
182 for (Size i = 0; i < strikes.size(); ++i) {
183 try {
184 result[i] =
185 QuantExt::normalSabrVolatility(strikes[i], forward, timeToExpiry, params[0], params[2], params[3]);
186 } catch (...) {
187 result[i] = 0.0;
188 }
189 }
190 break;
191 }
193 result.resize(strikes.size());
194 for (Size i = 0; i < strikes.size(); ++i) {
195 try {
196 result[i] = QuantExt::normalFreeBoundarySabrPrice(strikes[i], forward, timeToExpiry, params[0],
197 params[2], params[3]) *
198 (discountCurve_.empty() ? 1.0 : discountCurve_->discount(timeToExpiry));
199 if (strikes[i] < forward)
200 result[i] = result[i] - forward + strikes[i];
201 } catch (...) {
202 result[i] = 0.0;
203 }
204 }
205 break;
206 }
208 try {
209 KienitzLawsonSwayneSabrPdeDensity pde(params[0], params[1], params[2], params[3], forward, timeToExpiry,
210 lognormalShift, 50,
211 std::max<Size>(5, std::lround(24.0 * timeToExpiry + 0.5)), 5.0);
212 result = pde.callPrices(strikes);
213 for (Size i = 0; i < strikes.size(); ++i) {
214 if (strikes[i] < forward)
215 result[i] = result[i] - forward + strikes[i];
216 result[i] *= discountCurve_.empty() ? 1.0 : discountCurve_->discount(timeToExpiry);
217 }
218 } catch (...) {
219 result = std::vector<Real>(strikes.size(), 0.0);
220 }
221 break;
222 }
224 result.resize(strikes.size());
225 for (Size i = 0; i < strikes.size(); ++i) {
226 try {
227 if (strikes[i] < -lognormalShift || QuantLib::close_enough(strikes[i], 0.0))
228 result[i] = 0.0;
229 else
230 result[i] = sabrFlochKennedyVolatility(strikes[i] + lognormalShift, forward + lognormalShift,
231 timeToExpiry, params[0], params[1], params[2], params[3]);
232 } catch (...) {
233 result[i] = 0.0;
234 }
235 }
236 break;
237 }
238 default:
239 QL_FAIL("SabrParametricVolatility::preferredOutputQuoteType(): model variant ("
240 << static_cast<int>(modelVariant_) << ") not handled.");
241 }
242
243 // ensure we have a number, not inf or nan
244
245 for (auto& v : result)
246 if (!std::isfinite(v))
247 v = 0.0;
248
249 return result;
250}
QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve_
Real normalSabrVolatility(Rate strike, Rate forward, Time expiryTime, Real alpha, Real nu, Real rho)
Definition: normalsabr.cpp:28
Real normalFreeBoundarySabrPrice(Rate strike, Rate forward, Time expiryTime, Real alpha, Real nu, Real rho)
Definition: normalsabr.cpp:81
vector< Real > strikes
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ calibrateModelParameters()

std::tuple< std::vector< Real >, Real, Size > calibrateModelParameters ( const MarketSmile marketSmile,
const std::vector< std::pair< Real, bool > > &  params 
) const
private

Definition at line 253 of file sabrparametricvolatility.cpp.

254 {
255
256 // determine the number of free parameters
257
258 Size noFreeParams = 0;
259 for (auto const& p : params)
260 if (!p.second)
261 ++noFreeParams;
262
263 // if there are no free parameters, we just pass back the fixed parameters as the result
264
265 if (noFreeParams == 0) {
266 std::vector<Real> resultParams;
267 for (auto const& p : params)
268 resultParams.push_back(p.first);
269 return std::make_tuple(resultParams, 0.0, 0);
270 }
271
272 // if we have less data points than free parameters -> exit early
273
274 QL_REQUIRE(noFreeParams <= marketSmile.strikes.size(), "internal: less data points than free parameters");
275
276 // define the target function
277
278 struct TargetFunction : public QuantLib::CostFunction {
279 Real forward_;
280 Real timeToExpiry_;
281 Real lognormalShift_;
282 std::vector<Real> strikes_;
283 std::vector<Real> marketQuotes_;
284 Real refQuote_;
285 std::function<std::vector<Real>(const std::vector<Real>&, const Real, const Real, const Real,
286 const std::vector<Real>&)>
287 evalSabr_;
288 std::vector<std::pair<Real, bool>> params_;
289 std::vector<Real> invParams_;
290 std::function<std::vector<Real>(const std::vector<Real>&)> direct_;
291 std::function<std::vector<Real>(const std::vector<Real>&)> inverse_;
292
293 std::vector<Real> evalSabr(const Array& x) const {
294 std::vector<Real> params(4);
295 for (Size i = 0, j = 0; i < params_.size(); ++i) {
296 if (params_[i].second)
297 params[i] = invParams_[i];
298 else
299 params[i] = x[j++];
300 }
301 params = direct_(params);
302 return evalSabr_(params, forward_, timeToExpiry_, lognormalShift_, strikes_);
303 }
304
305 Array values(const Array& x) const override {
306 Array result(strikes_.size());
307 auto sabr = evalSabr(x);
308 for (Size i = 0; i < strikes_.size(); ++i) {
309 result[i] = (marketQuotes_[i] - sabr[i]) / refQuote_;
310 }
311 return result;
312 }
313 };
314
315 /* build the target function and populate the members:
316 strikes, marketQuotes : the latter are converted to the preferred output quote type of the SABR
317 evalSabr : the function to produce SABR values for a given vector of strikes */
318
319 TargetFunction t;
320
321 t.forward_ = marketSmile.forward;
322 t.timeToExpiry_ = marketSmile.timeToExpiry;
323 t.lognormalShift_ = marketSmile.lognormalShift;
324
325 t.evalSabr_ = [this](const std::vector<Real>& params, const Real forward, const Real timeToExpiry,
326 const Real lognormalShift, const std::vector<Real>& strikes) {
327 return evaluateSabr(params, forward, timeToExpiry, lognormalShift, strikes);
328 };
329
330 t.params_ = params;
331 for (Size i = 0; i < params.size(); ++i)
332 t.invParams_.push_back(params[i].first);
333 t.invParams_ = inverse(t.invParams_, marketSmile.forward, marketSmile.lognormalShift);
334
335 t.inverse_ = [this, &marketSmile](const std::vector<Real>& y) {
336 return inverse(y, marketSmile.forward, marketSmile.lognormalShift);
337 };
338 t.direct_ = [this, &marketSmile](const std::vector<Real>& x) {
339 return direct(x, marketSmile.forward, marketSmile.lognormalShift);
340 };
341
342 t.strikes_ = marketSmile.strikes;
343 for (Size i = 0; i < marketSmile.marketQuotes.size(); ++i) {
344 t.marketQuotes_.push_back(convert(
345 marketSmile.marketQuotes[i], inputMarketQuoteType_, marketSmile.lognormalShift,
346 marketSmile.optionTypes.empty() ? boost::none : boost::optional<Option::Type>(marketSmile.optionTypes[i]),
347 marketSmile.timeToExpiry, marketSmile.strikes[i], marketSmile.forward, preferredOutputQuoteType(),
348 marketSmile.lognormalShift, boost::none));
349 }
350 // we use relative errors w.r.t. the max market quote, because far otm quotes are close to zero
351 t.refQuote_ = *std::max_element(t.marketQuotes_.begin(), t.marketQuotes_.end());
352
353 // perform the calibration (this step might throw if all minimizations go wrong)
354
355 NoConstraint noConstraint;
356 LevenbergMarquardt lm;
357 EndCriteria endCriteria(100, 10, 1E-8, 1E-8, 1E-8);
358
359 std::vector<Real> bestResult(params.size());
360 Real bestError = QL_MAX_REAL;
361
362 HaltonRsg haltonRsg(noFreeParams, 42);
363
364 Array guess(noFreeParams);
365
366 Size attempt;
367 for (attempt = 0; attempt < maxCalibrationAttempts_; ++attempt) {
368
369 if (attempt == 0) {
370 // first attempt uses given initial model parameters
371 for (Size i = 0, j = 0; i < t.invParams_.size(); ++i) {
372 if (!params[i].second) {
373 guess[j++] = t.invParams_[i];
374 }
375 }
376 } else {
377 // subsequent attempts use randomized guess
378 auto g = inverse(getGuess(params, haltonRsg.nextSequence().value, t.forward_, t.lognormalShift_),
379 t.forward_, t.lognormalShift_);
380 for (Size i = 0, j = 0; i < g.size(); ++i) {
381 if (!params[i].second) {
382 guess[j++] = g[i];
383 }
384 }
385 }
386
387 Problem problem(t, noConstraint, guess);
388 try {
389 lm.minimize(problem, endCriteria);
390 } catch (...) {
391 continue;
392 }
393
394 Real thisError = problem.functionValue();
395 if (thisError < bestError) {
396 bestError = thisError;
397 for (Size i = 0, j = 0; i < bestResult.size(); ++i) {
398 if (params[i].second)
399 bestResult[i] = t.invParams_[i];
400 else
401 bestResult[i] = problem.currentValue()[j++];
402 }
403 bestResult = direct(bestResult, t.forward_, t.lognormalShift_);
404 }
405
406 if (bestError < exitEarlyErrorThreshold_)
407 break;
408 }
409
410 QL_REQUIRE(bestError < QL_MAX_REAL, "internal: all calibrations failed");
411
412 return std::make_tuple(bestResult, bestError, ++attempt);
413}
std::vector< Real > inverse(const std::vector< Real > &y, const Real forward, const Real lognormalShift) const
std::vector< Real > getGuess(const std::vector< std::pair< Real, bool > > &params, const std::vector< Real > &randomSeq, const Real forward, const Real lognormalShift) const
std::vector< Real > direct(const std::vector< Real > &x, const Real forward, const Real lognormalShift) const
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Member Data Documentation

◆ eps1

constexpr double eps1 = .0000001
staticconstexprprivate

Definition at line 75 of file sabrparametricvolatility.hpp.

◆ eps2

constexpr double eps2 = .9999
staticconstexprprivate

Definition at line 76 of file sabrparametricvolatility.hpp.

◆ max_nvol_equiv

constexpr double max_nvol_equiv = 0.02
staticconstexprprivate

Definition at line 77 of file sabrparametricvolatility.hpp.

◆ max_nu

constexpr double max_nu = 2.0
staticconstexprprivate

Definition at line 78 of file sabrparametricvolatility.hpp.

◆ modelVariant_

ModelVariant modelVariant_
private

Definition at line 93 of file sabrparametricvolatility.hpp.

◆ modelParameters_

std::map<std::pair<QuantLib::Real, QuantLib::Real>, std::vector<std::pair<Real, bool> > > modelParameters_
private

Definition at line 94 of file sabrparametricvolatility.hpp.

◆ maxCalibrationAttempts_

QuantLib::Size maxCalibrationAttempts_
private

Definition at line 95 of file sabrparametricvolatility.hpp.

◆ exitEarlyErrorThreshold_

QuantLib::Real exitEarlyErrorThreshold_
private

Definition at line 96 of file sabrparametricvolatility.hpp.

◆ maxAcceptableError_

QuantLib::Real maxAcceptableError_
private

Definition at line 97 of file sabrparametricvolatility.hpp.

◆ calibratedSabrParams_

std::map<std::pair<Real, Real>, std::vector<Real> > calibratedSabrParams_
mutableprivate

Definition at line 99 of file sabrparametricvolatility.hpp.

◆ lognormalShifts_

std::map<std::pair<Real, Real>, Real> lognormalShifts_
mutableprivate

Definition at line 100 of file sabrparametricvolatility.hpp.

◆ calibrationErrors_

std::map<std::pair<Real, Real>, Real> calibrationErrors_
mutableprivate

Definition at line 101 of file sabrparametricvolatility.hpp.

◆ noOfAttempts_

std::map<std::pair<Real, Real>, QuantLib::Size> noOfAttempts_
mutableprivate

Definition at line 102 of file sabrparametricvolatility.hpp.

◆ underlyingLengths_

std::vector<Real> underlyingLengths_
mutableprivate

Definition at line 104 of file sabrparametricvolatility.hpp.

◆ timeToExpiries_

std::vector<Real> timeToExpiries_
private

Definition at line 104 of file sabrparametricvolatility.hpp.

◆ underlyingLengthsForInterpolation_

std::vector<Real> underlyingLengthsForInterpolation_
mutableprivate

Definition at line 105 of file sabrparametricvolatility.hpp.

◆ timeToExpiriesForInterpolation_

std::vector<Real> timeToExpiriesForInterpolation_
private

Definition at line 105 of file sabrparametricvolatility.hpp.

◆ alpha_

QuantLib::Matrix alpha_
mutableprivate

Definition at line 106 of file sabrparametricvolatility.hpp.

◆ beta_

QuantLib::Matrix beta_
private

Definition at line 106 of file sabrparametricvolatility.hpp.

◆ nu_

QuantLib::Matrix nu_
private

Definition at line 106 of file sabrparametricvolatility.hpp.

◆ rho_

QuantLib::Matrix rho_
private

Definition at line 106 of file sabrparametricvolatility.hpp.

◆ lognormalShift_

QuantLib::Matrix lognormalShift_
private

Definition at line 106 of file sabrparametricvolatility.hpp.

◆ calibrationError_

QuantLib::Matrix calibrationError_
private

Definition at line 106 of file sabrparametricvolatility.hpp.

◆ isInterpolated_

QuantLib::Matrix isInterpolated_
private

Definition at line 106 of file sabrparametricvolatility.hpp.

◆ numberOfCalibrationAttempts_

QuantLib::Matrix numberOfCalibrationAttempts_
private

Definition at line 107 of file sabrparametricvolatility.hpp.

◆ alphaInterpolation_

QuantLib::Interpolation2D alphaInterpolation_
mutableprivate

Definition at line 108 of file sabrparametricvolatility.hpp.

◆ betaInterpolation_

QuantLib::Interpolation2D betaInterpolation_
private

Definition at line 108 of file sabrparametricvolatility.hpp.

◆ nuInterpolation_

QuantLib::Interpolation2D nuInterpolation_
private

Definition at line 108 of file sabrparametricvolatility.hpp.

◆ rhoInterpolation_

QuantLib::Interpolation2D rhoInterpolation_
private

Definition at line 108 of file sabrparametricvolatility.hpp.

◆ lognormalShiftInterpolation_

QuantLib::Interpolation2D lognormalShiftInterpolation_
private

Definition at line 109 of file sabrparametricvolatility.hpp.