26#include <ql/cashflows/capflooredcoupon.hpp>
27#include <ql/cashflows/fixedratecoupon.hpp>
28#include <ql/cashflows/floatingratecoupon.hpp>
29#include <ql/cashflows/simplecashflow.hpp>
30#include <ql/exercise.hpp>
31#include <ql/experimental/coupons/strippedcapflooredcoupon.hpp>
38 const std::string& baseCcy,
const std::map<std::string, Handle<YieldTermStructure>>& discountCurves,
39 const std::map<std::string, Handle<Quote>>& fxSpots,
const QuantLib::ext::shared_ptr<LinearGaussMarkovModel>& model,
40 const Real sy,
const Size ny,
const Real sx,
const Size nx,
41 const Handle<DefaultProbabilityTermStructure>& defaultCurve,
const Handle<Quote>& recoveryRate,
42 const Size maxGapDays,
const Size maxDiscretisationPoints)
44 maxDiscretisationPoints),
51 const Real cap,
const bool nakedOption) {
52 Size n = fixing.
size();
57 if (floor != -QL_MAX_REAL) {
58 Real effStrike = (floor - spread) / gearing;
62 if (cap != QL_MAX_REAL) {
63 Real effStrike = (cap - spread) / gearing;
69 if (floor != -QL_MAX_REAL && cap != QL_MAX_REAL)
70 rate = floorletRate - capletRate;
72 rate = floorletRate + capletRate;
81class IborCouponAnalyzer {
83 explicit IborCouponAnalyzer(
const QuantLib::ext::shared_ptr<CashFlow>& c) {
84 scf_ = QuantLib::ext::dynamic_pointer_cast<StrippedCappedFlooredCoupon>(c);
88 cf_ = QuantLib::ext::dynamic_pointer_cast<CappedFlooredCoupon>(c);
89 QuantLib::ext::shared_ptr<CashFlow> cc = c;
91 cc =
cf_->underlying();
92 ibor_ = QuantLib::ext::dynamic_pointer_cast<IborCoupon>(cc);
95 QuantLib::ext::shared_ptr<IborCoupon> underlying()
const {
return ibor_; }
98 if (cf_ &&
cf_->cap() != Null<Real>())
105 if (cf_ &&
cf_->floor() != Null<Real>())
111 bool nakedOption()
const {
return scf_ !=
nullptr; }
114 QuantLib::ext::shared_ptr<CappedFlooredCoupon>
cf_;
115 QuantLib::ext::shared_ptr<StrippedCappedFlooredCoupon>
scf_;
116 QuantLib::ext::shared_ptr<IborCoupon>
ibor_;
119class ONCouponAnalyzer {
121 explicit ONCouponAnalyzer(
const QuantLib::ext::shared_ptr<CashFlow>& c) {
122 cfcomp_ = QuantLib::ext::dynamic_pointer_cast<QuantExt::CappedFlooredOvernightIndexedCoupon>(c);
123 cfavg_ = QuantLib::ext::dynamic_pointer_cast<QuantExt::CappedFlooredAverageONIndexedCoupon>(c);
127 comp_ = QuantLib::ext::dynamic_pointer_cast<QuantExt::OvernightIndexedCoupon>(c);
131 avg_ = QuantLib::ext::dynamic_pointer_cast<QuantExt::AverageONIndexedCoupon>(c);
134 bool isONCoupon()
const {
return comp_ !=
nullptr ||
avg_ !=
nullptr; }
135 bool isAveraging()
const {
return avg_ !=
nullptr; }
137 const std::vector<Date>& fixingDates()
const {
138 if (comp_ !=
nullptr)
139 return comp_->fixingDates();
140 else if (avg_ !=
nullptr)
141 return avg_->fixingDates();
143 QL_FAIL(
"internal error, requested fixingDates from ONCouponAnalyzer, but no on coupon is given.");
147 const std::vector<Date>& valueDates()
const {
148 if (comp_ !=
nullptr)
149 return comp_->valueDates();
150 else if (avg_ !=
nullptr)
151 return avg_->valueDates();
153 QL_FAIL(
"internal error, requested valueDates from ONCouponAnalyzer, but no on coupon is given.");
157 const std::vector<Real>& dt()
const {
158 if (comp_ !=
nullptr)
160 else if (avg_ !=
nullptr)
163 QL_FAIL(
"internal error, requested dt from ONCouponAnalyzer, but no on coupon is given.");
167 QuantLib::ext::shared_ptr<OvernightIndex> overnightIndex()
const {
168 if (comp_ !=
nullptr)
169 return comp_->overnightIndex();
170 else if (avg_ !=
nullptr)
171 return avg_->overnightIndex();
173 QL_FAIL(
"internal error, requested overnightIndex from ONCouponAnalyzer, but no on coupon is given.");
177 Real gearing()
const {
178 if (comp_ !=
nullptr)
179 return comp_->gearing();
180 else if (avg_ !=
nullptr)
181 return avg_->gearing();
183 QL_FAIL(
"internal error, requested gearing from ONCouponAnalyzer, but no on coupon is given.");
187 Real spread()
const {
188 if (comp_ !=
nullptr)
189 return comp_->spread();
190 else if (avg_ !=
nullptr)
191 return avg_->spread();
193 QL_FAIL(
"internal error, requested spread from ONCouponAnalyzer, but no on coupon is given.");
197 Real nominal()
const {
198 if (comp_ !=
nullptr)
199 return comp_->nominal();
200 else if (avg_ !=
nullptr)
201 return avg_->nominal();
203 QL_FAIL(
"internal error, requested nominal from ONCouponAnalyzer, but no on coupon is given.");
208 if (cfcomp_ !=
nullptr &&
cfcomp_->cap() != Null<Real>())
210 else if (cfavg_ !=
nullptr &&
cfavg_->cap() != Null<Real>())
217 if (cfcomp_ !=
nullptr &&
cfcomp_->floor() != Null<Real>())
219 else if (cfavg_ !=
nullptr &&
cfavg_->floor() != Null<Real>())
225 bool localCapFloor()
const {
226 if (cfcomp_ !=
nullptr)
227 return cfcomp_->localCapFloor();
228 else if (cfavg_ !=
nullptr)
229 return cfavg_->localCapFloor();
234 bool nakedOption()
const {
235 if (cfcomp_ !=
nullptr)
237 else if (cfavg_ !=
nullptr)
238 return cfavg_->nakedOption();
243 Size rateCutoff()
const {
244 if (comp_ !=
nullptr)
245 return comp_->rateCutoff();
246 else if (avg_ !=
nullptr)
247 return avg_->rateCutoff();
249 QL_FAIL(
"internal error, requested rateCutoff from ONCouponAnalyzer, but no on coupon is given.");
253 bool includeSpread()
const {
254 if (comp_ !=
nullptr)
255 return comp_->includeSpread();
256 else if (cfavg_ !=
nullptr)
257 return cfavg_->includeSpread();
261 const Period& lookback()
const {
262 if (comp_ !=
nullptr)
263 return comp_->lookback();
264 else if (avg_ !=
nullptr)
265 return avg_->lookback();
267 QL_FAIL(
"internal error, requested lookback from ONCouponAnalyzer, but no on coupon is given.");
271 Real accrualPeriod()
const {
272 if (comp_ !=
nullptr)
273 return comp_->accrualPeriod();
274 else if (avg_ !=
nullptr)
275 return avg_->accrualPeriod();
277 QL_FAIL(
"internal error, requested accrualPeriod from ONCouponAnalyzer, but no on coupon is given.");
281 DayCounter dayCounter()
const {
282 if (comp_ !=
nullptr)
283 return comp_->dayCounter();
284 else if (avg_ !=
nullptr)
285 return avg_->dayCounter();
287 QL_FAIL(
"internal error, requested dayCounter from ONCouponAnalyzer, but no on coupon is given.");
291 const Date& accrualStartDate()
const {
292 if (comp_ !=
nullptr)
293 return comp_->accrualStartDate();
294 else if (avg_ !=
nullptr)
295 return avg_->accrualStartDate();
297 QL_FAIL(
"internal error, requested accrualStartDate from ONCouponAnalyzer, but no on coupon is given.");
302 QuantLib::ext::shared_ptr<QuantExt::OvernightIndexedCoupon>
comp_;
303 QuantLib::ext::shared_ptr<QuantExt::AverageONIndexedCoupon>
avg_;
304 QuantLib::ext::shared_ptr<QuantExt::CappedFlooredOvernightIndexedCoupon>
cfcomp_;
305 QuantLib::ext::shared_ptr<QuantExt::CappedFlooredAverageONIndexedCoupon>
cfavg_;
308Size getEventIndex(
const std::vector<Date>& eventDates,
const Date& d) {
309 auto ed = std::find(eventDates.begin(), eventDates.end(), d);
310 QL_REQUIRE(ed != eventDates.end(),
"internal error, can not find event date for " << d);
311 return std::distance(eventDates.begin(), ed);
314int getLatestRelevantCallIndex(
const Date& accrualStart,
const std::vector<Date>& callDates,
315 const std::vector<Date>& eventDates) {
316 auto it = std::upper_bound(callDates.begin(), callDates.end(), accrualStart);
317 if (it == callDates.end())
318 return std::numeric_limits<int>::max();
319 else if (it == callDates.begin())
322 return static_cast<int>(getEventIndex(eventDates, *std::next(it, -1)));
325RandomVariable& getRv(std::map<int, RandomVariable>& map,
const int index,
const Size
size) {
335 for (
auto const& ccy :
arguments_.underlyingCcys) {
338 "NumericLgmRiskParticipationAgreementEngine::protectionLegNpv(): underlying ccys must all be the same, got "
339 << ccy <<
", " <<
arguments_.underlyingCcys.front());
344 std::vector<Date> optionDates;
345 for (Size i = 0; i <
gridDates_.size() - 1; ++i) {
354 std::vector<Date> couponDates;
356 for (
auto const& c : l) {
360 IborCouponAnalyzer iborCouponAnalyzer(c);
361 ONCouponAnalyzer onCouponAnalyzer(c);
362 if (iborCouponAnalyzer.underlying() !=
nullptr) {
363 if (iborCouponAnalyzer.underlying()->fixingDate() >=
referenceDate_) {
365 couponDates.push_back(iborCouponAnalyzer.underlying()->fixingDate());
368 couponDates.push_back(c->date());
370 }
else if (onCouponAnalyzer.isONCoupon()) {
371 if (onCouponAnalyzer.fixingDates().empty())
373 couponDates.push_back(std::max(onCouponAnalyzer.fixingDates().front(),
referenceDate_));
374 }
else if (QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(c) !=
nullptr ||
375 QuantLib::ext::dynamic_pointer_cast<SimpleCashFlow>(c) !=
nullptr) {
376 couponDates.push_back(c->date());
378 QL_FAIL(
"NumericLgmRiskParticipationAgreementEngine: unsupported coupon type when constructing event "
379 "dates, only (capped/floored) Ibor, OIS, Fixed, SimpleCashFlow supported");
386 std::vector<Date> callDates;
387 std::vector<Real> callRebates;
388 std::vector<Date> callRebatePayDates;
391 for (
auto const& d :
arguments_.exercise->dates()) {
393 callDates.push_back(d);
394 if (
auto r = QuantLib::ext::dynamic_pointer_cast<QuantExt::RebatedExercise>(
arguments_.exercise)) {
395 callRebates.push_back(r->rebate(idx));
396 callRebatePayDates.push_back(r->rebatePaymentDate(idx));
398 callRebates.push_back(0);
399 callRebatePayDates.push_back(d);
408 std::set<Date> uniqueDates;
409 uniqueDates.insert(optionDates.begin(), optionDates.end());
410 uniqueDates.insert(couponDates.begin(), couponDates.end());
411 uniqueDates.insert(callDates.begin(), callDates.end());
413 std::vector<Date> eventDates(uniqueDates.begin(), uniqueDates.end());
417 std::vector<Real> eventTimes;
418 for (
auto const& d : eventDates) {
424 std::vector<std::vector<Real>> fixedCoupons(eventDates.size());
425 std::vector<std::vector<int>> fixedCouponsLatestRelevantCallEventIndex(eventDates.size());
427 std::vector<std::vector<QuantLib::ext::shared_ptr<InterestRateIndex>>> floatingIndices(eventDates.size());
428 std::vector<std::vector<Real>> floatingGearings(eventDates.size());
429 std::vector<std::vector<Real>> floatingSpreads(eventDates.size());
430 std::vector<std::vector<Real>> floatingCaps(eventDates.size());
431 std::vector<std::vector<Real>> floatingFloors(eventDates.size());
432 std::vector<std::vector<Real>> floatingMultipliers(eventDates.size());
433 std::vector<std::vector<Real>> payTimes(eventDates.size());
434 std::vector<std::vector<bool>> nakedOption(eventDates.size());
435 std::vector<std::vector<bool>> onIsAveraging(eventDates.size());
436 std::vector<std::vector<std::vector<Date>>> onFixingDates(eventDates.size());
437 std::vector<std::vector<std::vector<Date>>> onValueDates(eventDates.size());
438 std::vector<std::vector<std::vector<Real>>> onDt(eventDates.size());
439 std::vector<std::vector<Size>> onRateCutoff(eventDates.size());
440 std::vector<std::vector<bool>> onIncludeSpread(eventDates.size());
441 std::vector<std::vector<Period>> onLookback(eventDates.size());
442 std::vector<std::vector<bool>> onLocalCapFloor(eventDates.size());
443 std::vector<std::vector<int>> floatingCouponsLatestRelevantCallEventIndex(eventDates.size());
445 std::vector<Size> optionDateIndex(eventDates.size());
447 std::vector<Size> callDateIndex(eventDates.size());
448 std::vector<Real> callRebateAmount(eventDates.size());
449 std::vector<Real> callRebatePayTime(eventDates.size());
457 std::vector<std::set<Size>> trappedCouponIndex(eventDates.size());
465 std::vector<std::set<Size>> trappedCouponIndexCall(eventDates.size());
467 for (Size i = 0; i < eventDates.size(); ++i) {
471 auto od = std::find(optionDates.begin(), optionDates.end(), eventDates[i]);
472 if (od != optionDates.end()) {
473 optionDateIndex[i] = std::distance(optionDates.begin(), od);
476 for (
auto const& c : l) {
477 IborCouponAnalyzer iborCouponAnalyzer(c);
478 ONCouponAnalyzer onCouponAnalyzer(c);
479 if (iborCouponAnalyzer.underlying() !=
nullptr) {
480 if (iborCouponAnalyzer.underlying()->fixingDate() >=
referenceDate_ &&
481 iborCouponAnalyzer.underlying()->fixingDate() < eventDates[i] &&
482 eventDates[i] < c->date()) {
483 trappedCouponIndex[i].insert(
484 getEventIndex(eventDates, iborCouponAnalyzer.underlying()->fixingDate()));
486 }
else if (onCouponAnalyzer.isONCoupon()) {
487 if (onCouponAnalyzer.fixingDates().empty())
489 Date d = std::max(onCouponAnalyzer.fixingDates().front(),
referenceDate_);
490 if (d < eventDates[i] && eventDates[i] < c->date()) {
491 trappedCouponIndex[i].insert(getEventIndex(eventDates, d));
498 optionDateIndex[i] = Null<Size>();
503 auto cd = std::find(callDates.begin(), callDates.end(), eventDates[i]);
504 if (cd != callDates.end()) {
505 callDateIndex[i] = std::distance(callDates.begin(), cd);
508 for (
auto const& c : l) {
509 IborCouponAnalyzer iborCouponAnalyzer(c);
510 ONCouponAnalyzer onCouponAnalyzer(c);
511 if (iborCouponAnalyzer.underlying() !=
nullptr) {
512 if (iborCouponAnalyzer.underlying()->fixingDate() >=
referenceDate_ &&
513 iborCouponAnalyzer.underlying()->fixingDate() < eventDates[i] &&
514 eventDates[i] <= iborCouponAnalyzer.underlying()->accrualStartDate()) {
515 trappedCouponIndexCall[i].insert(
516 getEventIndex(eventDates, iborCouponAnalyzer.underlying()->fixingDate()));
518 }
else if (onCouponAnalyzer.isONCoupon()) {
519 if (onCouponAnalyzer.fixingDates().empty())
521 Date d = std::max(onCouponAnalyzer.fixingDates().front(),
referenceDate_);
522 if (d < eventDates[i] && eventDates[i] <= onCouponAnalyzer.accrualStartDate()) {
523 trappedCouponIndexCall[i].insert(getEventIndex(eventDates, d));
530 callDateIndex[i] = Null<Size>();
535 auto underlyingPayer =
arguments_.underlyingPayer.begin();
537 bool isPayer = *(underlyingPayer++);
538 for (
auto const& c : l) {
542 IborCouponAnalyzer iborCouponAnalyzer(c);
543 ONCouponAnalyzer onCouponAnalyzer(c);
544 if (iborCouponAnalyzer.underlying() !=
nullptr) {
545 if (iborCouponAnalyzer.underlying()->fixingDate() >=
referenceDate_ &&
546 iborCouponAnalyzer.underlying()->fixingDate() == eventDates[i]) {
548 floatingIndices[i].push_back(iborCouponAnalyzer.underlying()->iborIndex());
549 floatingGearings[i].push_back(iborCouponAnalyzer.underlying()->gearing());
550 floatingSpreads[i].push_back(iborCouponAnalyzer.underlying()->spread());
551 floatingMultipliers[i].push_back(iborCouponAnalyzer.underlying()->nominal() *
552 iborCouponAnalyzer.underlying()->accrualPeriod() *
553 (isPayer ? -1.0 : 1.0));
554 floatingCaps[i].push_back(iborCouponAnalyzer.cap());
555 floatingFloors[i].push_back(iborCouponAnalyzer.floor());
556 nakedOption[i].push_back(iborCouponAnalyzer.nakedOption());
558 }
else if (iborCouponAnalyzer.underlying()->fixingDate() <
referenceDate_ &&
559 c->date() == eventDates[i]) {
561 fixedCoupons[i].push_back((isPayer ? -1.0 : 1.0) * c->amount());
562 fixedCouponsLatestRelevantCallEventIndex[i].push_back(getLatestRelevantCallIndex(
563 iborCouponAnalyzer.underlying()->accrualStartDate(), callDates, eventDates));
568 onFixingDates[i].push_back(std::vector<Date>());
569 onValueDates[i].push_back(std::vector<Date>());
570 onDt[i].push_back(std::vector<Real>());
571 onRateCutoff[i].push_back(0);
572 onIncludeSpread[i].push_back(
false);
573 onLookback[i].push_back(0 * Days);
574 onLocalCapFloor[i].push_back(
false);
575 onIsAveraging[i].push_back(
false);
576 floatingCouponsLatestRelevantCallEventIndex[i].push_back(getLatestRelevantCallIndex(
577 iborCouponAnalyzer.underlying()->accrualStartDate(), callDates, eventDates));
578 }
else if (onCouponAnalyzer.isONCoupon()) {
579 if (onCouponAnalyzer.fixingDates().empty())
582 Date d = std::max(onCouponAnalyzer.fixingDates().front(),
referenceDate_);
583 if (d == eventDates[i]) {
584 floatingIndices[i].push_back(onCouponAnalyzer.overnightIndex());
585 floatingGearings[i].push_back(onCouponAnalyzer.gearing());
586 floatingSpreads[i].push_back(onCouponAnalyzer.spread());
587 floatingMultipliers[i].push_back(onCouponAnalyzer.nominal() * onCouponAnalyzer.accrualPeriod() *
588 (isPayer ? -1.0 : 1.0));
589 floatingCaps[i].push_back(onCouponAnalyzer.cap());
590 floatingFloors[i].push_back(onCouponAnalyzer.floor());
591 nakedOption[i].push_back(onCouponAnalyzer.nakedOption());
592 onLocalCapFloor[i].push_back(onCouponAnalyzer.localCapFloor());
593 onFixingDates[i].push_back(onCouponAnalyzer.fixingDates());
594 onValueDates[i].push_back(onCouponAnalyzer.valueDates());
595 onDt[i].push_back(onCouponAnalyzer.dt());
596 onRateCutoff[i].push_back(onCouponAnalyzer.rateCutoff());
597 onIncludeSpread[i].push_back(onCouponAnalyzer.includeSpread());
598 onLookback[i].push_back(onCouponAnalyzer.lookback());
599 onIsAveraging[i].push_back(onCouponAnalyzer.isAveraging());
601 floatingCouponsLatestRelevantCallEventIndex[i].push_back(
602 getLatestRelevantCallIndex(onCouponAnalyzer.accrualStartDate(), callDates, eventDates));
604 }
else if (
auto cpn = QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(c)) {
605 if (c->date() == eventDates[i]) {
607 fixedCoupons[i].push_back((isPayer ? -1.0 : 1.0) * cpn->amount());
608 fixedCouponsLatestRelevantCallEventIndex[i].push_back(
609 getLatestRelevantCallIndex(cpn->accrualStartDate(), callDates, eventDates));
611 }
else if (QuantLib::ext::dynamic_pointer_cast<SimpleCashFlow>(c) !=
nullptr) {
612 if (c->date() == eventDates[i]) {
614 fixedCoupons[i].push_back((isPayer ? -1.0 : 1.0) * c->amount());
615 fixedCouponsLatestRelevantCallEventIndex[i].push_back(
616 getLatestRelevantCallIndex(c->date(), callDates, eventDates));
619 QL_FAIL(
"NumericLgmRiskParticipationAgreementEngine: unsupported coupon type when collecting "
620 "coupon data, only (capped/floored) Ibor, OIS, Fixed, SimpleCashFlow supported");
633 std::map<int, RandomVariable> underlyingPv;
638 std::vector<Real> optionPv(optionDates.size(), 0.0);
640 for (
int i =
static_cast<int>(eventDates.size()) - 1; i >= 0; --i) {
648 if (i <
static_cast<int>(eventDates.size()) - 1) {
649 for (
auto& u : underlyingPv) {
650 u.second =
rollback(u.second, eventTimes[i + 1], eventTimes[i]);
656 for (
auto& u : underlyingPv) {
657 if (u.first != 0 && i <= u.first) {
658 underlyingPv[0] += u.second;
664 if (i <
static_cast<int>(eventDates.size()) - 1) {
665 swaptionPv =
rollback(swaptionPv, eventTimes[i + 1], eventTimes[i]);
670 for (Size k = 0; k < floatingIndices[i].size(); ++k) {
672 if (
auto on = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(floatingIndices[i][k])) {
673 if (onIsAveraging[i][k]) {
675 lgm.
averagedOnRate(on, onFixingDates[i][k], onValueDates[i][k], onDt[i][k], onRateCutoff[i][k],
676 onIncludeSpread[i][k], floatingSpreads[i][k], floatingGearings[i][k],
677 onLookback[i][k], floatingCaps[i][k], floatingFloors[i][k],
678 onLocalCapFloor[i][k], nakedOption[i][k], eventTimes[i], states);
680 rate = lgm.
compoundedOnRate(on, onFixingDates[i][k], onValueDates[i][k], onDt[i][k],
681 onRateCutoff[i][k], onIncludeSpread[i][k], floatingSpreads[i][k],
682 floatingGearings[i][k], onLookback[i][k], floatingCaps[i][k],
683 floatingFloors[i][k], onLocalCapFloor[i][k], nakedOption[i][k],
684 eventTimes[i], states);
686 }
else if (
auto ibor = QuantLib::ext::dynamic_pointer_cast<IborIndex>(floatingIndices[i][k])) {
687 rate = computeIborRate(lgm.
fixing(ibor, eventDates[i], eventTimes[i], states), floatingSpreads[i][k],
688 floatingGearings[i][k], floatingFloors[i][k], floatingCaps[i][k],
691 QL_FAIL(
"NumericLgmRiskParticipationAgreementEngine: unexpected index, should be IborIndex or "
697 getRv(underlyingPv, floatingCouponsLatestRelevantCallEventIndex[i][k],
gridSize()) += tmp;
704 if (optionDateIndex[i] != Null<Size>()) {
710 for (
auto const& t : trappedCouponIndex[i]) {
711 for (Size k = 0; k < floatingIndices[t].size(); ++k) {
712 if (payTimes[t][k] > eventTimes[i] && !QuantLib::close_enough(eventTimes[i], payTimes[t][k])) {
714 if (
auto on = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(floatingIndices[t][k])) {
715 if (onIsAveraging[t][k]) {
717 on, onFixingDates[t][k], onValueDates[t][k], onDt[t][k], onRateCutoff[t][k],
718 onIncludeSpread[t][k], floatingSpreads[t][k], floatingGearings[t][k],
719 onLookback[t][k], floatingCaps[t][k], floatingFloors[t][k], onLocalCapFloor[t][k],
720 nakedOption[t][k], eventTimes[i], states);
723 on, onFixingDates[t][k], onValueDates[t][k], onDt[t][k], onRateCutoff[t][k],
724 onIncludeSpread[t][k], floatingSpreads[t][k], floatingGearings[t][k],
725 onLookback[t][k], floatingCaps[t][k], floatingFloors[t][k], onLocalCapFloor[t][k],
726 nakedOption[t][k], eventTimes[i], states);
728 }
else if (
auto ibor = QuantLib::ext::dynamic_pointer_cast<IborIndex>(floatingIndices[t][k])) {
729 rate = computeIborRate(
730 lgm.
fixing(ibor, ibor->fixingCalendar().adjust(eventDates[i]), eventTimes[i], states),
731 floatingSpreads[t][k], floatingGearings[t][k], floatingFloors[t][k], floatingCaps[t][k],
742 if (callDateIndex[i] != Null<Size>()) {
746 for (
auto const& t : trappedCouponIndexCall[i]) {
747 for (Size k = 0; k < floatingIndices[t].size(); ++k) {
748 if (payTimes[t][k] > eventTimes[i] && !QuantLib::close_enough(eventTimes[i], payTimes[t][k])) {
750 if (
auto on = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(floatingIndices[t][k])) {
751 if (onIsAveraging[t][k]) {
753 on, onFixingDates[t][k], onValueDates[t][k], onDt[t][k], onRateCutoff[t][k],
754 onIncludeSpread[t][k], floatingSpreads[t][k], floatingGearings[t][k],
755 onLookback[t][k], floatingCaps[t][k], floatingFloors[t][k], onLocalCapFloor[t][k],
756 nakedOption[t][k], eventTimes[i], states);
759 on, onFixingDates[t][k], onValueDates[t][k], onDt[t][k], onRateCutoff[t][k],
760 onIncludeSpread[t][k], floatingSpreads[t][k], floatingGearings[t][k],
761 onLookback[t][k], floatingCaps[t][k], floatingFloors[t][k], onLocalCapFloor[t][k],
762 nakedOption[t][k], eventTimes[i], states);
764 }
else if (
auto ibor = QuantLib::ext::dynamic_pointer_cast<IborIndex>(floatingIndices[t][k])) {
765 rate = computeIborRate(
766 lgm.
fixing(ibor, ibor->fixingCalendar().adjust(eventDates[i]), eventTimes[i], states),
767 floatingSpreads[t][k], floatingGearings[t][k], floatingFloors[t][k], floatingCaps[t][k],
780 auto callRebateValue =
786 swaptionPv = callMultiplier *
787 max(callMultiplier * swaptionPv,
788 callMultiplier * (nakedOptionMultiplier * (underlyingPv[0] + trappedCouponPvCall)) +
795 for (
int j = 0; j <
arguments_.premium.size(); j++) {
796 Real premiumAmount = 0;
798 premiumAmount = -
arguments_.premium[j]->amount();
800 premiumAmount =
arguments_.premium[j]->amount();
803 if (
arguments_.premium[j]->date() > eventDates[i]) {
813 tmp += trappedCouponPv;
814 for (
auto const& u : underlyingPv)
822 if (optionDateIndex[i] != Null<Size>()) {
823 optionPv[optionDateIndex[i]] =
rollback(currentPositivePv, eventTimes[i], 0.0).
at(0);
828 for (Size k = 0; k < fixedCoupons[i].size(); ++k) {
831 getRv(underlyingPv, fixedCouponsLatestRelevantCallEventIndex[i][k],
gridSize()) += tmp;
838 for (
auto& u : underlyingPv) {
840 underlyingPv[0] += u.second;
843 underlyingPv[0] =
rollback(underlyingPv[0], eventTimes[0], 0.0);
845 swaptionPv =
rollback(swaptionPv, eventTimes[0], 0.0);
850 "NumericLgmRiskParticipationAgreementEngine::protectionLegNpv(): empty fx spot for ccy pair "
854 for (Size i = 0; i <
gridDates_.size() - 1; ++i) {
861 results_.additionalResults[
"OptionNpvs"] = optionPv;
862 results_.additionalResults[
"OptionExerciseDates"] = optionDates;
863 results_.additionalResults[
"UnderlyingNpv"] = underlyingPv.at(0).at(0);
864 results_.additionalResults[
"SwaptionNpv"] = swaptionPv.
at(0);
const Instrument::results * results_
const boost::shared_ptr< LinearGaussMarkovModel > & model() const
RandomVariable rollback(const RandomVariable &v, const Real t1, const Real t0) const
RandomVariable stateGrid(const Real t) const
RandomVariable fixing(const boost::shared_ptr< InterestRateIndex > &index, const Date &fixingDate, const Time t, const RandomVariable &x) const
RandomVariable averagedOnRate(const boost::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 numeraire(const Time t, const RandomVariable &x, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >()) const
RandomVariable reducedDiscountBond(const Time t, const Time T, const RandomVariable &x, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >()) const
RandomVariable compoundedOnRate(const boost::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
Real protectionLegNpv() const override
NumericLgmRiskParticipationAgreementEngine(const std::string &baseCcy, const std::map< std::string, Handle< YieldTermStructure > > &discountCurves, const std::map< std::string, Handle< Quote > > &fxSpots, const QuantLib::ext::shared_ptr< QuantExt::LinearGaussMarkovModel > &model, const Real sy, const Size ny, const Real sx, const Size nx, const Handle< DefaultProbabilityTermStructure > &defaultCurve, const Handle< Quote > &recoveryRate, const Size maxGapDays=Null< Size >(), const Size maxDiscretisationPoints=Null< Size >())
Real effectiveRecoveryRate_
std::vector< Date > gridDates_
std::map< std::string, Handle< Quote > > fxSpots_
std::map< std::string, Handle< YieldTermStructure > > discountCurves_
Handle< DefaultProbabilityTermStructure > defaultCurve_
RandomVariable max(RandomVariable x, const RandomVariable &y)
RandomVariable min(RandomVariable x, const RandomVariable &y)
Size size(const ValueType &v)
Serializable Credit Default Swap.
QuantLib::ext::shared_ptr< QuantExt::CappedFlooredOvernightIndexedCoupon > cfcomp_
QuantLib::ext::shared_ptr< QuantExt::AverageONIndexedCoupon > avg_
QuantLib::ext::shared_ptr< CappedFlooredCoupon > cf_
QuantLib::ext::shared_ptr< QuantExt::OvernightIndexedCoupon > comp_
QuantLib::ext::shared_ptr< QuantExt::CappedFlooredAverageONIndexedCoupon > cfavg_
QuantLib::ext::shared_ptr< IborCoupon > ibor_
QuantLib::ext::shared_ptr< StrippedCappedFlooredCoupon > scf_
Real at(const Size i) const
Swap::arguments * arguments_