Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
normalsabr.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2017 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
21
22#include <ql/errors.hpp>
23#include <ql/math/comparison.hpp>
24#include <ql/math/integrals/gausslobattointegral.hpp>
25
26namespace QuantExt {
27
28Real normalSabrVolatility(Rate strike, Rate forward, Time expiryTime, Real alpha, Real nu, Real rho) {
29
30 // update extreme parameters
31
32 alpha = std::max(alpha, 1E-5);
33 if (rho < -1 + 1E-5)
34 rho = -1 + 1E-5;
35 else if (rho > 1 - 1E-5)
36 rho = 1 - 1E-5;
37
38 // calculate result
39
40 Real zeta = nu / alpha * (forward - strike);
41 Real x = std::log((std::sqrt(1.0 - 2.0 * rho * zeta + zeta * zeta) - rho + zeta) / (1.0 - rho));
42 Real f = close_enough(x, 0.0) ? 1.0 : zeta / x;
43 Real vol = alpha * f * (1.0 + expiryTime * (2.0 - 3.0 * rho * rho) * nu * nu / 24.0);
44 QL_REQUIRE(std::isfinite(vol), "normalSabrVolatility: computed invalid vol for strike="
45 << strike << ", forward=" << forward << ", expiryTime=" << expiryTime
46 << ", alpha=" << alpha << ", nu=" << nu << ", rho=" << rho);
47 return std::max(vol, 0.00001);
48}
49
50Real normalSabrAlphaFromAtmVol(Rate forward, Time expiryTime, Real atmVol, Real nu, Real rho) {
51 return std::max(atmVol / (1.0 + expiryTime * (2.0 - 3.0 * rho * rho) * nu * nu / 24.0), 0.00001);
52}
53
54namespace {
55
56Real deltaR(const Real t) { return std::exp(t / 8.0) - (3072.0 + t * (384.0 + t * (24.0 + t))) / 3072.0; }
57
58Real g(const Real s) { return s / std::tanh(s) - 1.0; }
59
60Real R(const Real t, const Real s) {
61 Real s2 = s * s;
62 Real s4 = s2 * s2;
63 if (s < 0.03) {
64 return (3072.0 + t * (384.0 + t * (24.0 + t))) / 3072.0 - t * (2688.0 + t * (80.0 + 21.0 * t)) / 322560.0 * s2 +
65 t * (2816.0 - t * (88.0 + 63.0 * t)) / 3548160.0 * s4;
66 }
67 Real s6 = s2 * s4;
68 Real t2 = t * t;
69 Real t3 = t2 * t;
70 Real gval = g(s);
71 return 1.0 + 3.0 * t * gval / (8.0 * s2) - (5.0 * t2 * (-8.0 * s2 + gval * (24.0 + 3.0 * gval))) / (128.0 * s4) +
72 (35.0 * t3 * (-40.0 * s2 + gval * (120 + gval * (24.0 + 3.0 * gval)))) / (1024.0 * s6);
73}
74
75Real G(const Real t, const Real s) {
76 return std::sqrt(std::sinh(s) / s) * std::exp(-s * s / (2.0 * t) - t / 8.0) * (R(t, s) + deltaR(t));
77}
78
79} // namespace
80
81Real normalFreeBoundarySabrPrice(Rate strike, Rate forward, Time expiryTime, Real alpha, Real nu, Real rho) {
82 // update extreme parameters
83
84 nu = std::max(nu, 1E-6);
85 if (rho < -1 + 1E-5)
86 rho = -1 + 1E-5;
87 else if (rho > 1 - 1E-5)
88 rho = 1 - 1E-5;
89
90 // compute option price
91
92 Real V0 = alpha / nu;
93 Real k = (strike - forward) / V0 + rho;
94 Real rhobar = std::sqrt(1.0 - rho * rho);
95 Real arg = (-rho * k + std::sqrt(k * k + rhobar * rhobar)) / (rhobar * rhobar);
96 QL_REQUIRE(arg > 1.0 - 1E-12, "invalid arg (" << arg << "), must be >= 1");
97 Real s0 = std::acosh(std::max(1.0, arg));
98
99 auto integrand = [k, rho, nu, expiryTime](const Real s) {
100 Real tmp = (k - rho * std::cosh(s));
101 Real arg = std::sinh(s) * std::sinh(s) - tmp * tmp;
102 QL_REQUIRE(arg > -1E-12, "invalid arg (" << arg << "), must be >= 0 (tmp=" << tmp << ")");
103 return G(nu * nu * expiryTime, s) / std::sinh(s) * std::sqrt(std::max(0.0, arg));
104 };
105
106 Real lowerBound = std::max(s0, 1E-12);
107 Real upperBound = std::max(1.5 * s0, 1.0);
108 while (integrand(upperBound) > 1E-12)
109 upperBound *= 1.5;
110
111 GaussLobattoIntegral gl(10000, 1E-8);
112 Real price = V0 / M_PI * gl(integrand, lowerBound, upperBound);
113 price += std::max(forward - strike, 0.0);
114
115 return price;
116}
117
118Real normalFreeBoundarySabrVolatility(Rate strike, Rate forward, Time expiryTime, Real alpha, Real nu, Real rho) {
119 return exactBachelierImpliedVolatility(Option::Call, strike, forward, expiryTime,
120 normalFreeBoundarySabrPrice(strike, forward, expiryTime, alpha, nu, rho));
121}
122
123} // namespace QuantExt
implied bachelier volatility based on Jaeckel, Implied Normal Volatility, 2017
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Real normalFreeBoundarySabrVolatility(Rate strike, Rate forward, Time expiryTime, Real alpha, Real nu, Real rho)
Definition: normalsabr.cpp:118
Real exactBachelierImpliedVolatility(Option::Type optionType, Real strike, Real forward, Real tte, Real bachelierPrice, Real discount)
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
Real normalSabrAlphaFromAtmVol(Rate forward, Time expiryTime, Real atmVol, Real nu, Real rho)
Definition: normalsabr.cpp:50
normal SABR model implied volatility approximation