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
33namespace 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

source code of quantlib/ql/processes/blackscholesprocess.cpp