Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
exactbachelierimpliedvolatility.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 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
20
21#include <ql/math/comparison.hpp>
22
23#include <boost/math/distributions/normal.hpp>
24
25namespace QuantExt {
26
27using namespace QuantLib;
28
29namespace {
30
31static boost::math::normal_distribution<double> normal_dist;
32Real phi(const Real x) { return boost::math::pdf(normal_dist, x); }
33Real Phi(const Real x) { return boost::math::cdf(normal_dist, x); }
34
35Real PhiTilde(const Real x) { return Phi(x) + phi(x) / x; }
36
37Real inversePhiTilde(const Real PhiTildeStar) {
38 QL_REQUIRE(PhiTildeStar < 0.0, "inversePhiTilde(" << PhiTildeStar << "): negative argument required");
39 Real xbar;
40 if (PhiTildeStar < -0.001882039271) {
41 Real g = 1.0 / (PhiTildeStar - 0.5);
42 Real xibar = (0.032114372355 - g * g * (0.016969777977 - g * g * (2.6207332461E-3 - 9.6066952861E-5 * g * g))) /
43 (1.0 - g * g * (0.6635646938 - g * g * (0.14528712196 - 0.010472855461 * g * g)));
44 xbar = g * (0.3989422804014326 + xibar * g * g);
45 } else {
46 Real h = std::sqrt(-std::log(-PhiTildeStar));
47 xbar = (9.4883409779 - h * (9.6320903635 - h * (0.58556997323 + 2.1464093351 * h))) /
48 (1.0 - h * (0.65174820867 + h * (1.5120247828 + 6.6437847132E-5 * h)));
49 }
50 Real q = (PhiTilde(xbar) - PhiTildeStar) / phi(xbar);
51 Real xstar =
52 xbar + 3.0 * q * xbar * xbar * (2.0 - q * xbar * (2.0 + xbar * xbar)) /
53 (6.0 + q * xbar * (-12.0 + xbar * (6.0 * q + xbar * (-6.0 + q * xbar * (3.0 + xbar * xbar)))));
54 return xstar;
55}
56
57} // namespace
58
59Real exactBachelierImpliedVolatility(Option::Type optionType, Real strike, Real forward, Real tte, Real bachelierPrice,
60 Real discount) {
61
62 Real theta = optionType == Option::Call ? 1.0 : -1.0;
63
64 // compound bechelierPrice, so that effectively discount = 1
65
66 bachelierPrice /= discount;
67
68 // handle case strike = forward
69
70 if (std::abs(strike - forward) < 1E-15) {
71 return bachelierPrice / (std::sqrt(tte) * phi(0.0));
72 }
73
74 // handle case strike != forward
75
76 Real timeValue = bachelierPrice - std::max(theta * (forward - strike), 0.0);
77
78 if (std::abs(timeValue) < 1E-15)
79 return 0.0;
80
81 QL_REQUIRE(timeValue > 0.0, "exactBachelierImpliedVolatility(theta="
82 << theta << ",strike=" << strike << ",forward=" << forward << ",tte=" << tte
83 << ",price=" << bachelierPrice << "): option price implies negative time value ("
84 << timeValue << ")");
85
86 Real PhiTildeStar = -std::abs(timeValue / (strike - forward));
87 Real xstar = inversePhiTilde(PhiTildeStar);
88 Real impliedVol = std::abs((strike - forward) / (xstar * std::sqrt(tte)));
89
90 return impliedVol;
91}
92
93} // namespace QuantExt
implied bachelier volatility based on Jaeckel, Implied Normal Volatility, 2017
Real exactBachelierImpliedVolatility(Option::Type optionType, Real strike, Real forward, Real tte, Real bachelierPrice, Real discount)