/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> ¶ms, |
60 | | std::vector<bool> ¶mIsFixed, 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> ¶mIsFixed, |
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> ¶mIsFixed, |
113 | 0 | const std::vector<Real> ¶ms, 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> ¶ms, |
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 |