Coverage Report

Created: 2025-08-11 06:28

/src/quantlib/ql/experimental/volatility/sviinterpolation.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
 <http://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 sviinterpolation.hpp
21
    \brief Svi interpolation interpolation between discrete points
22
*/
23
24
#ifndef quantlib_svi_interpolation_hpp
25
#define quantlib_svi_interpolation_hpp
26
27
#include <ql/experimental/volatility/svismilesection.hpp>
28
#include <ql/math/interpolations/xabrinterpolation.hpp>
29
#include <utility>
30
31
namespace QuantLib {
32
33
namespace detail {
34
35
inline void checkSviParameters(const Real a, const Real b, const Real sigma,
36
0
                               const Real rho, const Real m, const Time tte) {
37
0
    QL_REQUIRE(b >= 0.0, "b (" << b << ") must be non negative");
38
0
    QL_REQUIRE(std::fabs(rho) < 1.0, "rho (" << rho << ") must be in (-1,1)");
39
0
    QL_REQUIRE(sigma > 0.0, "sigma (" << sigma << ") must be positive");
40
0
    QL_REQUIRE(a + b * sigma * std::sqrt(1.0 - rho * rho) >= 0.0,
41
0
               "a + b sigma sqrt(1-rho^2) (a=" << a << ", b=" << b << ", sigma="
42
0
                                               << sigma << ", rho=" << rho
43
0
                                               << ") must be non negative");
44
0
    QL_REQUIRE(b * (1.0 + std::fabs(rho)) <= 4.0,
45
0
               "b(1+|rho|) must be less than or equal to 4, (b=" << b << ", rho=" << rho << ")");
46
47
0
}
48
49
inline Real sviTotalVariance(const Real a, const Real b, const Real sigma,
50
0
                             const Real rho, const Real m, const Real k) {
51
0
    return a +
52
0
           b * (rho * (k - m) + std::sqrt((k - m) * (k - m) + sigma * sigma));
53
0
}
54
55
typedef SviSmileSection SviWrapper;
56
57
struct SviSpecs {
58
0
    Size dimension() { return 5; }
59
    void defaultValues(std::vector<Real> &params,
60
                       std::vector<bool> &paramIsFixed, const Real &forward,
61
                       const Real expiryTime,
62
0
                       const std::vector<Real> &addParams) {
63
0
        if (params[2] == Null<Real>())
64
0
            params[2] = 0.1;
65
0
        if (params[3] == Null<Real>())
66
0
            params[3] = -0.4;
67
0
        if (params[4] == Null<Real>())
68
0
            params[4] = 0.0;
69
0
        if (params[1] == Null<Real>())
70
0
            params[1] = 2.0 / (1.0 + std::fabs(params[3]));
71
0
        if (params[0] == Null<Real>()) {
72
0
            params[0] = std::max(
73
0
                0.20 * 0.20 * expiryTime -
74
0
                    params[1] * (params[3] * (-params[4]) +
75
0
                                 std::sqrt((-params[4]) * (-params[4]) +
76
0
                                           params[2] * params[2])),
77
0
                -params[1] * params[2] *
78
0
                std::sqrt(1.0 - params[3] * params[3]) + eps1());
79
0
        }
80
0
    }
81
    void guess(Array &values, const std::vector<bool> &paramIsFixed,
82
               const Real &forward, const Real expiryTime,
83
0
               const std::vector<Real> &r, const std::vector<Real> &addParams) {
84
0
        Size j = 0;
85
0
        if (!paramIsFixed[2])
86
0
            values[2] = r[j++] + eps1();
87
0
        if (!paramIsFixed[3])
88
0
            values[3] = (2.0 * r[j++] - 1.0) * eps2();
89
0
        if (!paramIsFixed[4])
90
0
            values[4] = (2.0 * r[j++] - 1.0);
91
0
        if (!paramIsFixed[1])
92
0
            values[1] = r[j++] * 4.0 / (1.0 + std::fabs(values[3])) * eps2();
93
0
        if (!paramIsFixed[0])
94
0
            values[0] = r[j++] * expiryTime -
95
0
                        eps2() * (values[1] * values[2] *
96
0
                                  std::sqrt(1.0 - values[3] * values[3]));
97
0
    }
98
    Array inverse(const Array &y, const std::vector<bool> &,
99
0
                  const std::vector<Real> &, const Real) {
100
0
        Array x(5);
101
0
        x[2] = std::sqrt(y[2] - eps1());
102
0
        x[3] = std::asin(y[3] / eps2());
103
0
        x[4] = y[4];
104
0
        x[1] = std::tan(y[1] / 4.0 * (1.0 + std::fabs(y[3])) / eps2() * M_PI -
105
0
                        M_PI / 2.0);
106
0
        x[0] = std::sqrt(y[0] - eps1() +
107
0
                         y[1] * y[2] * std::sqrt(1.0 - y[3] * y[3]));
108
0
        return x;
109
0
    }
110
0
    Real eps1() { return 0.000001; }
111
0
    Real eps2() { return 0.999999; }
112
    Array direct(const Array &x, const std::vector<bool> &paramIsFixed,
113
0
                 const std::vector<Real> &params, const Real forward) {
114
0
        Array y(5);
115
0
        y[2] = x[2] * x[2] + eps1();
116
0
        y[3] = std::sin(x[3]) * eps2();
117
0
        y[4] = x[4];
118
0
        if (paramIsFixed[1])
119
0
            y[1] = params[1];
120
0
        else
121
0
            y[1] = (std::atan(x[1]) + M_PI / 2.0) / M_PI * eps2() * 4.0 /
122
0
                   (1.0 + std::fabs(y[3]));
123
0
        if (paramIsFixed[0])
124
0
            y[0] = params[0];
125
0
        else
126
0
            y[0] = eps1() + x[0] * x[0] -
127
0
                   y[1] * y[2] * std::sqrt(1.0 - y[3] * y[3]);
128
0
        return y;
129
0
    }
130
    Real weight(const Real strike, const Real forward, const Real stdDev,
131
0
                const std::vector<Real> &addParams) {
132
0
        return blackFormulaStdDevDerivative(strike, forward, stdDev, 1.0);
133
0
    }
134
    typedef SviWrapper type;
135
    ext::shared_ptr<type> instance(const Time t, const Real &forward,
136
                                     const std::vector<Real> &params,
137
0
                                     const std::vector<Real> &addParams) {
138
0
        return ext::make_shared<type>(t, forward, params);
139
0
    }
140
};
141
}
142
143
//! %Svi smile interpolation between discrete volatility points.
144
class SviInterpolation : public Interpolation {
145
  public:
146
    template <class I1, class I2>
147
    SviInterpolation(const I1 &xBegin, // x = strikes
148
                     const I1 &xEnd,
149
                     const I2 &yBegin, // y = volatilities
150
                     Time t,           // option expiry
151
                     const Real &forward, Real a, Real b, Real sigma, Real rho,
152
                     Real m, bool aIsFixed, bool bIsFixed, bool sigmaIsFixed,
153
                     bool rhoIsFixed, bool mIsFixed, bool vegaWeighted = true,
154
                     const ext::shared_ptr<EndCriteria> &endCriteria =
155
                         ext::shared_ptr<EndCriteria>(),
156
                     const ext::shared_ptr<OptimizationMethod> &optMethod =
157
                         ext::shared_ptr<OptimizationMethod>(),
158
                     const Real errorAccept = 0.0020,
159
                     const bool useMaxError = false,
160
0
                     const Size maxGuesses = 50) {
161
162
0
        impl_ = ext::shared_ptr<Interpolation::Impl>(
163
0
            new detail::XABRInterpolationImpl<I1, I2, detail::SviSpecs>(
164
0
                xBegin, xEnd, yBegin, t, forward,
165
0
                {a, b, sigma, rho, m},
166
0
                {aIsFixed, bIsFixed, sigmaIsFixed, rhoIsFixed, mIsFixed},
167
0
                vegaWeighted, endCriteria, optMethod, errorAccept, useMaxError,
168
0
                maxGuesses));
169
0
    }
170
0
    Real expiry() const { return coeffs().t_; }
171
0
    Real forward() const { return coeffs().forward_; }
172
0
    Real a() const { return coeffs().params_[0]; }
173
0
    Real b() const { return coeffs().params_[1]; }
174
0
    Real sigma() const { return coeffs().params_[2]; }
175
0
    Real rho() const { return coeffs().params_[3]; }
176
0
    Real m() const { return coeffs().params_[4]; }
177
0
    Real rmsError() const { return coeffs().error_; }
178
0
    Real maxError() const { return coeffs().maxError_; }
179
0
    const std::vector<Real> &interpolationWeights() const {
180
0
        return coeffs().weights_;
181
0
    }
182
0
    EndCriteria::Type endCriteria() { return coeffs().XABREndCriteria_; }
183
184
  private:
185
0
    const detail::XABRCoeffHolder<detail::SviSpecs>& coeffs() const {
186
0
        return *dynamic_cast<detail::XABRCoeffHolder<detail::SviSpecs>*>(impl_.get());
187
0
    }
188
};
189
190
//! %Svi interpolation factory and traits
191
class Svi {
192
  public:
193
    Svi(Time t,
194
        Real forward,
195
        Real a,
196
        Real b,
197
        Real sigma,
198
        Real rho,
199
        Real m,
200
        bool aIsFixed,
201
        bool bIsFixed,
202
        bool sigmaIsFixed,
203
        bool rhoIsFixed,
204
        bool mIsFixed,
205
        bool vegaWeighted = false,
206
        ext::shared_ptr<EndCriteria> endCriteria = ext::shared_ptr<EndCriteria>(),
207
        ext::shared_ptr<OptimizationMethod> optMethod = ext::shared_ptr<OptimizationMethod>(),
208
        const Real errorAccept = 0.0020,
209
        const bool useMaxError = false,
210
        const Size maxGuesses = 50)
211
    : t_(t), forward_(forward), a_(a), b_(b), sigma_(sigma), rho_(rho), m_(m), aIsFixed_(aIsFixed),
212
      bIsFixed_(bIsFixed), sigmaIsFixed_(sigmaIsFixed), rhoIsFixed_(rhoIsFixed),
213
      mIsFixed_(mIsFixed), vegaWeighted_(vegaWeighted), endCriteria_(std::move(endCriteria)),
214
      optMethod_(std::move(optMethod)), errorAccept_(errorAccept), useMaxError_(useMaxError),
215
0
      maxGuesses_(maxGuesses) {}
216
    template <class I1, class I2>
217
    Interpolation interpolate(const I1 &xBegin, const I1 &xEnd,
218
                              const I2 &yBegin) const {
219
        return SviInterpolation(xBegin, xEnd, yBegin, t_, forward_, a_, b_,
220
                                 sigma_, rho_, m_, aIsFixed_, bIsFixed_,
221
                                 sigmaIsFixed_, rhoIsFixed_, mIsFixed_,
222
                                 vegaWeighted_, endCriteria_, optMethod_,
223
                                 errorAccept_, useMaxError_, maxGuesses_);
224
    }
225
    static const bool global = true;
226
227
  private:
228
    Time t_;
229
    Real forward_;
230
    Real a_, b_, sigma_, rho_, m_;
231
    bool aIsFixed_, bIsFixed_, sigmaIsFixed_, rhoIsFixed_, mIsFixed_;
232
    bool vegaWeighted_;
233
    const ext::shared_ptr<EndCriteria> endCriteria_;
234
    const ext::shared_ptr<OptimizationMethod> optMethod_;
235
    const Real errorAccept_;
236
    const bool useMaxError_;
237
    const Size maxGuesses_;
238
};
239
}
240
241
#endif