Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
lgmvectorised.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20
21#include <ql/instruments/overnightindexedswap.hpp>
22#include <ql/instruments/vanillaswap.hpp>
23
24#include <ql/cashflows/fixedratecoupon.hpp>
25#include <ql/cashflows/iborcoupon.hpp>
26#include <ql/cashflows/overnightindexedcoupon.hpp>
27#include <ql/indexes/iborindex.hpp>
28#include <ql/indexes/swapindex.hpp>
29
30namespace QuantExt {
31
33 const Handle<YieldTermStructure>& discountCurve) const {
34 QL_REQUIRE(t >= 0.0, "t (" << t << ") >= 0 required in LGMVectorised::numeraire");
35 RandomVariable Ht(x.size(), p_->H(t));
36 return exp(Ht * x + RandomVariable(x.size(), 0.5 * p_->zeta(t)) * Ht * Ht) /
38 (discountCurve.empty() ? p_->termStructure()->discount(t) : discountCurve->discount(t)));
39}
40
41RandomVariable LgmVectorised::discountBond(const Time t, const Time T, const RandomVariable& x,
42 const Handle<YieldTermStructure>& discountCurve) const {
43 if (QuantLib::close_enough(t, T))
44 return RandomVariable(x.size(), 1.0);
45 QL_REQUIRE(T >= t && t >= 0.0, "T(" << T << ") >= t(" << t << ") >= 0 required in LGMVectorised::discountBond");
46 RandomVariable Ht(x.size(), p_->H(t));
47 RandomVariable HT(x.size(), p_->H(T));
48 return RandomVariable(x.size(),
49 (discountCurve.empty() ? p_->termStructure()->discount(T) / p_->termStructure()->discount(t)
50 : discountCurve->discount(T) / discountCurve->discount(t))) *
51 exp(-(HT - Ht) * x - RandomVariable(x.size(), 0.5 * p_->zeta(t)) * (HT * HT - Ht * Ht));
52}
53
55 const Handle<YieldTermStructure>& discountCurve) const {
56 if (QuantLib::close_enough(t, T))
57 return RandomVariable(x.size(), 1.0) / numeraire(t, x, discountCurve);
58 QL_REQUIRE(T >= t && t >= 0.0,
59 "T(" << T << ") >= t(" << t << ") >= 0 required in LGMVectorised::reducedDiscountBond");
60 RandomVariable HT(x.size(), p_->H(T));
61 return RandomVariable(x.size(),
62 (discountCurve.empty() ? p_->termStructure()->discount(T) : discountCurve->discount(T))) *
63 exp(-HT * x - RandomVariable(x.size(), 0.5 * p_->zeta(t)) * HT * HT);
64}
65
66RandomVariable LgmVectorised::discountBondOption(Option::Type type, const Real K, const Time t, const Time S,
67 const Time T, const RandomVariable& x,
68 const Handle<YieldTermStructure>& discountCurve) const {
69 QL_REQUIRE(T > S && S >= t && t >= 0.0,
70 "T(" << T << ") > S(" << S << ") >= t(" << t << ") >= 0 required in LGMVectorised::discountBondOption");
71 RandomVariable w(x.size(), type == Option::Call ? 1.0 : -1.0);
72 RandomVariable pS = discountBond(t, S, x, discountCurve);
73 RandomVariable pT = discountBond(t, T, x, discountCurve);
74 // slight generalization of Lichters, Stamm, Gallagher 11.2.1
75 // with t < S, SSRN: https://ssrn.com/abstract=2246054
76 RandomVariable sigma(x.size(), std::sqrt(p_->zeta(t)) * (p_->H(T) - p_->H(S)));
78 log(pT / (RandomVariable(x.size(), K) * pS)) / sigma + RandomVariable(x.size(), 0.5) * sigma * sigma;
79 RandomVariable dm = dp - sigma;
80 return w * (pT * normalCdf(w * dp) - pS * RandomVariable(x.size(), K) * normalCdf(w * dm));
81}
82
83RandomVariable LgmVectorised::fixing(const QuantLib::ext::shared_ptr<InterestRateIndex>& index, const Date& fixingDate,
84 const Time t, const RandomVariable& x) const {
85
86 // handle case where fixing is deterministic
87
88 Date today = Settings::instance().evaluationDate();
89 if (fixingDate <= today)
90 return RandomVariable(x.size(), index->fixing(fixingDate));
91
92 // handle stochastic fixing
93
94 if (auto ibor = QuantLib::ext::dynamic_pointer_cast<IborIndex>(index)) {
95
96 // Ibor Index
97
98 Date d1 = ibor->valueDate(fixingDate);
99 Date d2 = ibor->maturityDate(d1);
100 Time T1 = std::max(t, p_->termStructure()->timeFromReference(d1));
101 Time T2 = std::max(T1, p_->termStructure()->timeFromReference(d2));
102 Time dt = ibor->dayCounter().yearFraction(d1, d2);
103 // reducedDiscountBond is faster to compute, there we use this here instead of discountBond
104 RandomVariable disc1 = reducedDiscountBond(t, T1, x, ibor->forwardingTermStructure());
105 RandomVariable disc2 = reducedDiscountBond(t, T2, x, ibor->forwardingTermStructure());
106 return (disc1 / disc2 - RandomVariable(x.size(), 1.0)) / RandomVariable(x.size(), dt);
107 } else if (auto swap = QuantLib::ext::dynamic_pointer_cast<SwapIndex>(index)) {
108
109 // Swap Index
110
111 auto swapDiscountCurve =
112 swap->exogenousDiscount() ? swap->discountingTermStructure() : swap->forwardingTermStructure();
113 Leg floatingLeg, fixedLeg;
114 if (auto ois = QuantLib::ext::dynamic_pointer_cast<OvernightIndexedSwapIndex>(index)) {
115 auto underlying = ois->underlyingSwap(fixingDate);
116 floatingLeg = underlying->overnightLeg();
117 fixedLeg = underlying->fixedLeg();
118 } else {
119 auto underlying = swap->underlyingSwap(fixingDate);
120 floatingLeg = underlying->floatingLeg();
121 fixedLeg = underlying->fixedLeg();
122 }
123 RandomVariable numerator(x.size(), 0.0), denominator(x.size(), 0.0);
124 for (auto const& c : floatingLeg) {
125 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<IborCoupon>(c)) {
126 Date fixingValueDate = swap->iborIndex()->fixingCalendar().advance(
127 cpn->fixingDate(), swap->iborIndex()->fixingDays(), Days);
128 Date fixingEndDate = cpn->fixingEndDate();
129 Time T1 = std::max(t, p_->termStructure()->timeFromReference(fixingValueDate));
130 Time T2 = std::max(
131 T1, p_->termStructure()->timeFromReference(fixingEndDate)); // accounts for QL_INDEXED_COUPON
132 Time T3 = std::max(T2, p_->termStructure()->timeFromReference(cpn->date()));
133 RandomVariable disc1 = reducedDiscountBond(t, T1, x, swap->forwardingTermStructure());
134 RandomVariable disc2 = reducedDiscountBond(t, T2, x, swap->forwardingTermStructure());
135 Real adjFactor =
136 cpn->dayCounter().yearFraction(cpn->accrualStartDate(), cpn->accrualEndDate(),
137 cpn->referencePeriodStart(), cpn->referencePeriodEnd()) /
138 swap->iborIndex()->dayCounter().yearFraction(fixingValueDate, fixingEndDate);
139 RandomVariable tmp = disc1 / disc2 - RandomVariable(x.size(), 1.0);
140 if (!QuantLib::close_enough(adjFactor, 1.0)) {
141 tmp *= RandomVariable(x.size(), adjFactor);
142 }
143 numerator += tmp * reducedDiscountBond(t, T3, x, swapDiscountCurve);
144 } else if (auto cpn = QuantLib::ext::dynamic_pointer_cast<OvernightIndexedCoupon>(c)) {
145 Date start = cpn->valueDates().front();
146 Date end = cpn->valueDates().back();
147 Time T1 = std::max(t, p_->termStructure()->timeFromReference(start));
148 Time T2 = std::max(T1, p_->termStructure()->timeFromReference(end));
149 Time T3 = std::max(T2, p_->termStructure()->timeFromReference(cpn->date()));
150 RandomVariable disc1 = reducedDiscountBond(t, T1, x, swap->forwardingTermStructure());
151 RandomVariable disc2 = reducedDiscountBond(t, T2, x, swap->forwardingTermStructure());
152 Real adjFactor =
153 cpn->dayCounter().yearFraction(cpn->accrualStartDate(), cpn->accrualEndDate(),
154 cpn->referencePeriodStart(), cpn->referencePeriodEnd()) /
155 swap->iborIndex()->dayCounter().yearFraction(start, end);
156 RandomVariable tmp;
157 if (cpn->averagingMethod() == RateAveraging::Compound) {
158 tmp = disc1 / disc2 - RandomVariable(x.size(), 1.0);
159 } else if (cpn->averagingMethod() == RateAveraging::Simple) {
160 tmp = QuantExt::log(disc1 / disc2);
161 } else {
162 QL_FAIL("LgmVectorised::fixing(): RateAveraging '"
163 << static_cast<int>(cpn->averagingMethod())
164 << "' not handled - internal error, contact dev.");
165 }
166 if (!QuantLib::close_enough(adjFactor, 1.0)) {
167 tmp *= RandomVariable(x.size(), adjFactor);
168 }
169 numerator += tmp * reducedDiscountBond(t, T3, x, swapDiscountCurve);
170 } else {
171 QL_FAIL("LgmVectorised::fixing(): expected ibor coupon");
172 }
173 }
174 for (auto const& c : fixedLeg) {
175 auto cpn = QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(c);
176 QL_REQUIRE(cpn, "LgmVectorised::fixing(): expected fixed coupon");
177 Date d = cpn->date();
178 Time T = std::max(t, p_->termStructure()->timeFromReference(d));
179 denominator +=
180 reducedDiscountBond(t, T, x, swapDiscountCurve) * RandomVariable(x.size(), cpn->accrualPeriod());
181 }
182 return numerator / denominator;
183
184 } else {
185 QL_FAIL("LgmVectorised::fixing(): index ('" << index->name() << "') must be ibor or swap index");
186 }
187}
188
189RandomVariable LgmVectorised::compoundedOnRate(const QuantLib::ext::shared_ptr<OvernightIndex>& index,
190 const std::vector<Date>& fixingDates,
191 const std::vector<Date>& valueDates, const std::vector<Real>& dt,
192 const Natural rateCutoff, const bool includeSpread, const Real spread,
193 const Real gearing, const Period lookback, Real cap, Real floor,
194 const bool localCapFloor, const bool nakedOption, const Time t,
195 const RandomVariable& x) const {
196
197 QL_REQUIRE(!includeSpread || QuantLib::close_enough(gearing, 1.0),
198 "LgmVectorised::compoundedOnRate(): if include spread = true, only a gearing 1.0 is allowed - scale "
199 "the notional in this case instead.");
200
201 QL_REQUIRE(rateCutoff < dt.size(), "LgmVectorised::compoundedOnRate(): rate cutoff ("
202 << rateCutoff << ") must be less than number of fixings in period ("
203 << dt.size() << ")");
204
205 /* We allow the observation time t to be later than the value dates for which to project ON fixings.
206 In this case we project the period from the first (future) value date to the last value date starting
207 from t, but use the actual portion of the underlying curve.
208 As a refinement, we might consider to scale x down to the volatility corresponding to the first future
209 value date as well (TODO) - this is all experimental and an approximation to meet the requirements of
210 an 1D backward solver, i.e. to be able to price e.g. Bermudan OIS swaptions in an efficient way. */
211
212 // the following is similar to the code in the overnight index coupon pricer
213
214 Size i = 0, n = dt.size();
215 Size nCutoff = n - rateCutoff;
216 Real compoundFactor = 1.0, compoundFactorWithoutSpread = 1.0;
217
218 Date today = Settings::instance().evaluationDate();
219
220 while (i < n && fixingDates[std::min(i, nCutoff)] < today) {
221 Rate pastFixing = IndexManager::instance().getHistory(index->name())[fixingDates[std::min(i, nCutoff)]];
222 QL_REQUIRE(pastFixing != Null<Real>(), "LgmVectorised::compoundedOnRate(): Missing "
223 << index->name() << " fixing for "
224 << fixingDates[std::min(i, nCutoff)]);
225 if (includeSpread) {
226 compoundFactorWithoutSpread *= (1.0 + pastFixing * dt[i]);
227 pastFixing += spread;
228 }
229 compoundFactor *= (1.0 + pastFixing * dt[i]);
230 ++i;
231 }
232
233 if (i < n && fixingDates[std::min(i, nCutoff)] == today) {
234 Rate pastFixing = IndexManager::instance().getHistory(index->name())[fixingDates[std::min(i, nCutoff)]];
235 if (pastFixing != Null<Real>()) {
236 if (includeSpread) {
237 compoundFactorWithoutSpread *= (1.0 + pastFixing * dt[i]);
238 pastFixing += spread;
239 }
240 compoundFactor *= (1.0 + pastFixing * dt[i]);
241 ++i;
242 }
243 }
244
245 RandomVariable compoundFactorLgm(x.size(), compoundFactor),
246 compoundFactorWithoutSpreadLgm(x.size(), compoundFactorWithoutSpread);
247
248 if (i < n) {
249 Handle<YieldTermStructure> curve = index->forwardingTermStructure();
250 QL_REQUIRE(!curve.empty(),
251 "LgmVectorised::compoundedOnRate(): null term structure set to this instance of " << index->name());
252
253 DiscountFactor startDiscount = curve->discount(valueDates[i]);
254 DiscountFactor endDiscount = curve->discount(valueDates[std::max(nCutoff, i)]);
255
256 if (nCutoff < n) {
257 DiscountFactor discountCutoffDate =
258 curve->discount(valueDates[nCutoff] + 1) / curve->discount(valueDates[nCutoff]);
259 endDiscount *= std::pow(discountCutoffDate, valueDates[n] - valueDates[nCutoff]);
260 }
261
262 // the times associated to the projection on the T0 curve
263
264 Real T1 = p_->termStructure()->timeFromReference(valueDates[i]);
265 Real T2 = p_->termStructure()->timeFromReference(valueDates[n]);
266
267 // the times we use for the projection in the LGM model, if t > T1 they are displaced by (t-T1)
268
269 Real T1_lgm = T1, T2_lgm = T2;
270 if (t > T1) {
271 T1_lgm += t - T1;
272 T2_lgm += t - T1;
273 }
274
275 // the discount factors estimated in the lgm model
276
277 RandomVariable disc1 = reducedDiscountBond(t, T1_lgm, x, curve);
278 RandomVariable disc2 = reducedDiscountBond(t, T2_lgm, x, curve);
279
280 // apply a correction to the discount factors
281
282 disc1 *= RandomVariable(x.size(), startDiscount / curve->discount(T1_lgm));
283 disc2 *= RandomVariable(x.size(), endDiscount / curve->discount(T2_lgm));
284
285 // continue with the usual computation
286
287 compoundFactorLgm *= disc1 / disc2;
288
289 if (includeSpread) {
290 compoundFactorWithoutSpreadLgm *= disc1 / disc2;
291 Real tau =
292 index->dayCounter().yearFraction(valueDates[i], valueDates.back()) / (valueDates.back() - valueDates[i]);
293 compoundFactorLgm *= RandomVariable(
294 x.size(), std::pow(1.0 + tau * spread, static_cast<int>(valueDates.back() - valueDates[i])));
295 }
296 }
297
298 Rate tau = index->dayCounter().yearFraction(valueDates.front(), valueDates.back());
299 RandomVariable rate = (compoundFactorLgm - RandomVariable(x.size(), 1.0)) / RandomVariable(x.size(), tau);
300 RandomVariable swapletRate = RandomVariable(x.size(), gearing) * rate;
301 RandomVariable effectiveSpread, effectiveIndexFixing;
302 if (!includeSpread) {
303 swapletRate += RandomVariable(x.size(), spread);
304 effectiveSpread = RandomVariable(x.size(), spread);
305 effectiveIndexFixing = rate;
306 } else {
307 effectiveSpread =
308 rate - (compoundFactorWithoutSpreadLgm - RandomVariable(x.size(), 1.0)) / RandomVariable(x.size(), tau);
309 effectiveIndexFixing = rate - effectiveSpread;
310 }
311
312 if (cap == Null<Real>() && floor == Null<Real>())
313 return swapletRate;
314
315 // handle cap / floor - we compute the intrinsic value only
316
317 if (gearing < 0.0) {
318 std::swap(cap, floor);
319 }
320
321 if (nakedOption)
322 swapletRate = RandomVariable(x.size(), 0.0);
323
324 RandomVariable floorletRate(x.size(), 0.0);
325 RandomVariable capletRate(x.size(), 0.0);
326
327 if (floor != Null<Real>()) {
328 // ignore localCapFloor, treat as global
329 RandomVariable effectiveStrike =
330 (RandomVariable(x.size(), floor) - effectiveSpread) / RandomVariable(x.size(), gearing);
331 floorletRate = RandomVariable(x.size(), gearing) *
332 max(RandomVariable(x.size(), 0.0), effectiveStrike - effectiveIndexFixing);
333 }
334
335 if (cap != Null<Real>()) {
336 RandomVariable effectiveStrike =
337 (RandomVariable(x.size(), cap) - effectiveSpread) / RandomVariable(x.size(), gearing);
338 capletRate = RandomVariable(x.size(), gearing) *
339 max(RandomVariable(x.size(), 0.0), effectiveIndexFixing - effectiveStrike);
340 if (nakedOption && floor == Null<Real>())
341 capletRate = -capletRate;
342 }
343
344 return swapletRate + floorletRate - capletRate;
345}
346
347RandomVariable LgmVectorised::averagedOnRate(const QuantLib::ext::shared_ptr<OvernightIndex>& index,
348 const std::vector<Date>& fixingDates, const std::vector<Date>& valueDates,
349 const std::vector<Real>& dt, const Natural rateCutoff,
350 const bool includeSpread, const Real spread, const Real gearing,
351 const Period lookback, Real cap, Real floor, const bool localCapFloor,
352 const bool nakedOption, const Time t, const RandomVariable& x) const {
353
354 QL_REQUIRE(!includeSpread || QuantLib::close_enough(gearing, 1.0),
355 "LgmVectorised::averageOnRate(): if include spread = true, only a gearing 1.0 is allowed - scale "
356 "the notional in this case instead.");
357
358 QL_REQUIRE(rateCutoff < dt.size(), "LgmVectorised::averageOnRate(): rate cutoff ("
359 << rateCutoff << ") must be less than number of fixings in period ("
360 << dt.size() << ")");
361
362 /* Same comment on t as in compoundedOnRate() above applies here */
363
364 // the following is similar to the code in the overnight index coupon pricer
365
366 Size i = 0, n = dt.size();
367 Size nCutoff = n - rateCutoff;
368 Real accumulatedRate = 0.0;
369
370 Date today = Settings::instance().evaluationDate();
371
372 while (i < n && fixingDates[std::min(i, nCutoff)] < today) {
373 Rate pastFixing = IndexManager::instance().getHistory(index->name())[fixingDates[std::min(i, nCutoff)]];
374 QL_REQUIRE(pastFixing != Null<Real>(), "LgmVectorised::averageOnRate(): Missing "
375 << index->name() << " fixing for "
376 << fixingDates[std::min(i, nCutoff)]);
377 accumulatedRate += pastFixing * dt[i];
378 ++i;
379 }
380
381 if (i < n && fixingDates[std::min(i, nCutoff)] == today) {
382 Rate pastFixing = IndexManager::instance().getHistory(index->name())[fixingDates[std::min(i, nCutoff)]];
383 if (pastFixing != Null<Real>()) {
384 accumulatedRate += pastFixing * dt[i];
385 ++i;
386 }
387 }
388
389 RandomVariable accumulatedRateLgm(x.size(), accumulatedRate);
390
391 if (i < n) {
392 Handle<YieldTermStructure> curve = index->forwardingTermStructure();
393 QL_REQUIRE(!curve.empty(),
394 "LgmVectorised::averageOnRate(): null term structure set to this instance of " << index->name());
395
396 DiscountFactor startDiscount = curve->discount(valueDates[i]);
397 DiscountFactor endDiscount = curve->discount(valueDates[std::max(nCutoff, i)]);
398
399 if (nCutoff < n) {
400 DiscountFactor discountCutoffDate =
401 curve->discount(valueDates[nCutoff] + 1) / curve->discount(valueDates[nCutoff]);
402 endDiscount *= std::pow(discountCutoffDate, valueDates[n] - valueDates[nCutoff]);
403 }
404
405 // the times associated to the projection on the T0 curve
406
407 Real T1 = p_->termStructure()->timeFromReference(valueDates[i]);
408 Real T2 = p_->termStructure()->timeFromReference(valueDates[n]);
409
410 // the times we use for the projection in the LGM model, if t > T1 they are displaced by (t-T1)
411
412 Real T1_lgm = T1, T2_lgm = T2;
413 if (t > T1) {
414 T1_lgm += t - T1;
415 T2_lgm += t - T1;
416 }
417
418 // the discount factors estimated in the lgm model
419
420 RandomVariable disc1 = reducedDiscountBond(t, T1_lgm, x, curve);
421 RandomVariable disc2 = reducedDiscountBond(t, T2_lgm, x, curve);
422
423 // apply a correction to the discount factors
424
425 disc1 *= RandomVariable(x.size(), startDiscount / curve->discount(T1_lgm));
426 disc2 *= RandomVariable(x.size(), endDiscount / curve->discount(T2_lgm));
427
428 // continue with the usual computation
429
430 accumulatedRateLgm += log(disc1 / disc2);
431 }
432
433 Rate tau = index->dayCounter().yearFraction(valueDates.front(), valueDates.back());
434 RandomVariable rate =
435 RandomVariable(x.size(), gearing / tau) * accumulatedRateLgm + RandomVariable(x.size(), spread);
436
437 if (cap == Null<Real>() && floor == Null<Real>())
438 return rate;
439
440 // handle cap / floor - we compute the intrinsic value only
441
442 if (gearing < 0.0) {
443 std::swap(cap, floor);
444 }
445
446 RandomVariable forwardRate = (rate - RandomVariable(x.size(), spread)) / RandomVariable(x.size(), gearing);
447 RandomVariable floorletRate(x.size(), 0.0);
448 RandomVariable capletRate(x.size(), 0.0);
449
450 if (nakedOption)
451 rate = RandomVariable(x.size(), 0.0);
452
453 if (floor != Null<Real>()) {
454 // ignore localCapFloor, treat as global
455 RandomVariable effectiveStrike = RandomVariable(x.size(), (floor - spread) / gearing);
456 floorletRate =
457 RandomVariable(x.size(), gearing) * max(RandomVariable(x.size(), 0.0), effectiveStrike - forwardRate);
458 }
459
460 if (cap != Null<Real>()) {
461 RandomVariable effectiveStrike = RandomVariable(x.size(), (cap - spread) / gearing);
462 capletRate =
463 RandomVariable(x.size(), gearing) * max(RandomVariable(x.size(), 0.0), forwardRate - effectiveStrike);
464 if (nakedOption && floor == Null<Real>())
465 capletRate = -capletRate;
466 }
467
468 return rate + floorletRate - capletRate;
469}
470
471RandomVariable LgmVectorised::averagedBmaRate(const QuantLib::ext::shared_ptr<BMAIndex>& index,
472 const std::vector<Date>& fixingDates, const Date& accrualStartDate,
473 const Date& accrualEndDate, const bool includeSpread, const Real spread,
474 const Real gearing, Real cap, Real floor, const bool nakedOption,
475 const Time t, const RandomVariable& x) const {
476
477 // similar to AverageBMACouponPricer
478
479 Natural cutoffDays = 0;
480 Date startDate = accrualStartDate - cutoffDays;
481 Date endDate = accrualEndDate - cutoffDays;
482 Date d1 = startDate, d2 = startDate;
483
484 QL_REQUIRE(!fixingDates.empty(), "LgmVectorised::averagedBmaRate(): fixing date list empty");
485 QL_REQUIRE(index->valueDate(fixingDates.front()) <= startDate,
486 "LgmVectorised::averagedBmaRate(): first fixing date valid after period start");
487 QL_REQUIRE(index->valueDate(fixingDates.back()) >= endDate,
488 "LgmVectorised::averagedBmaRate(): last fixing date valid before period end");
489
490 Handle<YieldTermStructure> curve = index->forwardingTermStructure();
491 QL_REQUIRE(!curve.empty(),
492 "LgmVectorised::averagedBmaRate(): null term structure set to this instance of " << index->name());
493
494 Date today = Settings::instance().evaluationDate();
495
496 RandomVariable avgBMA(x.size(), 0.0);
497
498 for (Size i = 0; i < fixingDates.size() - 1; ++i) {
499 Date valueDate = index->valueDate(fixingDates[i]);
500 Date nextValueDate = index->valueDate(fixingDates[i + 1]);
501 if (fixingDates[i] >= endDate || valueDate >= endDate)
502 break;
503 if (fixingDates[i + 1] < startDate || nextValueDate <= startDate)
504 continue;
505
506 d2 = std::min(nextValueDate, endDate);
508 if (fixingDates[i] <= today) {
509 // past fixing or forecast today's fixing on T0 curve (which is ok, since model independent)
510 fixing = RandomVariable(x.size(), index->fixing(fixingDates[i]));
511 } else {
512
513 Date start = index->fixingCalendar().advance(fixingDates[i], 1, Days);
514 Date end = index->maturityDate(start);
515 DiscountFactor startDiscount = curve->discount(start);
516 DiscountFactor endDiscount = curve->discount(end);
517
518 // the times associated to the projection on the T0 curve
519
520 Real T1 = p_->termStructure()->timeFromReference(start);
521 Real T2 = p_->termStructure()->timeFromReference(end);
522
523 // the times we use for the projection in the LGM model, if t > T1 they are displaced by (t-T1)
524
525 Real T1_lgm = T1, T2_lgm = T2;
526 if (t > T1) {
527 T1_lgm += t - T1;
528 T2_lgm += t - T1;
529 }
530
531 // the discount factors estimated in the lgm model
532
533 RandomVariable disc1 = reducedDiscountBond(t, T1_lgm, x, curve);
534 RandomVariable disc2 = reducedDiscountBond(t, T2_lgm, x, curve);
535
536 // apply a correction to the discount factors
537
538 disc1 *= RandomVariable(x.size(), startDiscount / curve->discount(T1_lgm));
539 disc2 *= RandomVariable(x.size(), endDiscount / curve->discount(T2_lgm));
540
541 // estimate the fixing
542
543 fixing = (disc1 / disc2 - RandomVariable(x.size(), 1.0)) /
544 RandomVariable(x.size(), index->dayCounter().yearFraction(start, end));
545 }
546
547 avgBMA += fixing * RandomVariable(x.size(), (d2 - d1));
548 d1 = d2;
549 }
550
551 avgBMA *= RandomVariable(x.size(), gearing / (endDate - startDate));
552 avgBMA += RandomVariable(x.size(), spread);
553
554 if (cap == Null<Real>() && floor == Null<Real>())
555 return avgBMA;
556
557 // handle cap / floor - we compute the intrinsic value only
558
559 if (gearing < 0.0) {
560 std::swap(cap, floor);
561 }
562
563 RandomVariable forwardRate = (avgBMA - RandomVariable(x.size(), spread)) / RandomVariable(x.size(), gearing);
564 RandomVariable floorletRate(x.size(), 0.0);
565 RandomVariable capletRate(x.size(), 0.0);
566
567 if (nakedOption)
568 avgBMA = RandomVariable(x.size(), 0.0);
569
570 if (floor != Null<Real>()) {
571 // ignore localCapFloor, treat as global
572 RandomVariable effectiveStrike = RandomVariable(x.size(), (floor - spread) / gearing);
573 floorletRate =
574 RandomVariable(x.size(), gearing) * max(RandomVariable(x.size(), 0.0), effectiveStrike - forwardRate);
575 }
576
577 if (cap != Null<Real>()) {
578 RandomVariable effectiveStrike = RandomVariable(x.size(), (cap - spread) / gearing);
579 capletRate =
580 RandomVariable(x.size(), gearing) * max(RandomVariable(x.size(), 0.0), forwardRate - effectiveStrike);
581 if (nakedOption && floor == Null<Real>())
582 capletRate = -capletRate;
583 }
584
585 return avgBMA + floorletRate - capletRate;
586}
587
588RandomVariable LgmVectorised::subPeriodsRate(const QuantLib::ext::shared_ptr<InterestRateIndex>& index,
589 const std::vector<Date>& fixingDates, const Time t,
590 const RandomVariable& x) const {
591
592 return fixing(index, fixingDates.front(), t, x);
593}
594
595} // namespace QuantExt
QuantLib::ext::shared_ptr< IrLgm1fParametrization > p_
RandomVariable discountBond(const Time t, const Time T, const RandomVariable &x, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >()) const
RandomVariable averagedOnRate(const QuantLib::ext::shared_ptr< OvernightIndex > &index, const std::vector< Date > &fixingDates, const std::vector< Date > &valueDates, const std::vector< Real > &dt, const Natural rateCutoff, const bool includeSpread, const Real spread, const Real gearing, const Period lookback, Real cap, Real floor, const bool localCapFloor, const bool nakedOption, const Time t, const RandomVariable &x) const
RandomVariable averagedBmaRate(const QuantLib::ext::shared_ptr< BMAIndex > &index, const std::vector< Date > &fixingDates, const Date &accrualStartDate, const Date &accrualEndDate, const bool includeSpread, const Real spread, const Real gearing, Real cap, Real floor, const bool nakedOption, const Time t, const RandomVariable &x) const
RandomVariable numeraire(const Time t, const RandomVariable &x, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >()) const
RandomVariable discountBondOption(Option::Type type, const Real K, const Time t, const Time S, const Time T, const RandomVariable &x, const Handle< YieldTermStructure > &discountCurve) const
RandomVariable fixing(const QuantLib::ext::shared_ptr< InterestRateIndex > &index, const Date &fixingDate, const Time t, const RandomVariable &x) const
RandomVariable reducedDiscountBond(const Time t, const Time T, const RandomVariable &x, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >()) const
RandomVariable compoundedOnRate(const QuantLib::ext::shared_ptr< OvernightIndex > &index, const std::vector< Date > &fixingDates, const std::vector< Date > &valueDates, const std::vector< Real > &dt, const Natural rateCutoff, const bool includeSpread, const Real spread, const Real gearing, const Period lookback, Real cap, Real floor, const bool localCapFloor, const bool nakedOption, const Time t, const RandomVariable &x) const
RandomVariable subPeriodsRate(const QuantLib::ext::shared_ptr< InterestRateIndex > &index, const std::vector< Date > &fixingDates, const Time t, const RandomVariable &x) const
vectorised lgm model calculations
CompiledFormula exp(CompiledFormula x)
RandomVariable normalCdf(RandomVariable x)
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)
CompiledFormula log(CompiledFormula x)