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 noarbsabrinterpolation.hpp
21     \brief noabr sabr interpolation between discrete points
22 */
23 
24 #ifndef quantlib_noarbsabr_interpolation_hpp
25 #define quantlib_noarbsabr_interpolation_hpp
26 
27 #include <ql/math/interpolations/sabrinterpolation.hpp>
28 #include <ql/experimental/volatility/noarbsabrsmilesection.hpp>
29 
30 #include <boost/assign/list_of.hpp>
31 
32 namespace QuantLib {
33 
34 namespace detail {
35 
36 // we can directly use the smile section as the wrapper
37 typedef NoArbSabrSmileSection NoArbSabrWrapper;
38 
39 struct NoArbSabrSpecs {
dimensionQuantLib::detail::NoArbSabrSpecs40     Size dimension() { return 4; }
epsQuantLib::detail::NoArbSabrSpecs41     Real eps() { return 0.000001; }
defaultValuesQuantLib::detail::NoArbSabrSpecs42     void defaultValues(std::vector<Real> &params,
43                        std::vector<bool> &paramIsFixed, const Real &forward,
44                        const Real expiryTime, const std::vector<Real> &addParams) {
45         SABRSpecs().defaultValues(params, paramIsFixed, forward, expiryTime, addParams);
46         // check if alpha / beta is admissable, otherwise adjust
47         // if possible (i.e. not fixed, otherwise an exception will
48         // be thrown from the model constructor anyway)
49         Real sigmaI = params[0] * std::pow(forward, params[1] - 1.0);
50         if (sigmaI < detail::NoArbSabrModel::sigmaI_min) {
51             if (!paramIsFixed[0])
52                 params[0] = detail::NoArbSabrModel::sigmaI_min * (1.0 + eps()) /
53                             std::pow(forward, params[1] - 1.0);
54             else {
55                 if (!paramIsFixed[1])
56                     params[1] = 1.0 +
57                                 std::log(detail::NoArbSabrModel::sigmaI_min *
58                                          (1.0 + eps()) / params[0]) /
59                                     std::log(forward);
60             }
61         }
62         if (sigmaI > detail::NoArbSabrModel::sigmaI_max) {
63             if (!paramIsFixed[0])
64                 params[0] = detail::NoArbSabrModel::sigmaI_max * (1.0 - eps()) /
65                             std::pow(forward, params[1] - 1.0);
66             else {
67                 if (!paramIsFixed[1])
68                     params[1] = 1.0 +
69                                 std::log(detail::NoArbSabrModel::sigmaI_max *
70                                          (1.0 - eps()) / params[0]) /
71                                     std::log(forward);
72             }
73         }
74     }
guessQuantLib::detail::NoArbSabrSpecs75     void guess(Array &values, const std::vector<bool> &paramIsFixed,
76                const Real &forward, const Real expiryTime,
77                const std::vector<Real> &r, const std::vector<Real> &) {
78         Size j = 0;
79         if (!paramIsFixed[1])
80             values[1] = detail::NoArbSabrModel::beta_min +
81                         (detail::NoArbSabrModel::beta_max -
82                          detail::NoArbSabrModel::beta_min) *
83                             r[j++];
84         if (!paramIsFixed[0]) {
85             Real sigmaI = detail::NoArbSabrModel::sigmaI_min +
86                           (detail::NoArbSabrModel::sigmaI_max -
87                            detail::NoArbSabrModel::sigmaI_min) *
88                               r[j++];
89             sigmaI *= (1.0 - eps());
90             sigmaI += eps() / 2.0;
91             values[0] = sigmaI / std::pow(forward, values[1] - 1.0);
92         }
93         if (!paramIsFixed[2])
94             values[2] = detail::NoArbSabrModel::nu_min +
95                         (detail::NoArbSabrModel::nu_max -
96                          detail::NoArbSabrModel::nu_min) *
97                             r[j++];
98         if (!paramIsFixed[3])
99             values[3] = detail::NoArbSabrModel::rho_min +
100                         (detail::NoArbSabrModel::rho_max -
101                          detail::NoArbSabrModel::rho_min) *
102                             r[j++];
103     }
inverseQuantLib::detail::NoArbSabrSpecs104     Array inverse(const Array &y, const std::vector<bool> &paramIsFixed,
105                   const std::vector<Real> &params, const Real forward) {
106         Array x(4);
107         x[1] = std::tan((y[1] - detail::NoArbSabrModel::beta_min) /
108                             (detail::NoArbSabrModel::beta_max -
109                              detail::NoArbSabrModel::beta_min) *
110                             M_PI +
111                         M_PI / 2.0);
112         x[0] = std::tan((y[0] * std::pow(forward, y[1] - 1.0) -
113                          detail::NoArbSabrModel::sigmaI_min) /
114                             (detail::NoArbSabrModel::sigmaI_max -
115                              detail::NoArbSabrModel::sigmaI_min) *
116                             M_PI -
117                         M_PI / 2.0);
118         x[2] = std::tan((y[2] - detail::NoArbSabrModel::nu_min) /
119                             (detail::NoArbSabrModel::nu_max -
120                              detail::NoArbSabrModel::nu_min) *
121                             M_PI +
122                         M_PI / 2.0);
123         x[3] = std::tan((y[3] - detail::NoArbSabrModel::rho_min) /
124                             (detail::NoArbSabrModel::rho_max -
125                              detail::NoArbSabrModel::rho_min) *
126                             M_PI +
127                         M_PI / 2.0);
128         return x;
129     }
directQuantLib::detail::NoArbSabrSpecs130     Array direct(const Array &x, const std::vector<bool> &paramIsFixed,
131                  const std::vector<Real> &params, const Real forward) {
132         Array y(4);
133         if (paramIsFixed[1])
134             y[1] = params[1];
135         else
136             y[1] = detail::NoArbSabrModel::beta_min +
137                    (detail::NoArbSabrModel::beta_max -
138                     detail::NoArbSabrModel::beta_min) *
139                        (std::atan(x[1]) + M_PI / 2.0) / M_PI;
140         // we compute alpha from sigmaI using beta
141         // if alpha is fixed we have to check if beta is admissable
142         // and adjust if need be
143         if (paramIsFixed[0]) {
144             y[0] = params[0];
145             Real sigmaI = y[0] * std::pow(forward, y[1] - 1.0);
146             if (sigmaI < detail::NoArbSabrModel::sigmaI_min) {
147                 y[1] = (1.0 +
148                         std::log(detail::NoArbSabrModel::sigmaI_min *
149                                  (1.0 + eps()) / y[0]) /
150                             std::log(forward));
151             }
152             if (sigmaI > detail::NoArbSabrModel::sigmaI_max) {
153                 y[1] = (1.0 +
154                         std::log(detail::NoArbSabrModel::sigmaI_max *
155                                  (1.0 - eps()) / y[0]) /
156                             std::log(forward));
157             }
158         } else {
159             Real sigmaI = detail::NoArbSabrModel::sigmaI_min +
160                           (detail::NoArbSabrModel::sigmaI_max -
161                            detail::NoArbSabrModel::sigmaI_min) *
162                               (std::atan(x[0]) + M_PI / 2.0) / M_PI;
163             y[0] = sigmaI / std::pow(forward, y[1] - 1.0);
164         }
165         if (paramIsFixed[2])
166             y[2] = params[2];
167         else
168             y[2] = detail::NoArbSabrModel::nu_min +
169                    (detail::NoArbSabrModel::nu_max -
170                     detail::NoArbSabrModel::nu_min) *
171                        (std::atan(x[2]) + M_PI / 2.0) / M_PI;
172         if (paramIsFixed[3])
173             y[3] = params[3];
174         else
175             y[3] = detail::NoArbSabrModel::rho_min +
176                    (detail::NoArbSabrModel::rho_max -
177                     detail::NoArbSabrModel::rho_min) *
178                        (std::atan(x[3]) + M_PI / 2.0) / M_PI;
179         return y;
180     }
weightQuantLib::detail::NoArbSabrSpecs181     Real weight(const Real strike, const Real forward, const Real stdDev,
182                 const std::vector<Real> &addParams) {
183         return blackFormulaStdDevDerivative(strike, forward, stdDev, 1.0);
184     }
185     typedef NoArbSabrWrapper type;
instanceQuantLib::detail::NoArbSabrSpecs186     ext::shared_ptr<type> instance(const Time t, const Real &forward,
187                                      const std::vector<Real> &params,
188                                      const std::vector<Real> &) {
189         return ext::make_shared<type>(t, forward, params);
190     }
191 };
192 }
193 
194 //! no arbitrage sabr smile interpolation between discrete volatility points.
195 class NoArbSabrInterpolation : public Interpolation {
196   public:
197     template <class I1, class I2>
NoArbSabrInterpolation(const I1 & xBegin,const I1 & xEnd,const I2 & yBegin,Time t,const Real & forward,Real alpha,Real beta,Real nu,Real rho,bool alphaIsFixed,bool betaIsFixed,bool nuIsFixed,bool rhoIsFixed,bool vegaWeighted=true,const ext::shared_ptr<EndCriteria> & endCriteria=ext::shared_ptr<EndCriteria> (),const ext::shared_ptr<OptimizationMethod> & optMethod=ext::shared_ptr<OptimizationMethod> (),const Real errorAccept=0.0020,const bool useMaxError=false,const Size maxGuesses=50,const Real shift=0.0)198     NoArbSabrInterpolation(
199         const I1 &xBegin, // x = strikes
200         const I1 &xEnd,
201         const I2 &yBegin, // y = volatilities
202         Time t,           // option expiry
203         const Real &forward, Real alpha, Real beta, Real nu, Real rho,
204         bool alphaIsFixed, bool betaIsFixed, bool nuIsFixed, bool rhoIsFixed,
205         bool vegaWeighted = true,
206         const ext::shared_ptr<EndCriteria> &endCriteria =
207             ext::shared_ptr<EndCriteria>(),
208         const ext::shared_ptr<OptimizationMethod> &optMethod =
209             ext::shared_ptr<OptimizationMethod>(),
210         const Real errorAccept = 0.0020, const bool useMaxError = false,
211         const Size maxGuesses = 50, const Real shift = 0.0) {
212 
213         QL_REQUIRE(shift==0.0,"NoArbSabrInterpolation for non zero shift not implemented");
214         impl_ = ext::shared_ptr<Interpolation::Impl>(
215             new detail::XABRInterpolationImpl<I1, I2, detail::NoArbSabrSpecs>(
216                 xBegin, xEnd, yBegin, t, forward,
217                 boost::assign::list_of(alpha)(beta)(nu)(rho),
218                 boost::assign::list_of(alphaIsFixed)(betaIsFixed)(nuIsFixed)(
219                     rhoIsFixed),
220                 vegaWeighted, endCriteria, optMethod, errorAccept, useMaxError,
221                 maxGuesses));
222         coeffs_ = ext::dynamic_pointer_cast<
223             detail::XABRCoeffHolder<detail::NoArbSabrSpecs> >(impl_);
224     }
expiry() const225     Real expiry() const { return coeffs_->t_; }
forward() const226     Real forward() const { return coeffs_->forward_; }
alpha() const227     Real alpha() const { return coeffs_->params_[0]; }
beta() const228     Real beta() const { return coeffs_->params_[1]; }
nu() const229     Real nu() const { return coeffs_->params_[2]; }
rho() const230     Real rho() const { return coeffs_->params_[3]; }
rmsError() const231     Real rmsError() const { return coeffs_->error_; }
maxError() const232     Real maxError() const { return coeffs_->maxError_; }
interpolationWeights() const233     const std::vector<Real> &interpolationWeights() const {
234         return coeffs_->weights_;
235     }
endCriteria()236     EndCriteria::Type endCriteria() { return coeffs_->XABREndCriteria_; }
237 
238   private:
239     ext::shared_ptr<detail::XABRCoeffHolder<detail::NoArbSabrSpecs> > coeffs_;
240 };
241 
242 //! no arbtrage sabr interpolation factory and traits
243 class NoArbSabr {
244   public:
NoArbSabr(Time t,Real forward,Real alpha,Real beta,Real nu,Real rho,bool alphaIsFixed,bool betaIsFixed,bool nuIsFixed,bool rhoIsFixed,bool vegaWeighted=false,const ext::shared_ptr<EndCriteria> & endCriteria=ext::shared_ptr<EndCriteria> (),const ext::shared_ptr<OptimizationMethod> & optMethod=ext::shared_ptr<OptimizationMethod> (),const Real errorAccept=0.0020,const bool useMaxError=false,const Size maxGuesses=50)245     NoArbSabr(Time t,
246               Real forward,
247               Real alpha,
248               Real beta,
249               Real nu,
250               Real rho,
251               bool alphaIsFixed,
252               bool betaIsFixed,
253               bool nuIsFixed,
254               bool rhoIsFixed,
255               bool vegaWeighted = false,
256               const ext::shared_ptr<EndCriteria>& endCriteria = ext::shared_ptr<EndCriteria>(),
257               const ext::shared_ptr<OptimizationMethod>& optMethod =
258                   ext::shared_ptr<OptimizationMethod>(),
259               const Real errorAccept = 0.0020,
260               const bool useMaxError = false,
261               const Size maxGuesses = 50)
262     : t_(t), forward_(forward), alpha_(alpha), beta_(beta), nu_(nu), rho_(rho),
263       alphaIsFixed_(alphaIsFixed), betaIsFixed_(betaIsFixed), nuIsFixed_(nuIsFixed),
264       rhoIsFixed_(rhoIsFixed), vegaWeighted_(vegaWeighted), endCriteria_(endCriteria),
265       optMethod_(optMethod), errorAccept_(errorAccept), useMaxError_(useMaxError),
266       maxGuesses_(maxGuesses) {}
267     template <class I1, class I2>
interpolate(const I1 & xBegin,const I1 & xEnd,const I2 & yBegin) const268     Interpolation interpolate(const I1 &xBegin, const I1 &xEnd,
269                               const I2 &yBegin) const {
270         return NoArbSabrInterpolation(
271             xBegin, xEnd, yBegin, t_, forward_, alpha_, beta_, nu_, rho_,
272             alphaIsFixed_, betaIsFixed_, nuIsFixed_, rhoIsFixed_, vegaWeighted_,
273             endCriteria_, optMethod_, errorAccept_, useMaxError_, maxGuesses_);
274     }
275     static const bool global = true;
276 
277   private:
278     Time t_;
279     Real forward_;
280     Real alpha_, beta_, nu_, rho_;
281     bool alphaIsFixed_, betaIsFixed_, nuIsFixed_, rhoIsFixed_;
282     bool vegaWeighted_;
283     const ext::shared_ptr<EndCriteria> endCriteria_;
284     const ext::shared_ptr<OptimizationMethod> optMethod_;
285     const Real errorAccept_;
286     const bool useMaxError_;
287     const Size maxGuesses_;
288 };
289 }
290 
291 #endif
292