Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
sabrparametricvolatility.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2024 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
22
23#include <ql/experimental/math/laplaceinterpolation.hpp>
24#include <ql/math/comparison.hpp>
25#include <ql/math/interpolations/bilinearinterpolation.hpp>
26#include <ql/math/interpolations/flatextrapolation2d.hpp>
27#include <ql/math/optimization/costfunction.hpp>
28#include <ql/math/optimization/levenbergmarquardt.hpp>
29#include <ql/math/randomnumbers/haltonrsg.hpp>
30#include <ql/termstructures/volatility/sabr.hpp>
31
32namespace QuantExt {
33
34using namespace QuantLib;
35
37 const ModelVariant modelVariant, const std::vector<MarketSmile> marketSmiles, const MarketModelType marketModelType,
38 const MarketQuoteType inputMarketQuoteType, const Handle<YieldTermStructure> discountCurve,
39 const std::map<std::pair<QuantLib::Real, QuantLib::Real>, std::vector<std::pair<Real, bool>>> modelParameters,
40 const Size maxCalibrationAttempts, const Real exitEarlyErrorThreshold, const Real maxAcceptableError)
41 : ParametricVolatility(marketSmiles, marketModelType, inputMarketQuoteType, discountCurve),
42 modelVariant_(modelVariant), modelParameters_(std::move(modelParameters)),
43 maxCalibrationAttempts_(maxCalibrationAttempts), exitEarlyErrorThreshold_(exitEarlyErrorThreshold),
44 maxAcceptableError_(maxAcceptableError) {
45 calculate();
46}
47
49 switch (modelVariant_) {
62 default:
63 QL_FAIL("SabrParametricVolatility::preferredOutputQuoteType(): model variant ("
64 << static_cast<int>(modelVariant_) << ") not handled.");
65 }
66}
67
68std::vector<Real> SabrParametricVolatility::getGuess(const std::vector<std::pair<Real, bool>>& params,
69 const std::vector<Real>& randomSeq, const Real forward,
70 const Real lognormalShift) const {
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}
102
103std::vector<std::pair<Real, bool>> SabrParametricVolatility::defaultModelParameters() const {
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}
122
123std::vector<Real> SabrParametricVolatility::direct(const std::vector<Real>& x, const Real forward,
124 const Real lognormalShift) const {
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}
133
134std::vector<Real> SabrParametricVolatility::inverse(const std::vector<Real>& y, const Real forward,
135 const Real lognormalShift) const {
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}
144
145std::vector<Real> SabrParametricVolatility::evaluateSabr(const std::vector<Real>& params, const Real forward,
146 const Real timeToExpiry, const Real lognormalShift,
147 const std::vector<Real>& strikes) const {
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}
251
252std::tuple<std::vector<Real>, Real, Size>
254 const std::vector<std::pair<Real, bool>>& params) const {
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}
414
415namespace {
416void laplaceInterpolationWithErrorHandling(Matrix& m, const std::vector<Real>& x, const std::vector<Real>& y) {
417 try {
418 laplaceInterpolation(m, x, y, 1E-6, 100);
419 } catch (const std::exception& e) {
420 QL_FAIL("Error during laplaceInterpolation() in SabrParametricVolatility: "
421 << e.what() << ", this might be related to the numerical parameters relTol, maxIterMult. Contact dev.");
422 }
423}
424} // namespace
425
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}
601
602Real SabrParametricVolatility::evaluate(const Real timeToExpiry, const Real underlyingLength, const Real strike,
603 const Real forward, const MarketQuoteType outputMarketQuoteType,
604 const Real outputLognormalShift,
605 const boost::optional<QuantLib::Option::Type> outputOptionType) const {
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}
618
619} // namespace QuantExt
std::vector< Real > callPrices(const std::vector< Real > &strikes) const
std::vector< MarketSmile > marketSmiles_
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
QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve_
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)
const QuantLib::Matrix & rho() const
QuantLib::Interpolation2D lognormalShiftInterpolation_
std::vector< std::pair< Real, bool > > defaultModelParameters() const
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
std::map< std::pair< Real, Real >, Real > calibrationErrors_
const QuantLib::Matrix & beta() const
const QuantLib::Matrix & lognormalShift() const
std::vector< Real > inverse(const std::vector< Real > &y, const Real forward, const Real lognormalShift) const
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
std::map< std::pair< Real, Real >, QuantLib::Size > noOfAttempts_
std::map< std::pair< Real, Real >, std::vector< Real > > calibratedSabrParams_
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::map< std::pair< QuantLib::Real, QuantLib::Real >, std::vector< std::pair< Real, bool > > > modelParameters_
const QuantLib::Matrix & nu() const
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_
std::vector< Real > direct(const std::vector< Real > &x, const Real forward, const Real lognormalShift) const
ParametricVolatility::MarketQuoteType preferredOutputQuoteType() const
Adaption of VBA code by Jörg Kienitz, 2017, to create a SABR density with PDE methods.
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
normal SABR model implied volatility approximation
sabr volatility structure
std::vector< QuantLib::Option::Type > optionTypes
vector< Real > strikes