/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 |