Coverage Report

Created: 2025-09-04 07:11

/src/quantlib/ql/experimental/volatility/noarbsabr.hpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2014 Peter Caspers
5
6
 This file is part of QuantLib, a free-software/open-source library
7
 for financial quantitative analysts and developers - http://quantlib.org/
8
9
 QuantLib is free software: you can redistribute it and/or modify it
10
 under the terms of the QuantLib license.  You should have received a
11
 copy of the license along with this program; if not, please email
12
 <quantlib-dev@lists.sf.net>. The license is also available online at
13
 <https://www.quantlib.org/license.shtml>.
14
15
 This program is distributed in the hope that it will be useful, but WITHOUT
16
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 FOR A PARTICULAR PURPOSE.  See the license for more details.
18
*/
19
20
/*! \file noarbsabr.hpp
21
    \brief No-arbitrage SABR
22
23
    Reference: Paul Doust, No-arbitrage SABR,
24
               The Journal of Computational Finance (3–31)
25
               Volume 15/Number 3, Spring 2012
26
27
    The parameters are bounded as follows (see also below)
28
29
    beta [0.01, 0.99]
30
    expiryTime (0.0, 30.0]
31
    sigmaI = alpha*forward^(beta-1) [0.05, 1.0]
32
    nu [0.0001, 0.8]
33
    rho [-0.99, 0.99]
34
35
    As suggested in the paper, d0 is interpolated (linearly)
36
    in phi space. For beta > 0.9 phi is extrapolated to a
37
    value corresponding to d0 = tiny_prob = 1E-5 at beta = 1.
38
    For tau < 0.25 phi is extrapolated flat.
39
    For rho outside [-0.75, 0.75] phi is extrapolated linearly.
40
41
    There are some parameter sets that are admissable, yet do
42
    not allow for the adjustment procedure as suggested in the
43
    paper to force the model implied forward to the correct
44
    value. In this case, no adjustment is done, leading to a
45
    model implied forward different from the desired one.
46
    This situation can be identified by comparing forward()
47
    and numericalForward().
48
*/
49
50
#ifndef quantlib_noarb_sabr
51
#define quantlib_noarb_sabr
52
53
#include <ql/qldefines.hpp>
54
#include <ql/types.hpp>
55
#include <ql/math/integrals/gausslobattointegral.hpp>
56
57
#include <vector>
58
59
namespace QuantLib {
60
61
namespace detail::NoArbSabrModel {
62
// parameter bounds
63
const Real beta_min = 0.01;
64
const Real beta_max = 0.99;
65
const Real expiryTime_max = 30.0;
66
const Real sigmaI_min = 0.05;
67
const Real sigmaI_max = 1.00;
68
const Real nu_min = 0.01;
69
const Real nu_max = 0.80;
70
const Real rho_min = -0.99;
71
const Real rho_max = 0.99;
72
// cutoff for phi(d0) / tau
73
// if beta = 0.99, d0 is below 1E-14 for
74
// bigger values than this
75
const Real phiByTau_cutoff = 124.587;
76
// number of mc simulations in tabulated
77
// absorption probabilities
78
const Real nsim = 2500000.0;
79
// small probability used for extrapolation
80
// of beta towards 1
81
const Real tiny_prob = 1E-5;
82
// minimum strike used for normal case integration
83
const Real strike_min = 1E-6;
84
// accuracy and max iterations for
85
// numerical integration
86
const Real i_accuracy = 1E-7;
87
const Size i_max_iterations = 10000;
88
// accuracy when adjusting the model forward
89
// to match the given forward
90
const Real forward_accuracy = 1E-6;
91
// step for searching the model forward
92
// in newton algorithm
93
const Real forward_search_step = 0.0010;
94
// lower bound for density evaluation
95
const Real density_lower_bound = 1E-50;
96
// threshold to identify a zero density
97
const Real density_threshold = 1E-100;
98
}
99
100
class NoArbSabrModel {
101
102
  public:
103
    NoArbSabrModel(Real expiryTime, Real forward, Real alpha, Real beta, Real nu, Real rho);
104
105
    Real optionPrice(Real strike) const;
106
    Real digitalOptionPrice(Real strike) const;
107
0
    Real density(const Real strike) const {
108
0
        return p(strike) * (1 - absProb_) / numericalIntegralOverP_;
109
0
    }
110
111
0
    Real forward() const { return externalForward_; }
112
0
    Real numericalForward() const { return numericalForward_; }
113
0
    Real expiryTime() const { return expiryTime_; }
114
0
    Real alpha() const { return alpha_; }
115
0
    Real beta() const { return beta_; }
116
0
    Real nu() const { return nu_; }
117
0
    Real rho() const { return rho_; }
118
119
0
    Real absorptionProbability() const { return absProb_; }
120
121
    private:
122
      Real p(Real f) const;
123
      Real forwardError(Real forward) const;
124
      const Real expiryTime_, externalForward_;
125
      const Real alpha_, beta_, nu_, rho_;
126
      Real absProb_, fmin_, fmax_;
127
      mutable Real forward_, numericalIntegralOverP_;
128
      mutable Real numericalForward_;
129
      ext::shared_ptr<GaussLobattoIntegral> integrator_;
130
      class integrand;
131
      class p_integrand;
132
};
133
134
namespace detail {
135
136
extern "C" const unsigned long sabrabsprob[1209600];
137
138
class D0Interpolator {
139
  public:
140
    D0Interpolator(Real forward, Real expiryTime, Real alpha, Real beta, Real nu, Real rho);
141
    Real operator()() const;
142
143
  private:
144
    Real phi(Real d0) const;
145
    Real d0(Real phi) const;
146
    const Real forward_, expiryTime_, alpha_, beta_, nu_, rho_, gamma_;
147
    Real sigmaI_;
148
    std::vector<Real> tauG_, sigmaIG_, rhoG_, nuG_, betaG_;
149
};
150
151
} // namespace detail
152
} // namespace QuantLib
153
154
#endif