Coverage Report

Created: 2025-10-14 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}