Coverage Report

Created: 2026-02-03 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/processes/gsrprocesscore.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2015 Peter Caspers
5
6
 This file is part of QuantLib, a free-software/open-source library
7
 for financial quantitative analysts and developers - http://quantlib.org/
8
9
 QuantLib is free software: you can redistribute it and/or modify it
10
 under the terms of the QuantLib license.  You should have received a
11
 copy of the license along with this program; if not, please email
12
 <quantlib-dev@lists.sf.net>. The license is also available online at
13
 <https://www.quantlib.org/license.shtml>.
14
15
 This program is distributed in the hope that it will be useful, but WITHOUT
16
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 FOR A PARTICULAR PURPOSE.  See the license for more details.
18
*/
19
20
#include <ql/processes/gsrprocesscore.hpp>
21
#include <cmath>
22
23
using std::exp;
24
using std::pow;
25
26
namespace QuantLib::detail {
27
28
GsrProcessCore::GsrProcessCore(Array times, Array vols,
29
                               Array reversions, const Real T)
30
0
    : times_(std::move(times)), vols_(std::move(vols)), reversions_(std::move(reversions)),
31
0
      T_(T), revZero_(reversions_.size(), false) {
32
0
    flushCache();
33
0
    checkTimesVolsReversions();
34
0
}
35
36
0
void GsrProcessCore::setTimes(Array times) {
37
0
    times_ = std::move(times);
38
0
    checkTimesVolsReversions();
39
0
}
40
41
0
void GsrProcessCore::setVols(Array vols) {
42
0
    vols_ = std::move(vols);
43
0
    checkTimesVolsReversions();
44
0
}
45
46
0
void GsrProcessCore::setReversions(Array reversions) {
47
0
    reversions_ = std::move(reversions);
48
0
    checkTimesVolsReversions();
49
0
}
50
51
0
void GsrProcessCore::checkTimesVolsReversions() const {
52
0
    QL_REQUIRE(times_.size() == vols_.size() - 1,
53
0
               "number of volatilities ("
54
0
                   << vols_.size() << ") compared to number of times ("
55
0
                   << times_.size() << " must be bigger by one");
56
0
    QL_REQUIRE(times_.size() == reversions_.size() - 1 || reversions_.size() == 1,
57
0
               "number of reversions ("
58
0
                   << vols_.size() << ") compared to number of times ("
59
0
                   << times_.size() << " must be bigger by one, or exactly "
60
0
                                       "1 reversion must be given");
61
0
    for (int i = 0; i < ((int)times_.size()) - 1; i++)
62
0
        QL_REQUIRE(times_[i] < times_[i + 1], "times must be increasing ("
63
0
                                                << times_[i] << "@" << i << " , "
64
0
                                                << times_[i + 1] << "@" << i + 1
65
0
                                                << ")");
66
0
}
67
68
0
void GsrProcessCore::flushCache() const {
69
0
    for (int i = 0; i < (int)reversions_.size(); i++)
70
        // small reversions cause numerical problems, so we keep them
71
        // away from zero
72
0
        if (std::fabs(reversions_[i]) < 1E-4)
73
0
            revZero_[i] = true;
74
0
        else
75
0
            revZero_[i] = false;
76
0
    cache1_.clear();
77
0
    cache2a_.clear();
78
0
    cache2b_.clear();
79
0
    cache3_.clear();
80
0
    cache4_.clear();
81
0
    cache5_.clear();
82
0
}
83
84
Real GsrProcessCore::expectation_x0dep_part(const Time w, const Real xw,
85
0
                                            const Time dt) const {
86
0
    Real t = w + dt;
87
0
    std::pair<Real, Real> key;
88
0
    key = std::make_pair(w, t);
89
0
    auto k = cache1_.find(key);
90
0
    if (k != cache1_.end())
91
0
        return xw * (k->second);
92
    // A(w,t)x(w)
93
0
    Real res2 = 1.0;
94
0
    for (int i = lowerIndex(w); i <= upperIndex(t) - 1; i++) {
95
0
        res2 *= exp(-rev(i) * (cappedTime(i + 1, t) - flooredTime(i, w)));
96
0
    }
97
0
    cache1_.insert(std::make_pair(key, res2));
98
0
    return res2 * xw;
99
0
}
100
101
Real GsrProcessCore::expectation_rn_part(const Time w,
102
0
                                         const Time dt) const {
103
104
0
    Real t = w + dt;
105
106
0
    std::pair<Real, Real> key;
107
0
    key = std::make_pair(w, t);
108
0
    auto k =
109
0
        cache2a_.find(key);
110
0
    if (k != cache2a_.end())
111
0
        return k->second;
112
113
0
    Real res = 0.0;
114
115
    // \int A(s,t)y(s)
116
0
    for (int k = lowerIndex(w); k <= upperIndex(t) - 1; k++) {
117
        // l<k
118
0
        for (int l = 0; l <= k - 1; l++) {
119
0
            Real res2 = 1.0;
120
            // alpha_l
121
0
            res2 *= revZero(l) ? Real(vol(l) * vol(l) * (time2(l + 1) - time2(l)))
122
0
                               : vol(l) * vol(l) / (2.0 * rev(l)) *
123
0
                                     (1.0 - exp(-2.0 * rev(l) *
124
0
                                                (time2(l + 1) - time2(l))));
125
            // zeta_i (i>k)
126
0
            for (int i = k + 1; i <= upperIndex(t) - 1; i++)
127
0
                res2 *= exp(-rev(i) * (cappedTime(i + 1, t) - time2(i)));
128
            // beta_j (j<k)
129
0
            for (int j = l + 1; j <= k - 1; j++)
130
0
                res2 *= exp(-2.0 * rev(j) * (time2(j + 1) - time2(j)));
131
            // zeta_k beta_k
132
0
            res2 *=
133
0
                revZero(k)
134
0
                    ? Real(2.0 * time2(k) - flooredTime(k, w) -
135
0
                          cappedTime(k + 1, t) -
136
0
                          2.0 * (time2(k) - cappedTime(k + 1, t)))
137
0
                    : Real((exp(rev(k) * (2.0 * time2(k) - flooredTime(k, w) -
138
0
                                     cappedTime(k + 1, t))) -
139
0
                       exp(2.0 * rev(k) * (time2(k) - cappedTime(k + 1, t)))) /
140
0
                          rev(k));
141
            // add to sum
142
0
            res += res2;
143
0
        }
144
        // l=k
145
0
        Real res2 = 1.0;
146
        // alpha_k zeta_k
147
0
        res2 *=
148
0
            revZero(k)
149
0
                ? Real(vol(k) * vol(k) / 4.0 *
150
0
                      (4.0 * pow(cappedTime(k + 1, t) - time2(k), 2.0) -
151
0
                       (pow(flooredTime(k, w) - 2.0 * time2(k) +
152
0
                                cappedTime(k + 1, t),
153
0
                            2.0) +
154
0
                        pow(cappedTime(k + 1, t) - flooredTime(k, w), 2.0))))
155
0
                : Real(vol(k) * vol(k) / (2.0 * rev(k) * rev(k)) *
156
0
                      (exp(-2.0 * rev(k) * (cappedTime(k + 1, t) - time2(k))) +
157
0
                       1.0 -
158
0
                       (exp(-rev(k) * (flooredTime(k, w) - 2.0 * time2(k) +
159
0
                                       cappedTime(k + 1, t))) +
160
0
                        exp(-rev(k) *
161
0
                            (cappedTime(k + 1, t) - flooredTime(k, w))))));
162
        // zeta_i (i>k)
163
0
        for (int i = k + 1; i <= upperIndex(t) - 1; i++)
164
0
            res2 *= exp(-rev(i) * (cappedTime(i + 1, t) - time2(i)));
165
        // no beta_j in this case ...
166
0
        res += res2;
167
0
    }
168
169
0
    cache2a_.insert(std::make_pair(key, res));
170
171
0
    return res;
172
0
} // expectation_rn_part
173
174
Real GsrProcessCore::expectation_tf_part(const Time w,
175
0
                                         const Time dt) const {
176
177
0
    Real t = w + dt;
178
179
0
    std::pair<Real, Real> key;
180
0
    key = std::make_pair(w, t);
181
0
    auto k =
182
0
        cache2b_.find(key);
183
0
    if (k != cache2b_.end())
184
0
        return k->second;
185
186
0
    Real res = 0.0;
187
    // int -A(s,t) \sigma^2 G(s,T)
188
0
    for (int k = lowerIndex(w); k <= upperIndex(t) - 1; k++) {
189
0
        Real res2 = 0.0;
190
        // l>k
191
0
        for (int l = k + 1; l <= upperIndex(T_) - 1; l++) {
192
0
            Real res3 = 1.0;
193
            // eta_l
194
0
            res3 *= revZero(l)
195
0
                        ? Real(cappedTime(l + 1, T_) - time2(l))
196
0
                        : (1.0 -
197
0
                           exp(-rev(l) * (cappedTime(l + 1, T_) - time2(l)))) /
198
0
                              rev(l);
199
            // zeta_i (i>k)
200
0
            for (int i = k + 1; i <= upperIndex(t) - 1; i++)
201
0
                res3 *= exp(-rev(i) * (cappedTime(i + 1, t) - time2(i)));
202
            // gamma_j (j>k)
203
0
            for (int j = k + 1; j <= l - 1; j++)
204
0
                res3 *= exp(-rev(j) * (time2(j + 1) - time2(j)));
205
            // zeta_k gamma_k
206
0
            res3 *=
207
0
                revZero(k)
208
0
                    ? Real((cappedTime(k + 1, t) - time2(k + 1) -
209
0
                       (2.0 * flooredTime(k, w) - cappedTime(k + 1, t) -
210
0
                        time2(k + 1))) /
211
0
                          2.0)
212
0
                    : Real((exp(rev(k) * (cappedTime(k + 1, t) - time2(k + 1))) -
213
0
                       exp(rev(k) * (2.0 * flooredTime(k, w) -
214
0
                                     cappedTime(k + 1, t) - time2(k + 1)))) /
215
0
                          (2.0 * rev(k)));
216
            // add to sum
217
0
            res2 += res3;
218
0
        }
219
        // l=k
220
0
        Real res3 = 1.0;
221
        // eta_k zeta_k
222
0
        res3 *=
223
0
            revZero(k)
224
0
                ? Real((-pow(cappedTime(k + 1, t) - cappedTime(k + 1, T_), 2.0) -
225
0
                   2.0 * pow(cappedTime(k + 1, t) - flooredTime(k, w), 2.0) +
226
0
                   pow(2.0 * flooredTime(k, w) - cappedTime(k + 1, T_) -
227
0
                           cappedTime(k + 1, t),
228
0
                       2.0)) /
229
0
                      4.0)
230
0
                : Real((2.0 - exp(rev(k) *
231
0
                             (cappedTime(k + 1, t) - cappedTime(k + 1, T_))) -
232
0
                   (2.0 * exp(-rev(k) *
233
0
                              (cappedTime(k + 1, t) - flooredTime(k, w))) -
234
0
                    exp(rev(k) *
235
0
                        (2.0 * flooredTime(k, w) - cappedTime(k + 1, T_) -
236
0
                         cappedTime(k + 1, t))))) /
237
0
                      (2.0 * rev(k) * rev(k)));
238
        // zeta_i (i>k)
239
0
        for (int i = k + 1; i <= upperIndex(t) - 1; i++)
240
0
            res3 *= exp(-rev(i) * (cappedTime(i + 1, t) - time2(i)));
241
        // no gamma_j in this case ...
242
0
        res2 += res3;
243
        // add to main accumulator
244
0
        res += -vol(k) * vol(k) * res2;
245
0
    }
246
247
0
    cache2b_.insert(std::make_pair(key, res));
248
249
0
    return res;
250
0
} // expectation_tf_part
251
252
0
Real GsrProcessCore::variance(const Time w, const Time dt) const {
253
254
0
    Real t = w + dt;
255
256
0
    std::pair<Real, Real> key;
257
0
    key = std::make_pair(w, t);
258
0
    auto k = cache3_.find(key);
259
0
    if (k != cache3_.end())
260
0
        return k->second;
261
262
0
    Real res = 0.0;
263
0
    for (int k = lowerIndex(w); k <= upperIndex(t) - 1; k++) {
264
0
        Real res2 = vol(k) * vol(k);
265
        // zeta_k^2
266
0
        res2 *= revZero(k)
267
0
                    ? Real(-(flooredTime(k, w) - cappedTime(k + 1, t)))
268
0
                    : (1.0 - exp(2.0 * rev(k) *
269
0
                                 (flooredTime(k, w) - cappedTime(k + 1, t)))) /
270
0
                          (2.0 * rev(k));
271
        // zeta_i (i>k)
272
0
        for (int i = k + 1; i <= upperIndex(t) - 1; i++) {
273
0
            res2 *= exp(-2.0 * rev(i) * (cappedTime(i + 1, t) - time2(i)));
274
0
        }
275
0
        res += res2;
276
0
    }
277
278
0
    cache3_.insert(std::make_pair(key, res));
279
0
    return res;
280
0
}
281
282
0
Real GsrProcessCore::y(const Time t) const {
283
0
    Real key;
284
0
    key = t;
285
0
    auto k = cache4_.find(key);
286
0
    if (k != cache4_.end())
287
0
        return k->second;
288
289
0
    Real res = 0.0;
290
0
    for (int i = 0; i <= upperIndex(t) - 1; i++) {
291
0
        Real res2 = 1.0;
292
0
        for (int j = i + 1; j <= upperIndex(t) - 1; j++) {
293
0
            res2 *= exp(-2.0 * rev(j) * (cappedTime(j + 1, t) - time2(j)));
294
0
        }
295
0
        res2 *= revZero(i) ? Real(vol(i) * vol(i) * (cappedTime(i + 1, t) - time2(i)))
296
0
                           : (vol(i) * vol(i) / (2.0 * rev(i)) *
297
0
                              (1.0 - exp(-2.0 * rev(i) *
298
0
                                         (cappedTime(i + 1, t) - time2(i)))));
299
0
        res += res2;
300
0
    }
301
302
0
    cache4_.insert(std::make_pair(key, res));
303
0
    return res;
304
0
}
305
306
0
Real GsrProcessCore::G(const Time t, const Time w) const {
307
0
    std::pair<Real, Real> key;
308
0
    key = std::make_pair(w, t);
309
0
    auto k = cache5_.find(key);
310
0
    if (k != cache5_.end())
311
0
        return k->second;
312
313
0
    Real res = 0.0;
314
0
    for (int i = lowerIndex(t); i <= upperIndex(w) - 1; i++) {
315
0
        Real res2 = 1.0;
316
0
        for (int j = lowerIndex(t); j <= i - 1; j++) {
317
0
            res2 *= exp(-rev(j) * (time2(j + 1) - flooredTime(j, t)));
318
0
        }
319
0
        res2 *= revZero(i) ? Real(cappedTime(i + 1, w) - flooredTime(i, t))
320
0
                           : (1.0 - exp(-rev(i) * (cappedTime(i + 1, w) -
321
0
                                                   flooredTime(i, t)))) /
322
0
                                 rev(i);
323
0
        res += res2;
324
0
    }
325
326
0
    cache5_.insert(std::make_pair(key, res));
327
0
    return res;
328
0
}
329
330
0
int GsrProcessCore::lowerIndex(const Time t) const {
331
0
    return static_cast<int>(std::upper_bound(times_.begin(), times_.end(), t) -
332
0
                            times_.begin());
333
0
}
334
335
0
int GsrProcessCore::upperIndex(const Time t) const {
336
0
    if (t < QL_MIN_POSITIVE_REAL)
337
0
        return 0;
338
0
    return static_cast<int>(
339
0
               std::upper_bound(times_.begin(), times_.end(), t - QL_EPSILON) -
340
0
               times_.begin()) +
341
0
           1;
342
0
}
343
344
0
Real GsrProcessCore::cappedTime(const Size index, const Real cap) const {
345
0
    return cap != Null<Real>() ? std::min(cap, time2(index)) : time2(index);
346
0
}
347
348
Real GsrProcessCore::flooredTime(const Size index,
349
0
                                 const Real floor) const {
350
0
    return floor != Null<Real>() ? std::max(floor, time2(index)) : time2(index);
351
0
}
352
353
0
Real GsrProcessCore::time2(const Size index) const {
354
0
    if (index == 0)
355
0
        return 0.0;
356
0
    if (index > times_.size())
357
0
        return T_; // FIXME how to ensure that forward
358
                   // measure time is geq all times
359
                   // given
360
0
    return times_[index - 1];
361
0
}
362
363
0
Real GsrProcessCore::vol(const Size index) const {
364
0
    if (index >= vols_.size())
365
0
        return vols_.back();
366
0
    return vols_[index];
367
0
}
368
369
0
Real GsrProcessCore::rev(const Size index) const {
370
0
    if (index >= reversions_.size())
371
0
        return reversions_.back();
372
0
    return reversions_[index];
373
0
}
374
375
0
bool GsrProcessCore::revZero(const Size index) const {
376
0
    if (index >= revZero_.size())
377
0
        return revZero_.back();
378
0
    return revZero_[index];
379
0
}
380
381
} // namesapce QuantLib