Coverage Report

Created: 2026-06-08 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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