Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
numericlgmmultilegoptionengine.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2021 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
28
29#include <ql/cashflows/averagebmacoupon.hpp>
30#include <ql/cashflows/capflooredcoupon.hpp>
31#include <ql/cashflows/fixedratecoupon.hpp>
32#include <ql/cashflows/iborcoupon.hpp>
33#include <ql/payoff.hpp>
34
35#include <boost/algorithm/string/join.hpp>
36
37namespace QuantExt {
38
39using namespace QuantLib;
40
42 return optionTime < belongsToUnderlyingMaxTime_ || QuantLib::close_enough(optionTime, belongsToUnderlyingMaxTime_);
43}
44
46 if (maxEstimationTime_ != Null<Real>()) {
47 return time < maxEstimationTime_ || QuantLib::close_enough(time, maxEstimationTime_);
48 } else {
49 return QuantLib::close_enough(time, exactEstimationTime_);
50 }
51}
52
54 if (maxEstimationTime_ != Null<Real>())
55 return false;
56 return time < exactEstimationTime_ || QuantLib::close_enough(time, exactEstimationTime_);
57}
58
60
62 if (couponEndTime_ != Null<Real>() && couponStartTime_ != Null<Real>()) {
63 return std::max(0.0, std::min(1.0, (couponEndTime_ - time) / (couponEndTime_ - couponStartTime_)));
64 }
65 return 1.0;
66}
67
70 const RandomVariable& state,
71 const Handle<YieldTermStructure>& discountCurve) const {
72 return calculator_(lgm, t, state, discountCurve);
73}
74
77
78 CashflowInfo info;
79 auto const& ts = solver_->model()->parametrization()->termStructure();
80 auto const& c = legs_[i][j];
81 Real payrec = payer_[i] ? -1.0 : 1.0;
82
83 Real T = solver_->model()->parametrization()->termStructure()->timeFromReference(c->date());
84
85 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c)) {
86 bool done = false;
87 if (exercise_->type() == Exercise::American) {
88 // american exercise implies that we can exercise into broken periods
89 info.belongsToUnderlyingMaxTime_ = ts->timeFromReference(cpn->accrualEndDate());
90 } else {
91 // bermudan exercise implies that we always exercise into whole periods
92 info.belongsToUnderlyingMaxTime_ = ts->timeFromReference(cpn->accrualStartDate());
93 }
94 info.couponStartTime_ = ts->timeFromReference(cpn->accrualStartDate());
95 info.couponEndTime_ = ts->timeFromReference(cpn->accrualEndDate());
96 if (auto ibor = QuantLib::ext::dynamic_pointer_cast<IborCoupon>(c)) {
97 info.maxEstimationTime_ = ts->timeFromReference(ibor->fixingDate());
98 info.calculator_ = [ibor, T, payrec](const LgmVectorised& lgm, const Real t, const RandomVariable& x,
99 const Handle<YieldTermStructure>& discountCurve) {
100 return (RandomVariable(x.size(), ibor->gearing()) *
101 lgm.fixing(ibor->index(), ibor->fixingDate(), t, x) +
102 RandomVariable(x.size(), ibor->spread())) *
103 RandomVariable(x.size(), ibor->accrualPeriod() * ibor->nominal() * payrec) *
104 lgm.reducedDiscountBond(t, T, x, discountCurve);
105 };
106 done = true;
107 } else if (auto fix = QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(cpn)) {
108 info.maxEstimationTime_ = ts->timeFromReference(fix->date());
109 info.calculator_ = [fix, T, payrec](const LgmVectorised& lgm, const Real t, const RandomVariable& x,
110 const Handle<YieldTermStructure>& discountCurve) {
111 return RandomVariable(x.size(), fix->amount() * payrec) *
112 lgm.reducedDiscountBond(t, T, x, discountCurve);
113 };
114 done = true;
115 } else if (auto on = QuantLib::ext::dynamic_pointer_cast<QuantExt::OvernightIndexedCoupon>(cpn)) {
116 info.maxEstimationTime_ = ts->timeFromReference(on->fixingDates().front());
117 info.calculator_ = [on, T, payrec](const LgmVectorised& lgm, const Real t, const RandomVariable& x,
118 const Handle<YieldTermStructure>& discountCurve) {
119 return lgm.compoundedOnRate(QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(on->index()), on->fixingDates(),
120 on->valueDates(), on->dt(), on->rateCutoff(), on->includeSpread(),
121 on->spread(), on->gearing(), on->lookback(), Null<Real>(), Null<Real>(),
122 false, false, t, x) *
123 RandomVariable(x.size(), on->accrualPeriod() * on->nominal() * payrec) *
124 lgm.reducedDiscountBond(t, T, x, discountCurve);
125 };
126 done = true;
127 } else if (auto av = QuantLib::ext::dynamic_pointer_cast<QuantExt::AverageONIndexedCoupon>(cpn)) {
128 info.maxEstimationTime_ = ts->timeFromReference(av->fixingDates().front());
129 info.calculator_ = [av, T, payrec](const LgmVectorised& lgm, const Real t, const RandomVariable& x,
130 const Handle<YieldTermStructure>& discountCurve) {
131 return lgm.averagedOnRate(QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(av->index()), av->fixingDates(),
132 av->valueDates(), av->dt(), av->rateCutoff(), false, av->spread(),
133 av->gearing(), av->lookback(), Null<Real>(), Null<Real>(), false, false, t,
134 x) *
135 RandomVariable(x.size(), av->accrualPeriod() * av->nominal() * payrec) *
136 lgm.reducedDiscountBond(t, T, x, discountCurve);
137 };
138 done = true;
139 } else if (auto bma = QuantLib::ext::dynamic_pointer_cast<QuantLib::AverageBMACoupon>(cpn)) {
140 info.maxEstimationTime_ = ts->timeFromReference(bma->fixingDates().front());
141 info.calculator_ = [bma, T, payrec](const LgmVectorised& lgm, const Real t, const RandomVariable& x,
142 const Handle<YieldTermStructure>& discountCurve) {
143 return lgm.averagedBmaRate(QuantLib::ext::dynamic_pointer_cast<BMAIndex>(bma->index()), bma->fixingDates(),
144 bma->accrualStartDate(), bma->accrualEndDate(), false, bma->spread(),
145 bma->gearing(), Null<Real>(), Null<Real>(), false, t, x) *
146 RandomVariable(x.size(), bma->accrualPeriod() * bma->nominal() * payrec) *
147 lgm.reducedDiscountBond(t, T, x, discountCurve);
148 };
149 done = true;
150 } else if (auto cf = QuantLib::ext::dynamic_pointer_cast<QuantLib::CappedFlooredCoupon>(cpn)) {
151 auto und = cf->underlying();
152 if (auto undibor = QuantLib::ext::dynamic_pointer_cast<QuantLib::IborCoupon>(und)) {
153 info.exactEstimationTime_ = ts->timeFromReference(und->fixingDate());
154 info.calculator_ = [cf, undibor, T, payrec](const LgmVectorised& lgm, const Real t,
155 const RandomVariable& x,
156 const Handle<YieldTermStructure>& discountCurve) {
157 RandomVariable cap(x.size(), cf->cap() == Null<Real>() ? QL_MAX_REAL : cf->cap());
158 RandomVariable floor(x.size(), cf->floor() == Null<Real>() ? -QL_MAX_REAL : cf->floor());
159
160 return max(floor, min(cap, (RandomVariable(x.size(), undibor->gearing()) *
161 lgm.fixing(undibor->index(), undibor->fixingDate(), t, x) +
162 RandomVariable(x.size(), undibor->spread())))) *
163 RandomVariable(x.size(), undibor->accrualPeriod() * undibor->nominal() * payrec) *
164 lgm.reducedDiscountBond(t, T, x, discountCurve);
165 };
166 done = true;
167 }
168 } else if (auto cfon = QuantLib::ext::dynamic_pointer_cast<QuantExt::CappedFlooredOvernightIndexedCoupon>(cpn)) {
169 auto und = cfon->underlying();
170 info.exactEstimationTime_ = ts->timeFromReference(und->fixingDates().front());
171 info.calculator_ = [cfon, und, T, payrec](const LgmVectorised& lgm, const Real t, const RandomVariable& x,
172 const Handle<YieldTermStructure>& discountCurve) {
173 return lgm.compoundedOnRate(QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(und->index()),
174 und->fixingDates(), und->valueDates(), und->dt(), und->rateCutoff(),
175 und->includeSpread(), und->spread(), und->gearing(), und->lookback(),
176 cfon->cap(), cfon->floor(), cfon->localCapFloor(), cfon->nakedOption(), t,
177 x) *
178 RandomVariable(x.size(), cfon->accrualPeriod() * cfon->nominal() * payrec) *
179 lgm.reducedDiscountBond(t, T, x, discountCurve);
180 };
181 done = true;
182 } else if (auto cfav = QuantLib::ext::dynamic_pointer_cast<QuantExt::CappedFlooredAverageONIndexedCoupon>(cpn)) {
183 auto und = cfav->underlying();
184 info.exactEstimationTime_ = ts->timeFromReference(und->fixingDates().front());
185 info.calculator_ = [cfav, und, T, payrec](const LgmVectorised& lgm, const Real t, const RandomVariable& x,
186 const Handle<YieldTermStructure>& discountCurve) {
187 return lgm.averagedOnRate(QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(und->index()), und->fixingDates(),
188 und->valueDates(), und->dt(), und->rateCutoff(), cfav->includeSpread(),
189 und->spread(), und->gearing(), und->lookback(), cfav->cap(), cfav->floor(),
190 cfav->localCapFloor(), cfav->nakedOption(), t, x) *
191 RandomVariable(x.size(), cfav->accrualPeriod() * cfav->nominal() * payrec) *
192 lgm.reducedDiscountBond(t, T, x, discountCurve);
193 };
194 done = true;
195 } else if (auto cfbma = QuantLib::ext::dynamic_pointer_cast<QuantExt::CappedFlooredAverageBMACoupon>(cpn)) {
196 auto und = cfbma->underlying();
197 info.exactEstimationTime_ = ts->timeFromReference(und->fixingDates().front());
198 info.calculator_ = [cfbma, und, T, payrec](const LgmVectorised& lgm, const Real t, const RandomVariable& x,
199 const Handle<YieldTermStructure>& discountCurve) {
200 return lgm.averagedBmaRate(QuantLib::ext::dynamic_pointer_cast<BMAIndex>(und->index()), und->fixingDates(),
201 und->accrualStartDate(), und->accrualEndDate(), cfbma->includeSpread(),
202 und->spread(), und->gearing(), cfbma->cap(), cfbma->floor(),
203 cfbma->nakedOption(), t, x) *
204 RandomVariable(x.size(), cfbma->accrualPeriod() * cfbma->nominal() * payrec) *
205 lgm.reducedDiscountBond(t, T, x, discountCurve);
206 };
207 done = true;
208 } else if (auto sub = QuantLib::ext::dynamic_pointer_cast<QuantExt::SubPeriodsCoupon1>(cpn)) {
209 info.maxEstimationTime_ = ts->timeFromReference(sub->fixingDates().front());
210 info.calculator_ = [sub, T, payrec](const LgmVectorised& lgm, const Real t, const RandomVariable& x,
211 const Handle<YieldTermStructure>& discountCurve) {
212 return lgm.subPeriodsRate(sub->index(), sub->fixingDates(), t, x) *
213 RandomVariable(x.size(), sub->accrualPeriod() * sub->nominal() * payrec) *
214 lgm.reducedDiscountBond(t, T, x, discountCurve);
215 };
216 done = true;
217 }
218 QL_REQUIRE(done, "NumericLgmMultiLegOptionEngineBase: coupon type not handled, supported coupon types: Fix, "
219 "(capfloored) Ibor, (capfloored) ON comp, (capfloored) ON avg, BMA/SIFMA, subperiod. leg = "
220 << i << " cf = " << j);
221 } else {
222 // can not cast to coupon
223 info.belongsToUnderlyingMaxTime_ = ts->timeFromReference(c->date());
224 info.maxEstimationTime_ = ts->timeFromReference(c->date());
225 info.calculator_ = [c, T, payrec](const LgmVectorised& lgm, const Real t, const RandomVariable& x,
226 const Handle<YieldTermStructure>& discountCurve) {
227 return RandomVariable(x.size(), c->amount() * payrec) * lgm.reducedDiscountBond(t, T, x, discountCurve);
228 };
229 }
230
231 // some postprocessing and checks
232
233 info.maxEstimationTime_ = std::max(0.0, info.maxEstimationTime_);
234 info.exactEstimationTime_ = std::max(0.0, info.exactEstimationTime_);
235
236 QL_REQUIRE(
237 info.belongsToUnderlyingMaxTime_ != Null<Real>(),
238 "NumericLgmMultiLegOptionEngineBase: internal error: cashflow info: belongsToUnderlyingMaxTime_ is null. leg = "
239 << i << " cf = " << j);
240 QL_REQUIRE(info.maxEstimationTime_ != Null<Real>() || info.exactEstimationTime_ != Null<Real>(),
241 "NumericLgmMultiLegOptionEngineBase: internal error: both maxEstimationTime_ and exactEstimationTime_ "
242 "is null. leg = "
243 << i << " cf = " << j);
244 return info;
245}
246
247/* Get the rebate amount on an exercise */
248RandomVariable getRebatePv(const LgmVectorised& lgm, const Real t, const RandomVariable& x,
249 const Handle<YieldTermStructure>& discountCurve,
250 const QuantLib::ext::shared_ptr<RebatedExercise>& exercise, const Date& d) {
251 if (exercise == nullptr)
252 return RandomVariable(x.size(), 0.0);
253 if (exercise->type() == Exercise::American) {
254 return RandomVariable(x.size(), exercise->rebate(0)) *
256 t, lgm.parametrization()->termStructure()->timeFromReference(exercise->rebatePaymentDate(d)), x);
257 } else {
258 auto f = std::find(exercise->dates().begin(), exercise->dates().end(), d);
259 QL_REQUIRE(f != exercise->dates().end(), "NumericLgmMultiLegOptionEngine: internal error: exercise date "
260 << d << " from rebate payment not found amount exercise dates.");
261 Size index = std::distance(exercise->dates().begin(), f);
262 return RandomVariable(x.size(), exercise->rebate(index)) *
264 t, lgm.parametrization()->termStructure()->timeFromReference(exercise->rebatePaymentDate(index)), x);
265 }
266}
267
269 const QuantLib::ext::shared_ptr<LgmBackwardSolver>& solver, const Handle<YieldTermStructure>& discountCurve,
270 const Size americanExerciseTimeStepsPerYear)
271 : solver_(solver), discountCurve_(discountCurve),
272 americanExerciseTimeStepsPerYear_(americanExerciseTimeStepsPerYear) {}
273
275 std::vector<std::string>& messages) {
276 return instrumentIsHandled(m.legs(), m.payer(), m.currency(), m.exercise(), m.settlementType(),
277 m.settlementMethod(), messages);
278}
279
281 const std::vector<Leg>& legs, const std::vector<bool>& payer, const std::vector<Currency>& currency,
282 const QuantLib::ext::shared_ptr<Exercise>& exercise, const Settlement::Type& settlementType,
283 const Settlement::Method& settlementMethod, std::vector<std::string>& messages) {
284
285 bool isHandled = true;
286
287 // is there a unique pay currency and all interest rate indices are in this same currency?
288
289 for (Size i = 1; i < currency.size(); ++i) {
290 if (currency[0] != currency[i]) {
291 messages.push_back("NumericLgmMultilegOptionEngine: can only handle single currency underlyings, got " +
292 currency[0].code() + " on leg #1 and " + currency[i].code() + " on leg #" +
293 std::to_string(i + 1));
294 isHandled = false;
295 }
296 }
297
298 for (Size i = 0; i < legs.size(); ++i) {
299 for (Size j = 0; j < legs[i].size(); ++j) {
300 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(legs[i][j])) {
301 if (cpn->index()->currency() != currency[0]) {
302 messages.push_back("NumericLgmMultilegOptionEngine: can only handle indices (" +
303 cpn->index()->name() + ") with same currency as unqiue pay currency (" +
304 currency[0].code());
305 }
306 }
307 }
308 }
309
310 // check coupon types
311
312 for (Size i = 0; i < legs.size(); ++i) {
313 for (Size j = 0; j < legs[i].size(); ++j) {
314 if (auto c = QuantLib::ext::dynamic_pointer_cast<Coupon>(legs[i][j])) {
315 if (!(QuantLib::ext::dynamic_pointer_cast<IborCoupon>(c) || QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(c) ||
316 QuantLib::ext::dynamic_pointer_cast<QuantExt::OvernightIndexedCoupon>(c) ||
317 QuantLib::ext::dynamic_pointer_cast<QuantExt::AverageONIndexedCoupon>(c) ||
318 QuantLib::ext::dynamic_pointer_cast<QuantLib::AverageBMACoupon>(c) ||
319 QuantLib::ext::dynamic_pointer_cast<QuantExt::CappedFlooredOvernightIndexedCoupon>(c) ||
320 QuantLib::ext::dynamic_pointer_cast<QuantExt::CappedFlooredAverageONIndexedCoupon>(c) ||
321 QuantLib::ext::dynamic_pointer_cast<QuantExt::CappedFlooredAverageBMACoupon>(c) ||
322 QuantLib::ext::dynamic_pointer_cast<QuantExt::SubPeriodsCoupon1>(c) ||
323 (QuantLib::ext::dynamic_pointer_cast<QuantLib::CappedFlooredCoupon>(c) &&
324 QuantLib::ext::dynamic_pointer_cast<QuantLib::IborCoupon>(
325 QuantLib::ext::dynamic_pointer_cast<QuantLib::CappedFlooredCoupon>(c)->underlying())))) {
326 messages.push_back(
327 "NumericLgmMultilegOptionEngine: coupon type not handled, supported coupon types: Fix, "
328 "(capfloored) Ibor, (capfloored) ON comp, (capfloored) ON avg, BMA/SIFMA, subperiod. leg = " +
329 std::to_string(i) + " cf = " + std::to_string(j));
330 isHandled = false;
331 }
332 }
333 }
334 }
335
336 return isHandled;
337}
338
340
341 std::vector<std::string> messages;
342 QL_REQUIRE(
344 "NumericLgmMultiLegOptionEngineBase::calculate(): instrument is not handled: " << boost::join(messages, ", "));
345
346 // handle empty exercise
347
348 if (exercise_ == nullptr) {
349 npv_ = 0.0;
350 for (Size i = 0; i < legs_.size(); ++i) {
351 for (Size j = 0; j < legs_[i].size(); ++j) {
352 npv_ += legs_[i][j]->amount() * discountCurve_->discount(legs_[i][j]->date());
353 }
354 }
356 return;
357 }
358
359 // we have a non-empty exercise
360
361 auto rebatedExercise = QuantLib::ext::dynamic_pointer_cast<QuantExt::RebatedExercise>(exercise_);
362 auto const& ts = solver_->model()->parametrization()->termStructure();
363 Date refDate = ts->referenceDate();
364
365 /* Build the cashflow info */
366
367 enum class CashflowStatus { Open, Cached, Done };
368
369 std::vector<CashflowInfo> cashflows;
370 std::vector<CashflowStatus> cashflowStatus;
371
372 for (Size i = 0; i < legs_.size(); ++i) {
373 for (Size j = 0; j < legs_[i].size(); ++j) {
374 cashflows.push_back(buildCashflowInfo(i, j));
375 cashflowStatus.push_back(CashflowStatus::Open);
376 }
377 }
378
379 /* Build the time grid containing the option times */
380
381 std::set<Real> optionTimes;
382 std::map<Real, Date> optionDates;
383
384 if (exercise_->type() == Exercise::Bermudan || exercise_->type() == Exercise::European) {
385 for (auto const& d : exercise_->dates()) {
386 if (d > refDate) {
387 optionTimes.insert(ts->timeFromReference(d));
388 optionDates[ts->timeFromReference(d)] = d;
389 }
390 }
391 } else if (exercise_->type() == Exercise::American) {
392 QL_REQUIRE(exercise_->dates().size() == 2, "NumericLgmMultiLegOptionEngineBase::calculate(): internal error: "
393 "expected 2 dates for AmericanExercise, got "
394 << exercise_->dates().size());
395 Real t1 = std::max(0.0, ts->timeFromReference(exercise_->dates().front()));
396 Real t2 = std::max(t1, ts->timeFromReference(exercise_->dates().back()));
397 Size steps =
398 std::max<Size>(1, static_cast<Size>((t2 - t1) * static_cast<Real>(americanExerciseTimeStepsPerYear_)));
399 optionTimes.insert(t1);
400 for (Size i = 0; i <= steps; ++i) {
401 optionTimes.insert(t1 + static_cast<Real>(i) * (t2 - t1) / static_cast<Real>(steps));
402 }
403 } else {
404 QL_FAIL("NumericLgmMultiLegOptionEngineBase::calculate(): internal error: exercise type "
405 << static_cast<int>(exercise_->type()) << " not handled.");
406 }
407
408 /* Add specific times required to simulate cashflows */
409
410 std::set<Real> requiredCfSimTimes;
411
412 for (auto const& c : cashflows) {
413 if (Real t = c.requiredSimulationTime(); t != Null<Real>())
414 requiredCfSimTimes.insert(t);
415 }
416
417 /* Join the two grids to get the time grid which we use for the backward run */
418
419 std::set<Real> timeGrid{0.0};
420 timeGrid.insert(optionTimes.begin(), optionTimes.end());
421 timeGrid.insert(requiredCfSimTimes.begin(), requiredCfSimTimes.end());
422
423 /* Step backwards through the grid and compute the option npv */
424
425 LgmVectorised lgm(solver_->model()->parametrization());
426
427 RandomVariable underlyingNpv(solver_->gridSize(), 0.0);
428 RandomVariable optionNpv(solver_->gridSize(), 0.0);
429 RandomVariable provisionalNpv(solver_->gridSize(), 0.0);
430
431 std::vector<RandomVariable> cache(cashflows.size());
432
433 for (auto it = timeGrid.rbegin(); it != timeGrid.rend(); ++it) {
434
435 Real t_from = *it;
436 Real t_to = (it != std::next(timeGrid.rend(), -1)) ? *std::next(it, 1) : t_from;
437
438 RandomVariable state = solver_->stateGrid(t_from);
439
440 // update cashflows on current time
441
442 provisionalNpv = RandomVariable(solver_->gridSize(), 0.0);
443
444 for (Size i = 0; i < cashflows.size(); ++i) {
445 if (cashflowStatus[i] == CashflowStatus::Done)
446 continue;
447 if (cashflows[i].isPartOfUnderlying(t_from)) {
448 RandomVariable cpnRatio(solver_->gridSize(), cashflows[i].couponRatio(t_from));
449 bool isBrokenCoupon = !QuantLib::close_enough(cpnRatio.at(0), 1.0);
450 if (cashflowStatus[i] == CashflowStatus::Cached) {
451 if (isBrokenCoupon) {
452 provisionalNpv += cache[i] * cpnRatio;
453 } else {
454 underlyingNpv += cache[i];
455 cache[i].clear();
456 cashflowStatus[i] = CashflowStatus::Done;
457 }
458 } else if (cashflows[i].canBeEstimated(t_from)) {
459 if (isBrokenCoupon) {
460 cache[i] = cashflows[i].pv(lgm, t_from, state, discountCurve_);
461 cashflowStatus[i] = CashflowStatus::Cached;
462 provisionalNpv += cache[i] * cpnRatio;
463 } else {
464 underlyingNpv += cashflows[i].pv(lgm, t_from, state, discountCurve_);
465 cashflowStatus[i] = CashflowStatus::Done;
466 }
467 } else {
468 provisionalNpv += cashflows[i].pv(lgm, t_from, state, discountCurve_) * cpnRatio;
469 }
470 } else if (cashflows[i].mustBeEstimated(t_from) && cashflowStatus[i] == CashflowStatus::Open) {
471 cache[i] = cashflows[i].pv(lgm, t_from, state, discountCurve_);
472 cashflowStatus[i] = CashflowStatus::Cached;
473 }
474 }
475
476 // process optionality
477
478 if (optionTimes.find(t_from) != optionTimes.end()) {
479 auto rebateNpv =
480 getRebatePv(lgm, t_from, state, discountCurve_, rebatedExercise,
481 exercise_->type() == Exercise::American ? Null<Date>() : optionDates.at(t_from));
482 optionNpv = max(optionNpv, underlyingNpv + provisionalNpv + rebateNpv);
483 }
484
485 // roll back
486
487 if (t_from != t_to) {
488 underlyingNpv = solver_->rollback(underlyingNpv, t_from, t_to);
489 optionNpv = solver_->rollback(optionNpv, t_from, t_to);
490 for (auto& c : cache) {
491 if (!c.initialised())
492 continue;
493 c = solver_->rollback(c, t_from, t_to);
494 }
495 // need to roll back provisionalNpv only for the last step t_1 -> t_0 = 0
496 if (it == std::next(timeGrid.rend(), -1))
497 provisionalNpv = solver_->rollback(provisionalNpv, t_from, t_to);
498 }
499 }
500
501 /* Set the results */
502
503 npv_ = optionNpv.at(0);
504 underlyingNpv_ = underlyingNpv.at(0);
505 for (auto const& c : cache) {
506 if (c.initialised())
507 underlyingNpv_ += c.at(0);
508 }
509 underlyingNpv_ += provisionalNpv.at(0);
510
511 additionalResults_ = getAdditionalResultsMap(solver_->model()->getCalibrationInfo());
512
513 if (rebatedExercise) {
514 for (Size i = 0; i < rebatedExercise->dates().size(); ++i) {
515 std::ostringstream d;
516 d << QuantLib::io::iso_date(rebatedExercise->dates()[i]);
517 additionalResults_["exerciseFee_" + d.str()] = -rebatedExercise->rebate(i);
518 }
519 }
520
521} // NumericLgmMultiLegOptionEngineBase::calculate()
522
523NumericLgmMultiLegOptionEngine::NumericLgmMultiLegOptionEngine(const QuantLib::ext::shared_ptr<LinearGaussMarkovModel>& model,
524 const Real sy, const Size ny, const Real sx,
525 const Size nx,
526 const Handle<YieldTermStructure>& discountCurve,
527 const Size americanExerciseTimeStepsPerYear)
528 : NumericLgmMultiLegOptionEngineBase(QuantLib::ext::make_shared<LgmConvolutionSolver2>(model, sy, ny, sx, nx),
529 discountCurve, americanExerciseTimeStepsPerYear) {
530 registerWith(solver_->model());
531 registerWith(discountCurve_);
532}
533
534NumericLgmMultiLegOptionEngine::NumericLgmMultiLegOptionEngine(const QuantLib::ext::shared_ptr<LinearGaussMarkovModel>& model,
535 const Real maxTime, const QuantLib::FdmSchemeDesc scheme,
536 const Size stateGridPoints, const Size timeStepsPerYear,
537 const Real mesherEpsilon,
538 const Handle<YieldTermStructure>& discountCurve,
539 const Size americanExerciseTimeStepsPerYear)
541 QuantLib::ext::make_shared<LgmFdSolver>(model, maxTime, scheme, stateGridPoints, timeStepsPerYear, mesherEpsilon),
542 discountCurve, americanExerciseTimeStepsPerYear) {
543 registerWith(solver_->model());
544 registerWith(discountCurve_);
545}
546
548 legs_ = arguments_.legs;
549 payer_ = arguments_.payer;
550 currency_ = arguments_.currency;
551 exercise_ = arguments_.exercise;
552 settlementType_ = arguments_.settlementType;
553 settlementMethod_ = arguments_.settlementMethod;
554
556
557 results_.value = npv_;
558 results_.underlyingNpv = underlyingNpv_;
559 results_.additionalResults = additionalResults_;
560 results_.additionalResults["underlyingNpv"] = underlyingNpv_;
561} // NumericLgmSwaptionEngine::calculate
562
563NumericLgmSwaptionEngine::NumericLgmSwaptionEngine(const QuantLib::ext::shared_ptr<LinearGaussMarkovModel>& model,
564 const Real sy, const Size ny, const Real sx, const Size nx,
565 const Handle<YieldTermStructure>& discountCurve,
566 const Size americanExerciseTimeStepsPerYear)
567 : NumericLgmMultiLegOptionEngineBase(QuantLib::ext::make_shared<LgmConvolutionSolver2>(model, sy, ny, sx, nx),
568 discountCurve, americanExerciseTimeStepsPerYear) {
569 registerWith(solver_->model());
570 registerWith(discountCurve_);
571}
572
573NumericLgmSwaptionEngine::NumericLgmSwaptionEngine(const QuantLib::ext::shared_ptr<LinearGaussMarkovModel>& model,
574 const Real maxTime, const QuantLib::FdmSchemeDesc scheme,
575 const Size stateGridPoints, const Size timeStepsPerYear,
576 const Real mesherEpsilon,
577 const Handle<YieldTermStructure>& discountCurve,
578 const Size americanExerciseTimeStepsPerYear)
580 QuantLib::ext::make_shared<LgmFdSolver>(model, maxTime, scheme, stateGridPoints, timeStepsPerYear, mesherEpsilon),
581 discountCurve, americanExerciseTimeStepsPerYear) {
582 registerWith(solver_->model());
583 registerWith(discountCurve_);
584}
585
587 legs_ = arguments_.legs;
588 payer_.resize(arguments_.payer.size());
589 for (Size i = 0; i < arguments_.payer.size(); ++i) {
590 payer_[i] = QuantLib::close_enough(arguments_.payer[i], -1.0);
591 }
592 currency_ = std::vector<Currency>(legs_.size(), arguments_.swap->iborIndex()->currency());
593 exercise_ = arguments_.exercise;
594 settlementType_ = arguments_.settlementType;
595 settlementMethod_ = arguments_.settlementMethod;
596
598
599 results_.value = npv_;
600 results_.additionalResults = additionalResults_;
601 results_.additionalResults["underlyingNpv"] = underlyingNpv_;
602} // NumericLgmSwaptionEngine::calculate
603
605 const QuantLib::ext::shared_ptr<LinearGaussMarkovModel>& model, const Real sy, const Size ny, const Real sx, const Size nx,
606 const Handle<YieldTermStructure>& discountCurve, const Size americanExerciseTimeStepsPerYear)
607 : NumericLgmMultiLegOptionEngineBase(QuantLib::ext::make_shared<LgmConvolutionSolver2>(model, sy, ny, sx, nx),
608 discountCurve, americanExerciseTimeStepsPerYear) {
609 registerWith(solver_->model());
610 registerWith(discountCurve_);
611}
612
614 const QuantLib::ext::shared_ptr<LinearGaussMarkovModel>& model, const Real maxTime, const QuantLib::FdmSchemeDesc scheme,
615 const Size stateGridPoints, const Size timeStepsPerYear, const Real mesherEpsilon,
616 const Handle<YieldTermStructure>& discountCurve, const Size americanExerciseTimeStepsPerYear)
618 QuantLib::ext::make_shared<LgmFdSolver>(model, maxTime, scheme, stateGridPoints, timeStepsPerYear, mesherEpsilon),
619 discountCurve, americanExerciseTimeStepsPerYear) {
620 registerWith(solver_->model());
621 registerWith(discountCurve_);
622}
623
625 legs_ = arguments_.legs;
626 payer_.resize(arguments_.payer.size());
627 for (Size i = 0; i < arguments_.payer.size(); ++i) {
628 payer_[i] = QuantLib::close_enough(arguments_.payer[i], -1.0);
629 }
630 currency_ = std::vector<Currency>(legs_.size(), arguments_.swap->iborIndex()->currency());
631 exercise_ = arguments_.exercise;
632 settlementType_ = arguments_.settlementType;
633 settlementMethod_ = arguments_.settlementMethod;
634
636
637 results_.value = npv_;
638 results_.additionalResults = additionalResults_;
639 results_.additionalResults["underlyingNpv"] = underlyingNpv_;
640} // NumericLgmSwaptionEngine::calculate
641
642} // namespace QuantExt
coupon paying the weighted average of the daily overnight rate
coupon paying a capped / floored average bma rate
const Instrument::results * results_
Definition: cdsoption.cpp:81
Numerical convolution solver for the LGM model.
Numerical FD solver for the LGM model.
Definition: lgmfdsolver.hpp:36
QuantLib::ext::shared_ptr< IrLgm1fParametrization > parametrization() const
RandomVariable reducedDiscountBond(const Time t, const Time T, const RandomVariable &x, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >()) const
const std::vector< Leg > & legs() const
const Settlement::Type settlementType() const
const Settlement::Method settlementMethod() const
const QuantLib::ext::shared_ptr< Exercise > exercise() const
const std::vector< Currency > & currency() const
const std::vector< bool > & payer() const
QuantLib::ext::shared_ptr< LgmBackwardSolver > solver_
CashflowInfo buildCashflowInfo(const Size i, const Size j) const
static bool instrumentIsHandled(const MultiLegOption &m, std::vector< std::string > &messages)
NumericLgmMultiLegOptionEngineBase(const QuantLib::ext::shared_ptr< LgmBackwardSolver > &solver, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >(), const Size americanExerciseTimeStepsPerYear=24)
NumericLgmMultiLegOptionEngine(const QuantLib::ext::shared_ptr< LinearGaussMarkovModel > &model, const Real sy, const Size ny, const Real sx, const Size nx, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >(), const Size americanExerciseTimeStepsPerYear=24)
NumericLgmNonstandardSwaptionEngine(const QuantLib::ext::shared_ptr< LinearGaussMarkovModel > &model, const Real sy, const Size ny, const Real sx, const Size nx, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >(), const Size americanExerciseTimeStepsPerYear=24)
NumericLgmSwaptionEngine(const QuantLib::ext::shared_ptr< LinearGaussMarkovModel > &model, const Real sy, const Size ny, const Real sx, const Size nx, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >(), const Size americanExerciseTimeStepsPerYear=24)
numeric convolution solver for the LGM model using RandoMVariable
std::map< std::string, boost::any > getAdditionalResultsMap(const LgmCalibrationInfo &info)
RandomVariable getRebatePv(const LgmVectorised &lgm, const Real t, const RandomVariable &x, const Handle< YieldTermStructure > &discountCurve, const QuantLib::ext::shared_ptr< RebatedExercise > &exercise, const Date &d)
CompiledFormula min(CompiledFormula x, const CompiledFormula &y)
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)
coupon paying the compounded daily overnight rate, copy of QL class, added includeSpread flag
more flexible version of ql class
JY INF index sigma component.
std::function< RandomVariable(const LgmVectorised &, const Real, const RandomVariable &, const Handle< YieldTermStructure > &)> calculator_
RandomVariable pv(const LgmVectorised &lgm, const Real t, const RandomVariable &state, const Handle< YieldTermStructure > &discountCurve) const
Real at(const Size i) const
Coupon with a number of sub-periods.
Swap::arguments * arguments_
std::vector< Size > steps