/src/quantlib/ql/pricingengines/vanilla/analytichestonengine.hpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2004, 2005, 2008 Klaus Spanderen |
5 | | Copyright (C) 2007 StatPro Italia srl |
6 | | |
7 | | This file is part of QuantLib, a free-software/open-source library |
8 | | for financial quantitative analysts and developers - http://quantlib.org/ |
9 | | |
10 | | QuantLib is free software: you can redistribute it and/or modify it |
11 | | under the terms of the QuantLib license. You should have received a |
12 | | copy of the license along with this program; if not, please email |
13 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
14 | | <https://www.quantlib.org/license.shtml>. |
15 | | |
16 | | This program is distributed in the hope that it will be useful, but WITHOUT |
17 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
19 | | */ |
20 | | |
21 | | /*! \file analytichestonengine.hpp |
22 | | \brief analytic Heston-model engine |
23 | | */ |
24 | | |
25 | | #ifndef quantlib_analytic_heston_engine_hpp |
26 | | #define quantlib_analytic_heston_engine_hpp |
27 | | |
28 | | #include <ql/utilities/null.hpp> |
29 | | #include <ql/math/integrals/integral.hpp> |
30 | | #include <ql/math/integrals/gaussianquadratures.hpp> |
31 | | #include <ql/pricingengines/genericmodelengine.hpp> |
32 | | #include <ql/models/equity/hestonmodel.hpp> |
33 | | #include <ql/instruments/vanillaoption.hpp> |
34 | | #include <functional> |
35 | | #include <complex> |
36 | | |
37 | | namespace QuantLib { |
38 | | |
39 | | //! analytic Heston-model engine based on Fourier transform |
40 | | |
41 | | /*! Integration detail: |
42 | | Two algebraically equivalent formulations of the complex |
43 | | logarithm of the Heston model exist. Gatherals [2005] |
44 | | (also Duffie, Pan and Singleton [2000], and Schoutens, |
45 | | Simons and Tistaert[2004]) version does not cause |
46 | | discoutinuities whereas the original version (e.g. Heston [1993]) |
47 | | needs some sort of "branch correction" to work properly. |
48 | | Gatheral's version does also work with adaptive integration |
49 | | routines and should be preferred over the original Heston version. |
50 | | */ |
51 | | |
52 | | /*! References: |
53 | | |
54 | | Heston, Steven L., 1993. A Closed-Form Solution for Options |
55 | | with Stochastic Volatility with Applications to Bond and |
56 | | Currency Options. The review of Financial Studies, Volume 6, |
57 | | Issue 2, 327-343. |
58 | | |
59 | | A. Sepp, Pricing European-Style Options under Jump Diffusion |
60 | | Processes with Stochastic Volatility: Applications of Fourier |
61 | | Transform (<http://math.ut.ee/~spartak/papers/stochjumpvols.pdf>) |
62 | | |
63 | | R. Lord and C. Kahl, Why the rotation count algorithm works, |
64 | | http://papers.ssrn.com/sol3/papers.cfm?abstract_id=921335 |
65 | | |
66 | | H. Albrecher, P. Mayer, W.Schoutens and J. Tistaert, |
67 | | The Little Heston Trap, http://www.schoutens.be/HestonTrap.pdf |
68 | | |
69 | | J. Gatheral, The Volatility Surface: A Practitioner's Guide, |
70 | | Wiley Finance |
71 | | |
72 | | F. Le Floc'h, Fourier Integration and Stochastic Volatility |
73 | | Calibration, |
74 | | https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2362968 |
75 | | |
76 | | L. Andersen, and V. Piterbarg, 2010, |
77 | | Interest Rate Modeling, Volume I: Foundations and Vanilla Models, |
78 | | Atlantic Financial Press London. |
79 | | |
80 | | L. Andersen and M. Lake, 2018 |
81 | | Robust High-Precision Option Pricing by Fourier Transforms: |
82 | | Contour Deformations and Double-Exponential Quadrature, |
83 | | https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3231626 |
84 | | |
85 | | \ingroup vanillaengines |
86 | | |
87 | | \test the correctness of the returned value is tested by |
88 | | reproducing results available in web/literature |
89 | | and comparison with Black pricing. |
90 | | */ |
91 | | class AnalyticHestonEngine |
92 | | : public GenericModelEngine<HestonModel, |
93 | | VanillaOption::arguments, |
94 | | VanillaOption::results> { |
95 | | public: |
96 | | class Integration; |
97 | | class OptimalAlpha; |
98 | | class AP_Helper; |
99 | | |
100 | | enum ComplexLogFormula { |
101 | | // Gatheral form of characteristic function w/o control variate |
102 | | Gatheral, |
103 | | // old branch correction form of the characteristic function w/o control variate |
104 | | BranchCorrection, |
105 | | // Gatheral form with Andersen-Piterbarg control variate |
106 | | AndersenPiterbarg, |
107 | | // same as AndersenPiterbarg, but a slightly better control variate |
108 | | AndersenPiterbargOptCV, |
109 | | // Gatheral form with asymptotic expansion of the characteristic function as control variate |
110 | | // https://hpcquantlib.wordpress.com/2020/08/30/a-novel-control-variate-for-the-heston-model |
111 | | AsymptoticChF, |
112 | | // angled contour shift integral with control variate |
113 | | AngledContour, |
114 | | // angled contour shift integral w/o control variate |
115 | | AngledContourNoCV, |
116 | | // auto selection of best control variate algorithm from above |
117 | | OptimalCV |
118 | | }; |
119 | | |
120 | | // Simple to use constructor: Using adaptive |
121 | | // Gauss-Lobatto integration and Gatheral's version of complex log. |
122 | | // Be aware: using a too large number for maxEvaluations might result |
123 | | // in a stack overflow as the Lobatto integration is a recursive |
124 | | // algorithm. |
125 | | AnalyticHestonEngine(const ext::shared_ptr<HestonModel>& model, |
126 | | Real relTolerance, Size maxEvaluations); |
127 | | |
128 | | // Constructor using Laguerre integration |
129 | | // and Gatheral's version of complex log. |
130 | | AnalyticHestonEngine(const ext::shared_ptr<HestonModel>& model, |
131 | | Size integrationOrder = 144); |
132 | | |
133 | | // Constructor giving full control |
134 | | // over the Fourier integration algorithm |
135 | | AnalyticHestonEngine(const ext::shared_ptr<HestonModel>& model, |
136 | | ComplexLogFormula cpxLog, const Integration& itg, |
137 | | Real andersenPiterbargEpsilon = 1e-25, |
138 | | Real alpha = -0.5); |
139 | | |
140 | | void calculate() const override; |
141 | | |
142 | | // normalized characteristic function |
143 | | std::complex<Real> chF(const std::complex<Real>& z, Time t) const; |
144 | | std::complex<Real> lnChF(const std::complex<Real>& z, Time t) const; |
145 | | |
146 | | Size numberOfEvaluations() const; |
147 | | |
148 | | [[deprecated("Use AnalyticHestonEngine::priceVanillaPayoff instead.")]] |
149 | | static void doCalculation(Real riskFreeDiscount, |
150 | | Real dividendDiscount, |
151 | | Real spotPrice, |
152 | | Real strikePrice, |
153 | | Real term, |
154 | | Real kappa, |
155 | | Real theta, |
156 | | Real sigma, |
157 | | Real v0, |
158 | | Real rho, |
159 | | const TypePayoff& type, |
160 | | const Integration& integration, |
161 | | ComplexLogFormula cpxLog, |
162 | | const AnalyticHestonEngine* enginePtr, |
163 | | Real& value, |
164 | | Size& evaluations); |
165 | | |
166 | | Real priceVanillaPayoff( |
167 | | const ext::shared_ptr<PlainVanillaPayoff>& payoff, |
168 | | const Date& maturity) const; |
169 | | |
170 | | Real priceVanillaPayoff( |
171 | | const ext::shared_ptr<PlainVanillaPayoff>& payoff, Time maturity) const; |
172 | | |
173 | | static ComplexLogFormula optimalControlVariate( |
174 | | Time t, Real v0, Real kappa, Real theta, Real sigma, Real rho); |
175 | | |
176 | | protected: |
177 | | // call back for extended stochastic volatility |
178 | | // plus jump diffusion engines like bates model |
179 | | virtual std::complex<Real> addOnTerm(Real phi, |
180 | | Time t, |
181 | | Size j) const; |
182 | | |
183 | | private: |
184 | | class Fj_Helper; |
185 | | |
186 | | Real priceVanillaPayoff( |
187 | | const ext::shared_ptr<PlainVanillaPayoff>& payoff, |
188 | | Time maturity, Real fwd) const; |
189 | | |
190 | | |
191 | | mutable Size evaluations_; |
192 | | const ComplexLogFormula cpxLog_; |
193 | | const ext::shared_ptr<Integration> integration_; |
194 | | const Real andersenPiterbargEpsilon_, alpha_; |
195 | | }; |
196 | | |
197 | | |
198 | | class AnalyticHestonEngine::Integration { |
199 | | public: |
200 | | // non adaptive integration algorithms based on Gaussian quadrature |
201 | | static Integration gaussLaguerre (Size integrationOrder = 128); |
202 | | static Integration gaussLegendre (Size integrationOrder = 128); |
203 | | static Integration gaussChebyshev (Size integrationOrder = 128); |
204 | | static Integration gaussChebyshev2nd(Size integrationOrder = 128); |
205 | | |
206 | | // Gatheral's version has to be used for an adaptive integration |
207 | | // algorithm .Be aware: using a too large number for maxEvaluations might |
208 | | // result in a stack overflow as the these integrations are based on |
209 | | // recursive algorithms. |
210 | | static Integration gaussLobatto(Real relTolerance, Real absTolerance, |
211 | | Size maxEvaluations = 1000, |
212 | | bool useConvergenceEstimate = false); |
213 | | |
214 | | // usually these routines have a poor convergence behavior. |
215 | | static Integration gaussKronrod(Real absTolerance, |
216 | | Size maxEvaluations = 1000); |
217 | | static Integration simpson(Real absTolerance, |
218 | | Size maxEvaluations = 1000); |
219 | | static Integration trapezoid(Real absTolerance, |
220 | | Size maxEvaluations = 1000); |
221 | | static Integration discreteSimpson(Size evaluation = 1000); |
222 | | static Integration discreteTrapezoid(Size evaluation = 1000); |
223 | | static Integration expSinh(Real relTolerance = 1e-8); |
224 | | |
225 | | static Real andersenPiterbargIntegrationLimit( |
226 | | Real c_inf, Real epsilon, Real v0, Real t); |
227 | | |
228 | | Real calculate(Real c_inf, |
229 | | const std::function<Real(Real)>& f, |
230 | | const std::function<Real()>& maxBound = {}, |
231 | | Real scaling = 1.0) const; |
232 | | |
233 | | Real calculate(Real c_inf, |
234 | | const std::function<Real(Real)>& f, |
235 | | Real maxBound) const; |
236 | | |
237 | | Size numberOfEvaluations() const; |
238 | | bool isAdaptiveIntegration() const; |
239 | | |
240 | | private: |
241 | | enum Algorithm |
242 | | { GaussLobatto, GaussKronrod, Simpson, Trapezoid, |
243 | | DiscreteTrapezoid, DiscreteSimpson, |
244 | | GaussLaguerre, GaussLegendre, |
245 | | GaussChebyshev, GaussChebyshev2nd, |
246 | | ExpSinh}; |
247 | | |
248 | | Integration(Algorithm intAlgo, ext::shared_ptr<GaussianQuadrature> quadrature); |
249 | | |
250 | | Integration(Algorithm intAlgo, ext::shared_ptr<Integrator> integrator); |
251 | | |
252 | | const Algorithm intAlgo_; |
253 | | const ext::shared_ptr<Integrator> integrator_; |
254 | | const ext::shared_ptr<GaussianQuadrature> gaussianQuadrature_; |
255 | | }; |
256 | | |
257 | | class AnalyticHestonEngine::AP_Helper { |
258 | | public: |
259 | | AP_Helper(Time term, Real fwd, Real strike, |
260 | | ComplexLogFormula cpxLog, |
261 | | const AnalyticHestonEngine* enginePtr, |
262 | | Real alpha = -0.5); |
263 | | |
264 | | Real operator()(Real u) const; |
265 | | Real controlVariateValue() const; |
266 | | |
267 | | private: |
268 | | const Time term_; |
269 | | const Real fwd_, strike_, freq_; |
270 | | const ComplexLogFormula cpxLog_; |
271 | | const AnalyticHestonEngine* const enginePtr_; |
272 | | const Real alpha_, s_alpha_; |
273 | | Real vAvg_, tanPhi_; |
274 | | std::complex<Real> phi_, psi_; |
275 | | }; |
276 | | |
277 | | |
278 | | class AnalyticHestonEngine::OptimalAlpha { |
279 | | public: |
280 | | OptimalAlpha( |
281 | | Time t, |
282 | | const AnalyticHestonEngine* enginePtr); |
283 | | |
284 | | Real operator()(Real strike) const; |
285 | | std::pair<Real, Real> alphaGreaterZero(Real strike) const; |
286 | | std::pair<Real, Real> alphaSmallerMinusOne(Real strike) const; |
287 | | |
288 | | Size numberOfEvaluations() const; |
289 | | Real M(Real k) const; |
290 | | Real k(Real x, Integer sgn) const; |
291 | | Real alphaMin(Real strike) const; |
292 | | Real alphaMax(Real strike) const; |
293 | | |
294 | | private: |
295 | | std::pair<Real, Real> findMinima(Real lower, Real upper, Real strike) const; |
296 | | |
297 | | const Real t_, fwd_, kappa_, theta_, sigma_, rho_; |
298 | | |
299 | | const Real eps_; |
300 | | |
301 | | const AnalyticHestonEngine* const enginePtr_; |
302 | | Real km_, kp_; |
303 | | mutable Size evaluations_ = 0; |
304 | | }; |
305 | | |
306 | | |
307 | | inline std::complex<Real> AnalyticHestonEngine::addOnTerm( |
308 | 0 | Real, Time, Size) const { |
309 | 0 | return std::complex<Real>(0,0); |
310 | 0 | } |
311 | | } |
312 | | |
313 | | #endif |