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> ¶ms, 43 std::vector<bool> ¶mIsFixed, 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> ¶mIsFixed, 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> ¶mIsFixed, 105 const std::vector<Real> ¶ms, 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> ¶mIsFixed, 131 const std::vector<Real> ¶ms, 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> ¶ms, 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