@@ -123,27 +123,22 @@ std::vector<std::pair<Real, bool>> SabrParametricVolatility::defaultModelParamet
123123std::vector<Real> SabrParametricVolatility::direct (const std::vector<Real>& x, const Real forward,
124124 const Real lognormalShift) const {
125125 std::vector<Real> y (4 );
126- // beta as in QuantLib::SABRSpecs
127- y[1 ] = std::fabs (x[1 ]) < std::sqrt (-std::log (eps1)) ? std::exp (-(x[1 ] * x[1 ])) : eps1;
128- // max 0.02 normal vol equivalent
129- Real fbeta = std::pow (forward + lognormalShift, y[1 ]);
130- y[0 ] =
131- (std::fabs (x[0 ]) < std::sqrt (-std::log (fbeta * eps1 / 0.02 )) ? 0.02 * std::exp (-(x[0 ] * x[0 ])) / fbeta : eps1);
132- // nu max 5.0
133- y[2 ] = (std::fabs (x[2 ]) < std::sqrt (-std::log (2.0 * eps1)) ? 2.0 * std::exp (-(x[2 ] * x[2 ])) : eps1);
134- // rho as in QuantLib::SABRSpecs
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);
135130 y[3 ] = std::fabs (x[3 ]) < 2.5 * M_PI ? eps2 * std::sin (x[3 ]) : eps2 * (x[3 ] > 0.0 ? 1.0 : (-1.0 ));
136131 return y;
137132}
138133
139134std::vector<Real> SabrParametricVolatility::inverse (const std::vector<Real>& y, const Real forward,
140135 const Real lognormalShift) const {
141136 std::vector<Real> x (4 );
142- x[1 ] = std::sqrt (-std::log (y[1 ]));
143- Real fbeta = std::pow (forward + lognormalShift, y[1 ]);
144- x[0 ] = std::sqrt (-std::log (y[0 ] * fbeta / 0.02 ));
145- x[2 ] = std::sqrt (-std::log (y[2 ] / 2.0 ));
146- x[3 ] = std::asin (y[3 ] / eps2 );
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 ])) );
147142 return x;
148143}
149144
@@ -267,9 +262,9 @@ SabrParametricVolatility::calibrateModelParameters(const MarketSmile& marketSmil
267262
268263 // if there are no free parameters, we just pass back the fixed parameters as the result
269264
270- if (noFreeParams == 0 ) {
265+ if (noFreeParams == 0 ) {
271266 std::vector<Real> resultParams;
272- for (auto const & p : params)
267+ for (auto const & p : params)
273268 resultParams.push_back (p.first );
274269 return std::make_tuple (resultParams, 0.0 , 0 );
275270 }
@@ -286,6 +281,7 @@ SabrParametricVolatility::calibrateModelParameters(const MarketSmile& marketSmil
286281 Real lognormalShift_;
287282 std::vector<Real> strikes_;
288283 std::vector<Real> marketQuotes_;
284+ Real refQuote_;
289285 std::function<std::vector<Real>(const std::vector<Real>&, const Real, const Real, const Real,
290286 const std::vector<Real>&)>
291287 evalSabr_;
@@ -310,23 +306,10 @@ SabrParametricVolatility::calibrateModelParameters(const MarketSmile& marketSmil
310306 Array result (strikes_.size ());
311307 auto sabr = evalSabr (x);
312308 for (Size i = 0 ; i < strikes_.size (); ++i) {
313- result[i] = (marketQuotes_[i] - sabr[i]);
309+ result[i] = (marketQuotes_[i] - sabr[i]) / refQuote_ ;
314310 }
315311 return result;
316312 }
317-
318- Real normalizedError (const Array& x) const {
319- auto sabr = evalSabr (x);
320- Real maxQuote = *std::max_element (marketQuotes_.begin (), marketQuotes_.end ());
321- Real result = 0.0 ;
322- for (Size i = 0 ; i < strikes_.size (); ++i) {
323- /* we want a relative error measure, but can't do this point by point, because for far-ootm strikes
324- * premiums are close to zero */
325- result += std::pow ((marketQuotes_[i] - sabr[i]) / maxQuote, 2.0 );
326- }
327- result = std::sqrt (result / static_cast <Real>(strikes_.size ()));
328- return result;
329- }
330313 };
331314
332315 /* build the target function and populate the members:
@@ -364,6 +347,8 @@ SabrParametricVolatility::calibrateModelParameters(const MarketSmile& marketSmil
364347 marketSmile.timeToExpiry , marketSmile.strikes [i], marketSmile.forward , preferredOutputQuoteType (),
365348 marketSmile.lognormalShift , boost::none));
366349 }
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 ());
367352
368353 // perform the calibration (this step might throw if all minimizations go wrong)
369354
@@ -406,7 +391,7 @@ SabrParametricVolatility::calibrateModelParameters(const MarketSmile& marketSmil
406391 continue ;
407392 }
408393
409- Real thisError = t. normalizedError ( problem.currentValue () );
394+ Real thisError = problem.functionValue ( );
410395 if (thisError < bestError) {
411396 bestError = thisError;
412397 for (Size i = 0 , j = 0 ; i < bestResult.size (); ++i) {
@@ -427,6 +412,17 @@ SabrParametricVolatility::calibrateModelParameters(const MarketSmile& marketSmil
427412 return std::make_tuple (bestResult, bestError, ++attempt);
428413}
429414
415+ namespace {
416+ void 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+
430426void SabrParametricVolatility::calculate () {
431427
432428 // if no model parameters are given, we provide the default ones
@@ -460,6 +456,7 @@ void SabrParametricVolatility::calculate() {
460456 calibrationErrors_[key] = error;
461457 noOfAttempts_[key] = noOfAttempts;
462458 } catch (const std::exception& e) {
459+ std::cerr << " error: " << e.what () << std::endl;
463460 // all calibration failed -> do not populate params, but interpolate them below
464461 }
465462 lognormalShifts_[key] = s.lognormalShift ;
@@ -515,10 +512,10 @@ void SabrParametricVolatility::calculate() {
515512
516513 // interpolate the null values
517514
518- laplaceInterpolation (alpha_, timeToExpiries_, underlyingLengths_);
519- laplaceInterpolation (beta_, timeToExpiries_, underlyingLengths_);
520- laplaceInterpolation (nu_, timeToExpiries_, underlyingLengths_);
521- laplaceInterpolation (rho_, timeToExpiries_, underlyingLengths_);
515+ laplaceInterpolationWithErrorHandling (alpha_, timeToExpiries_, underlyingLengths_);
516+ laplaceInterpolationWithErrorHandling (beta_, timeToExpiries_, underlyingLengths_);
517+ laplaceInterpolationWithErrorHandling (nu_, timeToExpiries_, underlyingLengths_);
518+ laplaceInterpolationWithErrorHandling (rho_, timeToExpiries_, underlyingLengths_);
522519
523520 // sanitize values produced by the the interpolation that are not allowed
524521
0 commit comments