Coverage Report

Created: 2025-10-14 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/experimental/lattices/extendedbinomialtree.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2001, 2002, 2003 Sadruddin Rejeb
5
 Copyright (C) 2003 Ferdinando Ametrano
6
 Copyright (C) 2005 StatPro Italia srl
7
 Copyright (C) 2008 John Maiden
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/experimental/lattices/extendedbinomialtree.hpp>
24
#include <ql/math/distributions/binomialdistribution.hpp>
25
26
namespace QuantLib {
27
28
    ExtendedJarrowRudd::ExtendedJarrowRudd(
29
                        const ext::shared_ptr<StochasticProcess1D>& process,
30
                        Time end, Size steps, Real)
31
0
    : ExtendedEqualProbabilitiesBinomialTree<ExtendedJarrowRudd>(
32
0
                                                        process, end, steps) {
33
        // drift removed
34
0
        up_ = process->stdDeviation(0.0, x0_, dt_);
35
0
    }
36
37
0
    Real ExtendedJarrowRudd::upStep(Time stepTime) const {
38
0
        return treeProcess_->stdDeviation(stepTime, x0_, dt_);
39
0
    }
40
41
42
43
    ExtendedCoxRossRubinstein::ExtendedCoxRossRubinstein(
44
                        const ext::shared_ptr<StochasticProcess1D>& process,
45
                        Time end, Size steps, Real)
46
0
    : ExtendedEqualJumpsBinomialTree<ExtendedCoxRossRubinstein>(
47
0
                                                        process, end, steps) {
48
49
0
        dx_ = process->stdDeviation(0.0, x0_, dt_);
50
0
        pu_ = 0.5 + 0.5*this->driftStep(0.0)/dx_;
51
0
        pd_ = 1.0 - pu_;
52
53
0
        QL_REQUIRE(pu_<=1.0, "negative probability");
54
0
        QL_REQUIRE(pu_>=0.0, "negative probability");
55
0
    }
56
57
0
    Real ExtendedCoxRossRubinstein::dxStep(Time stepTime) const {
58
0
        return this->treeProcess_->stdDeviation(stepTime, x0_, dt_);
59
0
    }
60
61
0
    Real ExtendedCoxRossRubinstein::probUp(Time stepTime) const {
62
0
        return 0.5 + 0.5*this->driftStep(stepTime)/dxStep(stepTime);
63
0
    }
64
65
66
    ExtendedAdditiveEQPBinomialTree::ExtendedAdditiveEQPBinomialTree(
67
                        const ext::shared_ptr<StochasticProcess1D>& process,
68
                        Time end, Size steps, Real)
69
0
    : ExtendedEqualProbabilitiesBinomialTree<ExtendedAdditiveEQPBinomialTree>(
70
0
                                                        process, end, steps) {
71
72
0
          up_ = - 0.5 * this->driftStep(0.0) + 0.5 *
73
0
            std::sqrt(4.0*process->variance(0.0, x0_, dt_)-
74
0
                      3.0*this->driftStep(0.0)*this->driftStep(0.0));
75
0
    }
76
77
0
    Real ExtendedAdditiveEQPBinomialTree::upStep(Time stepTime) const {
78
0
        return (- 0.5 * this->driftStep(stepTime) + 0.5 *
79
0
            std::sqrt(4.0*this->treeProcess_->variance(stepTime, x0_, dt_)-
80
0
            3.0*this->driftStep(stepTime)*this->driftStep(stepTime)));
81
0
    }
82
83
84
85
86
    ExtendedTrigeorgis::ExtendedTrigeorgis(
87
                        const ext::shared_ptr<StochasticProcess1D>& process,
88
                        Time end, Size steps, Real)
89
0
    : ExtendedEqualJumpsBinomialTree<ExtendedTrigeorgis>(process, end, steps) {
90
91
0
        dx_ = std::sqrt(process->variance(0.0, x0_, dt_)+
92
0
            this->driftStep(0.0)*this->driftStep(0.0));
93
0
        pu_ = 0.5 + 0.5*this->driftStep(0.0) / ExtendedTrigeorgis::dxStep(0.0);
94
0
        pd_ = 1.0 - pu_;
95
96
0
        QL_REQUIRE(pu_<=1.0, "negative probability");
97
0
        QL_REQUIRE(pu_>=0.0, "negative probability");
98
0
    }
99
100
0
    Real ExtendedTrigeorgis::dxStep(Time stepTime) const {
101
0
        return std::sqrt(this->treeProcess_->variance(stepTime, x0_, dt_)+
102
0
            this->driftStep(stepTime)*this->driftStep(stepTime));
103
0
    }
104
105
0
    Real ExtendedTrigeorgis::probUp(Time stepTime) const {
106
0
        return 0.5 + 0.5*this->driftStep(stepTime)/dxStep(stepTime);
107
0
    }
108
109
110
    ExtendedTian::ExtendedTian(
111
                        const ext::shared_ptr<StochasticProcess1D>& process,
112
                        Time end, Size steps, Real)
113
0
    : ExtendedBinomialTree<ExtendedTian>(process, end, steps) {
114
115
0
        Real q = std::exp(process->variance(0.0, x0_, dt_));
116
117
0
        Real r = std::exp(this->driftStep(0.0))*std::sqrt(q);
118
119
0
        up_ = 0.5 * r * q * (q + 1 + std::sqrt(q * q + 2 * q - 3));
120
0
        down_ = 0.5 * r * q * (q + 1 - std::sqrt(q * q + 2 * q - 3));
121
122
0
        pu_ = (r - down_) / (up_ - down_);
123
0
        pd_ = 1.0 - pu_;
124
125
        // doesn't work
126
        //     treeCentering_ = (up_+down_)/2.0;
127
        //     up_ = up_-treeCentering_;
128
129
0
        QL_REQUIRE(pu_<=1.0, "negative probability");
130
0
        QL_REQUIRE(pu_>=0.0, "negative probability");
131
0
    }
132
133
0
    Real ExtendedTian::underlying(Size i, Size index) const {
134
0
        Time stepTime = i*this->dt_;
135
0
        Real q = std::exp(this->treeProcess_->variance(stepTime, x0_, dt_));
136
0
        Real r = std::exp(this->driftStep(stepTime))*std::sqrt(q);
137
138
0
        Real up = 0.5 * r * q * (q + 1 + std::sqrt(q * q + 2 * q - 3));
139
0
        Real down = 0.5 * r * q * (q + 1 - std::sqrt(q * q + 2 * q - 3));
140
141
0
        return x0_ * std::pow(down, Real(BigInteger(i)-BigInteger(index)))
142
0
            * std::pow(up, Real(index));
143
0
    }
144
145
0
    Real ExtendedTian::probability(Size i, Size, Size branch) const {
146
0
        Time stepTime = i*this->dt_;
147
0
        Real q = std::exp(this->treeProcess_->variance(stepTime, x0_, dt_));
148
0
        Real r = std::exp(this->driftStep(stepTime))*std::sqrt(q);
149
150
0
        Real up = 0.5 * r * q * (q + 1 + std::sqrt(q * q + 2 * q - 3));
151
0
        Real down = 0.5 * r * q * (q + 1 - std::sqrt(q * q + 2 * q - 3));
152
153
0
        Real pu = (r - down) / (up - down);
154
0
        Real pd = 1.0 - pu;
155
156
0
        return (branch == 1 ? pu : pd);
157
0
    }
158
159
160
    ExtendedLeisenReimer::ExtendedLeisenReimer(const ext::shared_ptr<StochasticProcess1D>& process,
161
                                               Time end,
162
                                               Size steps,
163
                                               Real strike)
164
0
    : ExtendedBinomialTree<ExtendedLeisenReimer>(
165
0
          process, end, ((steps % 2) != 0U ? steps : steps + 1)),
166
0
      end_(end), oddSteps_((steps % 2) != 0U ? steps : steps + 1), strike_(strike) {
167
168
0
        QL_REQUIRE(strike>0.0, "strike " << strike << "must be positive");
169
0
        Real variance = process->variance(0.0, x0_, end);
170
171
0
        Real ermqdt = std::exp(this->driftStep(0.0) + 0.5*variance/oddSteps_);
172
0
        Real d2 = (std::log(x0_/strike) + this->driftStep(0.0)*oddSteps_ ) /
173
0
            std::sqrt(variance);
174
175
0
        pu_ = PeizerPrattMethod2Inversion(d2, oddSteps_);
176
0
        pd_ = 1.0 - pu_;
177
0
        Real pdash = PeizerPrattMethod2Inversion(d2+std::sqrt(variance),
178
0
                                                 oddSteps_);
179
0
        up_ = ermqdt * pdash / pu_;
180
0
        down_ = (ermqdt - pu_ * up_) / (1.0 - pu_);
181
0
    }
182
183
0
    Real ExtendedLeisenReimer::underlying(Size i, Size index) const {
184
0
        Time stepTime = i*this->dt_;
185
0
        Real variance = this->treeProcess_->variance(stepTime, x0_, end_);
186
0
        Real ermqdt = std::exp(this->driftStep(stepTime) + 0.5*variance/oddSteps_);
187
0
        Real d2 = (std::log(x0_/strike_) + this->driftStep(stepTime)*oddSteps_ ) /
188
0
            std::sqrt(variance);
189
190
0
        Real pu = PeizerPrattMethod2Inversion(d2, oddSteps_);
191
0
        Real pdash = PeizerPrattMethod2Inversion(d2+std::sqrt(variance),
192
0
            oddSteps_);
193
0
        Real up = ermqdt * pdash / pu;
194
0
        Real down = (ermqdt - pu * up) / (1.0 - pu);
195
196
0
        return x0_ * std::pow(down, Real(BigInteger(i)-BigInteger(index)))
197
0
            * std::pow(up, Real(index));
198
0
    }
199
200
0
    Real ExtendedLeisenReimer::probability(Size i, Size, Size branch) const {
201
0
        Time stepTime = i*this->dt_;
202
0
        Real variance = this->treeProcess_->variance(stepTime, x0_, end_);
203
0
        Real d2 = (std::log(x0_/strike_) + this->driftStep(stepTime)*oddSteps_ ) /
204
0
            std::sqrt(variance);
205
206
0
        Real pu = PeizerPrattMethod2Inversion(d2, oddSteps_);
207
0
        Real pd = 1.0 - pu;
208
209
0
        return (branch == 1 ? pu : pd);
210
0
    }
211
212
213
214
0
    Real ExtendedJoshi4::computeUpProb(Real k, Real dj) const {
215
0
        Real alpha = dj/(std::sqrt(8.0));
216
0
        Real alpha2 = alpha*alpha;
217
0
        Real alpha3 = alpha*alpha2;
218
0
        Real alpha5 = alpha3*alpha2;
219
0
        Real alpha7 = alpha5*alpha2;
220
0
        Real beta = -0.375*alpha-alpha3;
221
0
        Real gamma = (5.0/6.0)*alpha5 + (13.0/12.0)*alpha3
222
0
            +(25.0/128.0)*alpha;
223
0
        Real delta = -0.1025 *alpha- 0.9285 *alpha3
224
0
            -1.43 *alpha5 -0.5 *alpha7;
225
0
        Real p =0.5;
226
0
        Real rootk= std::sqrt(k);
227
0
        p+= alpha/rootk;
228
0
        p+= beta /(k*rootk);
229
0
        p+= gamma/(k*k*rootk);
230
        // delete next line to get results for j three tree
231
0
        p+= delta/(k*k*k*rootk);
232
0
        return p;
233
0
    }
234
235
    ExtendedJoshi4::ExtendedJoshi4(const ext::shared_ptr<StochasticProcess1D>& process,
236
                                   Time end,
237
                                   Size steps,
238
                                   Real strike)
239
0
    : ExtendedBinomialTree<ExtendedJoshi4>(process, end, ((steps % 2) != 0U ? steps : steps + 1)),
240
0
      end_(end), oddSteps_((steps % 2) != 0U ? steps : steps + 1), strike_(strike) {
241
242
0
        QL_REQUIRE(strike>0.0, "strike " << strike << "must be positive");
243
0
        Real variance = process->variance(0.0, x0_, end);
244
245
0
        Real ermqdt = std::exp(this->driftStep(0.0) + 0.5*variance/oddSteps_);
246
0
        Real d2 = (std::log(x0_/strike) + this->driftStep(0.0)*oddSteps_ ) /
247
0
            std::sqrt(variance);
248
249
0
        pu_ = computeUpProb((oddSteps_-1.0)/2.0,d2 );
250
0
        pd_ = 1.0 - pu_;
251
0
        Real pdash = computeUpProb((oddSteps_-1.0)/2.0,d2+std::sqrt(variance));
252
0
        up_ = ermqdt * pdash / pu_;
253
0
        down_ = (ermqdt - pu_ * up_) / (1.0 - pu_);
254
0
    }
255
256
0
    Real ExtendedJoshi4::underlying(Size i, Size index) const {
257
0
        Time stepTime = i*this->dt_;
258
0
        Real variance = this->treeProcess_->variance(stepTime, x0_, end_);
259
0
        Real ermqdt = std::exp(this->driftStep(stepTime) + 0.5*variance/oddSteps_);
260
0
        Real d2 = (std::log(x0_/strike_) + this->driftStep(stepTime)*oddSteps_ ) /
261
0
            std::sqrt(variance);
262
263
0
        Real pu = computeUpProb((oddSteps_-1.0)/2.0,d2 );
264
0
        Real pdash = computeUpProb((oddSteps_-1.0)/2.0,d2+std::sqrt(variance));
265
0
        Real up = ermqdt * pdash / pu;
266
0
        Real down = (ermqdt - pu * up) / (1.0 - pu);
267
268
0
        return x0_ * std::pow(down, Real(BigInteger(i)-BigInteger(index)))
269
0
            * std::pow(up, Real(index));
270
0
    }
271
272
0
    Real ExtendedJoshi4::probability(Size i, Size, Size branch) const {
273
0
        Time stepTime = i*this->dt_;
274
0
        Real variance = this->treeProcess_->variance(stepTime, x0_, end_);
275
0
        Real d2 = (std::log(x0_/strike_) + this->driftStep(stepTime)*oddSteps_ ) /
276
0
            std::sqrt(variance);
277
278
0
        Real pu = computeUpProb((oddSteps_-1.0)/2.0,d2 );
279
0
        Real pd = 1.0 - pu;
280
281
0
        return (branch == 1 ? pu : pd);
282
0
    }
283
284
}