/src/quantlib/ql/processes/blackscholesprocess.cpp
Line | Count | Source |
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 | | <https://www.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 | 0 | : StochasticProcess1D(ext::make_shared<EulerDiscretization>()), x0_(std::move(x0)), |
42 | 0 | riskFreeRate_(std::move(riskFreeTS)), dividendYield_(std::move(dividendTS)), |
43 | 0 | blackVolatility_(std::move(blackVolTS)), externalLocalVolTS_(std::move(localVolTS)), |
44 | 0 | forceDiscretization_(false), hasExternalLocalVol_(true), updated_(false), |
45 | 0 | isStrikeIndependent_(false) { |
46 | 0 | registerWith(x0_); |
47 | 0 | registerWith(riskFreeRate_); |
48 | 0 | registerWith(dividendYield_); |
49 | 0 | registerWith(blackVolatility_); |
50 | 0 | registerWith(externalLocalVolTS_); |
51 | 0 | } |
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 | 987 | : StochasticProcess1D(disc), x0_(std::move(x0)), riskFreeRate_(std::move(riskFreeTS)), |
61 | 987 | dividendYield_(std::move(dividendTS)), blackVolatility_(std::move(blackVolTS)), |
62 | 987 | forceDiscretization_(forceDiscretization), hasExternalLocalVol_(false), updated_(false), |
63 | 987 | isStrikeIndependent_(false) { |
64 | 987 | registerWith(x0_); |
65 | 987 | registerWith(riskFreeRate_); |
66 | 987 | registerWith(dividendYield_); |
67 | 987 | registerWith(blackVolatility_); |
68 | 987 | } |
69 | | |
70 | 0 | Real GeneralizedBlackScholesProcess::x0() const { |
71 | 0 | return x0_->value(); |
72 | 0 | } |
73 | | |
74 | 0 | Real GeneralizedBlackScholesProcess::drift(Time t, Real x) const { |
75 | 0 | 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 | 0 | Time t1 = t + 0.0001; |
79 | 0 | return riskFreeRate_->forwardRate(t,t1,Continuous,NoFrequency,true).rate() |
80 | 0 | - dividendYield_->forwardRate(t,t1,Continuous,NoFrequency,true).rate() |
81 | 0 | - 0.5 * sigma * sigma; |
82 | 0 | } |
83 | | |
84 | 0 | Real GeneralizedBlackScholesProcess::diffusion(Time t, Real x) const { |
85 | 0 | return localVolatility()->localVol(t, x, true); |
86 | 0 | } |
87 | | |
88 | 0 | Real GeneralizedBlackScholesProcess::apply(Real x0, Real dx) const { |
89 | 0 | return x0 * std::exp(dx); |
90 | 0 | } |
91 | | |
92 | | Real GeneralizedBlackScholesProcess::expectation(Time t0, |
93 | | Real x0, |
94 | 0 | Time dt) const { |
95 | 0 | localVolatility(); // trigger update |
96 | 0 | if(isStrikeIndependent_ && !forceDiscretization_) { |
97 | | // exact value for curves |
98 | 0 | return x0 * |
99 | 0 | std::exp(dt * (riskFreeRate_->forwardRate(t0, t0 + dt, Continuous, |
100 | 0 | NoFrequency, true).rate() - |
101 | 0 | dividendYield_->forwardRate( |
102 | 0 | t0, t0 + dt, Continuous, NoFrequency, true).rate())); |
103 | 0 | } else { |
104 | 0 | QL_FAIL("not implemented"); |
105 | 0 | } |
106 | 0 | } |
107 | | |
108 | 0 | Real GeneralizedBlackScholesProcess::stdDeviation(Time t0, Real x0, Time dt) const { |
109 | 0 | localVolatility(); // trigger update |
110 | 0 | if(isStrikeIndependent_ && !forceDiscretization_) { |
111 | | // exact value for curves |
112 | 0 | return std::sqrt(variance(t0,x0,dt)); |
113 | 0 | } |
114 | 0 | else{ |
115 | 0 | return discretization_->diffusion(*this,t0,x0,dt); |
116 | 0 | } |
117 | 0 | } |
118 | | |
119 | 0 | Real GeneralizedBlackScholesProcess::variance(Time t0, Real x0, Time dt) const { |
120 | 0 | localVolatility(); // trigger update |
121 | 0 | if(isStrikeIndependent_ && !forceDiscretization_) { |
122 | | // exact value for curves |
123 | 0 | return blackVolatility_->blackVariance(t0 + dt, 0.01) - |
124 | 0 | blackVolatility_->blackVariance(t0, 0.01); |
125 | 0 | } |
126 | 0 | else{ |
127 | 0 | return discretization_->variance(*this,t0,x0,dt); |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | | Real GeneralizedBlackScholesProcess::evolve(Time t0, Real x0, |
132 | 0 | Time dt, Real dw) const { |
133 | 0 | localVolatility(); // trigger update |
134 | 0 | if (isStrikeIndependent_ && !forceDiscretization_) { |
135 | | // exact value for curves |
136 | 0 | Real var = variance(t0, x0, dt); |
137 | 0 | Real drift = (riskFreeRate_->forwardRate(t0, t0 + dt, Continuous, |
138 | 0 | NoFrequency, true).rate() - |
139 | 0 | dividendYield_->forwardRate(t0, t0 + dt, Continuous, |
140 | 0 | NoFrequency, true).rate()) * |
141 | 0 | dt - |
142 | 0 | 0.5 * var; |
143 | 0 | return apply(x0, std::sqrt(var) * dw + drift); |
144 | 0 | } else |
145 | 0 | return apply(x0, discretization_->drift(*this, t0, x0, dt) + |
146 | 0 | stdDeviation(t0, x0, dt) * dw); |
147 | 0 | } |
148 | | |
149 | 0 | Time GeneralizedBlackScholesProcess::time(const Date& d) const { |
150 | 0 | return riskFreeRate_->dayCounter().yearFraction( |
151 | 0 | riskFreeRate_->referenceDate(), d); |
152 | 0 | } |
153 | | |
154 | 830k | void GeneralizedBlackScholesProcess::update() { |
155 | 830k | updated_ = false; |
156 | 830k | StochasticProcess1D::update(); |
157 | 830k | } |
158 | | |
159 | | const Handle<Quote>& |
160 | 0 | GeneralizedBlackScholesProcess::stateVariable() const { |
161 | 0 | return x0_; |
162 | 0 | } |
163 | | |
164 | | const Handle<YieldTermStructure>& |
165 | 0 | GeneralizedBlackScholesProcess::dividendYield() const { |
166 | 0 | return dividendYield_; |
167 | 0 | } |
168 | | |
169 | | const Handle<YieldTermStructure>& |
170 | 0 | GeneralizedBlackScholesProcess::riskFreeRate() const { |
171 | 0 | return riskFreeRate_; |
172 | 0 | } |
173 | | |
174 | | const Handle<BlackVolTermStructure>& |
175 | 0 | GeneralizedBlackScholesProcess::blackVolatility() const { |
176 | 0 | return blackVolatility_; |
177 | 0 | } |
178 | | |
179 | | const Handle<LocalVolTermStructure>& |
180 | 0 | GeneralizedBlackScholesProcess::localVolatility() const { |
181 | 0 | if (hasExternalLocalVol_) |
182 | 0 | return externalLocalVolTS_; |
183 | | |
184 | 0 | if (!updated_) { |
185 | 0 | isStrikeIndependent_=true; |
186 | | |
187 | | // constant Black vol? |
188 | 0 | ext::shared_ptr<BlackConstantVol> constVol = |
189 | 0 | ext::dynamic_pointer_cast<BlackConstantVol>( |
190 | 0 | *blackVolatility()); |
191 | 0 | if (constVol != nullptr) { |
192 | | // ok, the local vol is constant too. |
193 | 0 | localVolatility_.linkTo(ext::make_shared<LocalConstantVol>( |
194 | 0 | constVol->referenceDate(), |
195 | 0 | constVol->blackVol(0.0, x0_->value()), |
196 | 0 | constVol->dayCounter())); |
197 | 0 | updated_ = true; |
198 | 0 | return localVolatility_; |
199 | 0 | } |
200 | | |
201 | | // ok, so it's not constant. Maybe it's strike-independent? |
202 | 0 | ext::shared_ptr<BlackVarianceCurve> volCurve = |
203 | 0 | ext::dynamic_pointer_cast<BlackVarianceCurve>( |
204 | 0 | *blackVolatility()); |
205 | 0 | if (volCurve != nullptr) { |
206 | | // ok, we can use the optimized algorithm |
207 | 0 | localVolatility_.linkTo(ext::make_shared<LocalVolCurve>( |
208 | 0 | Handle<BlackVarianceCurve>(volCurve))); |
209 | 0 | updated_ = true; |
210 | 0 | return localVolatility_; |
211 | 0 | } |
212 | | |
213 | | // ok, so it's strike-dependent. Never mind. |
214 | 0 | localVolatility_.linkTo( |
215 | 0 | ext::make_shared<LocalVolSurface>(blackVolatility_, riskFreeRate_, |
216 | 0 | dividendYield_, x0_->value())); |
217 | 0 | updated_ = true; |
218 | 0 | isStrikeIndependent_ = false; |
219 | 0 | return localVolatility_; |
220 | |
|
221 | 0 | } else { |
222 | 0 | return localVolatility_; |
223 | 0 | } |
224 | 0 | } |
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 | 0 | : GeneralizedBlackScholesProcess( |
236 | 0 | x0, |
237 | | // no dividend yield |
238 | 0 | Handle<YieldTermStructure>(ext::shared_ptr<YieldTermStructure>( |
239 | 0 | new FlatForward(0, NullCalendar(), 0.0, Actual365Fixed()))), |
240 | 0 | riskFreeTS, |
241 | 0 | blackVolTS, |
242 | 0 | 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 | 987 | : GeneralizedBlackScholesProcess(x0,dividendTS,riskFreeTS,blackVolTS,d, |
253 | 987 | 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 | 0 | : GeneralizedBlackScholesProcess(x0,riskFreeTS,riskFreeTS,blackVolTS,d, |
262 | 0 | 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 | 0 | : GeneralizedBlackScholesProcess(x0,foreignRiskFreeTS,domesticRiskFreeTS, |
273 | 0 | blackVolTS,d,forceDiscretization) {} |
274 | | |
275 | | } |