| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2003 Ferdinando Ametrano |
| 5 | Copyright (C) 2001, 2002, 2003 Sadruddin Rejeb |
| 6 | Copyright (C) 2004, 2005, 2006, 2007 StatPro Italia srl |
| 7 | Copyright (C) 2015 Peter Caspers |
| 8 | |
| 9 | This file is part of QuantLib, a free-software/open-source library |
| 10 | for financial quantitative analysts and developers - http://quantlib.org/ |
| 11 | |
| 12 | QuantLib is free software: you can redistribute it and/or modify it |
| 13 | under the terms of the QuantLib license. You should have received a |
| 14 | copy of the license along with this program; if not, please email |
| 15 | <quantlib-dev@lists.sf.net>. The license is also available online at |
| 16 | <http://quantlib.org/license.shtml>. |
| 17 | |
| 18 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 19 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 20 | FOR A PARTICULAR PURPOSE. See the license for more details. |
| 21 | */ |
| 22 | |
| 23 | #include <ql/processes/blackscholesprocess.hpp> |
| 24 | #include <ql/termstructures/volatility/equityfx/localconstantvol.hpp> |
| 25 | #include <ql/termstructures/volatility/equityfx/localvolcurve.hpp> |
| 26 | #include <ql/termstructures/volatility/equityfx/localvolsurface.hpp> |
| 27 | #include <ql/termstructures/yield/flatforward.hpp> |
| 28 | #include <ql/time/calendars/nullcalendar.hpp> |
| 29 | #include <ql/time/daycounters/actual365fixed.hpp> |
| 30 | #include <utility> |
| 31 | |
| 32 | |
| 33 | namespace QuantLib { |
| 34 | |
| 35 | GeneralizedBlackScholesProcess::GeneralizedBlackScholesProcess( |
| 36 | Handle<Quote> x0, |
| 37 | Handle<YieldTermStructure> dividendTS, |
| 38 | Handle<YieldTermStructure> riskFreeTS, |
| 39 | Handle<BlackVolTermStructure> blackVolTS, |
| 40 | Handle<LocalVolTermStructure> localVolTS) |
| 41 | : StochasticProcess1D(ext::make_shared<EulerDiscretization>()), x0_(std::move(x0)), |
| 42 | riskFreeRate_(std::move(riskFreeTS)), dividendYield_(std::move(dividendTS)), |
| 43 | blackVolatility_(std::move(blackVolTS)), externalLocalVolTS_(std::move(localVolTS)), |
| 44 | forceDiscretization_(false), hasExternalLocalVol_(true), updated_(false), |
| 45 | isStrikeIndependent_(false) { |
| 46 | registerWith(h: x0_); |
| 47 | registerWith(h: riskFreeRate_); |
| 48 | registerWith(h: dividendYield_); |
| 49 | registerWith(h: blackVolatility_); |
| 50 | registerWith(h: externalLocalVolTS_); |
| 51 | } |
| 52 | |
| 53 | GeneralizedBlackScholesProcess::GeneralizedBlackScholesProcess( |
| 54 | Handle<Quote> x0, |
| 55 | Handle<YieldTermStructure> dividendTS, |
| 56 | Handle<YieldTermStructure> riskFreeTS, |
| 57 | Handle<BlackVolTermStructure> blackVolTS, |
| 58 | const ext::shared_ptr<discretization>& disc, |
| 59 | bool forceDiscretization) |
| 60 | : StochasticProcess1D(disc), x0_(std::move(x0)), riskFreeRate_(std::move(riskFreeTS)), |
| 61 | dividendYield_(std::move(dividendTS)), blackVolatility_(std::move(blackVolTS)), |
| 62 | forceDiscretization_(forceDiscretization), hasExternalLocalVol_(false), updated_(false), |
| 63 | isStrikeIndependent_(false) { |
| 64 | registerWith(h: x0_); |
| 65 | registerWith(h: riskFreeRate_); |
| 66 | registerWith(h: dividendYield_); |
| 67 | registerWith(h: blackVolatility_); |
| 68 | } |
| 69 | |
| 70 | Real GeneralizedBlackScholesProcess::x0() const { |
| 71 | return x0_->value(); |
| 72 | } |
| 73 | |
| 74 | Real GeneralizedBlackScholesProcess::drift(Time t, Real x) const { |
| 75 | Real sigma = diffusion(t,x); |
| 76 | // we could be more anticipatory if we know the right dt |
| 77 | // for which the drift will be used |
| 78 | Time t1 = t + 0.0001; |
| 79 | return riskFreeRate_->forwardRate(t1: t,t2: t1,comp: Continuous,freq: NoFrequency,extrapolate: true).rate() |
| 80 | - dividendYield_->forwardRate(t1: t,t2: t1,comp: Continuous,freq: NoFrequency,extrapolate: true).rate() |
| 81 | - 0.5 * sigma * sigma; |
| 82 | } |
| 83 | |
| 84 | Real GeneralizedBlackScholesProcess::diffusion(Time t, Real x) const { |
| 85 | return localVolatility()->localVol(t, underlyingLevel: x, extrapolate: true); |
| 86 | } |
| 87 | |
| 88 | Real GeneralizedBlackScholesProcess::apply(Real x0, Real dx) const { |
| 89 | return x0 * std::exp(x: dx); |
| 90 | } |
| 91 | |
| 92 | Real GeneralizedBlackScholesProcess::expectation(Time t0, |
| 93 | Real x0, |
| 94 | Time dt) const { |
| 95 | localVolatility(); // trigger update |
| 96 | if(isStrikeIndependent_ && !forceDiscretization_) { |
| 97 | // exact value for curves |
| 98 | return x0 * |
| 99 | std::exp(x: dt * (riskFreeRate_->forwardRate(t1: t0, t2: t0 + dt, comp: Continuous, |
| 100 | freq: NoFrequency, extrapolate: true).rate() - |
| 101 | dividendYield_->forwardRate( |
| 102 | t1: t0, t2: t0 + dt, comp: Continuous, freq: NoFrequency, extrapolate: true).rate())); |
| 103 | } else { |
| 104 | QL_FAIL("not implemented" ); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | Real GeneralizedBlackScholesProcess::stdDeviation(Time t0, Real x0, Time dt) const { |
| 109 | localVolatility(); // trigger update |
| 110 | if(isStrikeIndependent_ && !forceDiscretization_) { |
| 111 | // exact value for curves |
| 112 | return std::sqrt(x: variance(t0,x0,dt)); |
| 113 | } |
| 114 | else{ |
| 115 | return discretization_->diffusion(*this,t0,x0,dt); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | Real GeneralizedBlackScholesProcess::variance(Time t0, Real x0, Time dt) const { |
| 120 | localVolatility(); // trigger update |
| 121 | if(isStrikeIndependent_ && !forceDiscretization_) { |
| 122 | // exact value for curves |
| 123 | return blackVolatility_->blackVariance(t: t0 + dt, strike: 0.01) - |
| 124 | blackVolatility_->blackVariance(t: t0, strike: 0.01); |
| 125 | } |
| 126 | else{ |
| 127 | return discretization_->variance(*this,t0,x0,dt); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | Real GeneralizedBlackScholesProcess::evolve(Time t0, Real x0, |
| 132 | Time dt, Real dw) const { |
| 133 | localVolatility(); // trigger update |
| 134 | if (isStrikeIndependent_ && !forceDiscretization_) { |
| 135 | // exact value for curves |
| 136 | Real var = variance(t0, x0, dt); |
| 137 | Real drift = (riskFreeRate_->forwardRate(t1: t0, t2: t0 + dt, comp: Continuous, |
| 138 | freq: NoFrequency, extrapolate: true).rate() - |
| 139 | dividendYield_->forwardRate(t1: t0, t2: t0 + dt, comp: Continuous, |
| 140 | freq: NoFrequency, extrapolate: true).rate()) * |
| 141 | dt - |
| 142 | 0.5 * var; |
| 143 | return apply(x0, dx: std::sqrt(x: var) * dw + drift); |
| 144 | } else |
| 145 | return apply(x0, dx: discretization_->drift(*this, t0, x0, dt) + |
| 146 | stdDeviation(t0, x0, dt) * dw); |
| 147 | } |
| 148 | |
| 149 | Time GeneralizedBlackScholesProcess::time(const Date& d) const { |
| 150 | return riskFreeRate_->dayCounter().yearFraction( |
| 151 | d1: riskFreeRate_->referenceDate(), d2: d); |
| 152 | } |
| 153 | |
| 154 | void GeneralizedBlackScholesProcess::update() { |
| 155 | updated_ = false; |
| 156 | StochasticProcess1D::update(); |
| 157 | } |
| 158 | |
| 159 | const Handle<Quote>& |
| 160 | GeneralizedBlackScholesProcess::stateVariable() const { |
| 161 | return x0_; |
| 162 | } |
| 163 | |
| 164 | const Handle<YieldTermStructure>& |
| 165 | GeneralizedBlackScholesProcess::dividendYield() const { |
| 166 | return dividendYield_; |
| 167 | } |
| 168 | |
| 169 | const Handle<YieldTermStructure>& |
| 170 | GeneralizedBlackScholesProcess::riskFreeRate() const { |
| 171 | return riskFreeRate_; |
| 172 | } |
| 173 | |
| 174 | const Handle<BlackVolTermStructure>& |
| 175 | GeneralizedBlackScholesProcess::blackVolatility() const { |
| 176 | return blackVolatility_; |
| 177 | } |
| 178 | |
| 179 | const Handle<LocalVolTermStructure>& |
| 180 | GeneralizedBlackScholesProcess::localVolatility() const { |
| 181 | if (hasExternalLocalVol_) |
| 182 | return externalLocalVolTS_; |
| 183 | |
| 184 | if (!updated_) { |
| 185 | isStrikeIndependent_=true; |
| 186 | |
| 187 | // constant Black vol? |
| 188 | ext::shared_ptr<BlackConstantVol> constVol = |
| 189 | ext::dynamic_pointer_cast<BlackConstantVol>( |
| 190 | r: *blackVolatility()); |
| 191 | if (constVol != nullptr) { |
| 192 | // ok, the local vol is constant too. |
| 193 | localVolatility_.linkTo(h: ext::make_shared<LocalConstantVol>( |
| 194 | args: constVol->referenceDate(), |
| 195 | args: constVol->blackVol(t: 0.0, strike: x0_->value()), |
| 196 | args: constVol->dayCounter())); |
| 197 | updated_ = true; |
| 198 | return localVolatility_; |
| 199 | } |
| 200 | |
| 201 | // ok, so it's not constant. Maybe it's strike-independent? |
| 202 | ext::shared_ptr<BlackVarianceCurve> volCurve = |
| 203 | ext::dynamic_pointer_cast<BlackVarianceCurve>( |
| 204 | r: *blackVolatility()); |
| 205 | if (volCurve != nullptr) { |
| 206 | // ok, we can use the optimized algorithm |
| 207 | localVolatility_.linkTo(h: ext::make_shared<LocalVolCurve>( |
| 208 | args: Handle<BlackVarianceCurve>(volCurve))); |
| 209 | updated_ = true; |
| 210 | return localVolatility_; |
| 211 | } |
| 212 | |
| 213 | // ok, so it's strike-dependent. Never mind. |
| 214 | localVolatility_.linkTo( |
| 215 | h: ext::make_shared<LocalVolSurface>(args: blackVolatility_, args: riskFreeRate_, |
| 216 | args: dividendYield_, args: x0_->value())); |
| 217 | updated_ = true; |
| 218 | isStrikeIndependent_ = false; |
| 219 | return localVolatility_; |
| 220 | |
| 221 | } else { |
| 222 | return localVolatility_; |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | |
| 227 | // specific models |
| 228 | |
| 229 | BlackScholesProcess::BlackScholesProcess( |
| 230 | const Handle<Quote>& x0, |
| 231 | const Handle<YieldTermStructure>& riskFreeTS, |
| 232 | const Handle<BlackVolTermStructure>& blackVolTS, |
| 233 | const ext::shared_ptr<discretization>& d, |
| 234 | bool forceDiscretization) |
| 235 | : GeneralizedBlackScholesProcess( |
| 236 | x0, |
| 237 | // no dividend yield |
| 238 | Handle<YieldTermStructure>(ext::shared_ptr<YieldTermStructure>( |
| 239 | new FlatForward(0, NullCalendar(), 0.0, Actual365Fixed()))), |
| 240 | riskFreeTS, |
| 241 | blackVolTS, |
| 242 | d,forceDiscretization) {} |
| 243 | |
| 244 | |
| 245 | BlackScholesMertonProcess::BlackScholesMertonProcess( |
| 246 | const Handle<Quote>& x0, |
| 247 | const Handle<YieldTermStructure>& dividendTS, |
| 248 | const Handle<YieldTermStructure>& riskFreeTS, |
| 249 | const Handle<BlackVolTermStructure>& blackVolTS, |
| 250 | const ext::shared_ptr<discretization>& d, |
| 251 | bool forceDiscretization) |
| 252 | : GeneralizedBlackScholesProcess(x0,dividendTS,riskFreeTS,blackVolTS,d, |
| 253 | forceDiscretization) {} |
| 254 | |
| 255 | |
| 256 | BlackProcess::BlackProcess(const Handle<Quote>& x0, |
| 257 | const Handle<YieldTermStructure>& riskFreeTS, |
| 258 | const Handle<BlackVolTermStructure>& blackVolTS, |
| 259 | const ext::shared_ptr<discretization>& d, |
| 260 | bool forceDiscretization) |
| 261 | : GeneralizedBlackScholesProcess(x0,riskFreeTS,riskFreeTS,blackVolTS,d, |
| 262 | forceDiscretization) {} |
| 263 | |
| 264 | |
| 265 | GarmanKohlagenProcess::GarmanKohlagenProcess( |
| 266 | const Handle<Quote>& x0, |
| 267 | const Handle<YieldTermStructure>& foreignRiskFreeTS, |
| 268 | const Handle<YieldTermStructure>& domesticRiskFreeTS, |
| 269 | const Handle<BlackVolTermStructure>& blackVolTS, |
| 270 | const ext::shared_ptr<discretization>& d, |
| 271 | bool forceDiscretization) |
| 272 | : GeneralizedBlackScholesProcess(x0,foreignRiskFreeTS,domesticRiskFreeTS, |
| 273 | blackVolTS,d,forceDiscretization) {} |
| 274 | |
| 275 | } |
| 276 | |