Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
numericlgmbgsflexiswapengine.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/cashflows/coupon.hpp>
22
23namespace QuantExt {
24
25NumericLgmBgsFlexiSwapEngine::NumericLgmBgsFlexiSwapEngine(const QuantLib::ext::shared_ptr<LinearGaussMarkovModel>& model,
26 const Real sy, const Size ny, const Real sx, const Size nx,
27 const Handle<Quote>& minCpr, const Handle<Quote>& maxCpr,
28 const Handle<YieldTermStructure>& discountCurve,
29 const Method method, const Real singleSwaptionThreshold)
30 : NumericLgmFlexiSwapEngineBase(model, sy, ny, sx, nx, discountCurve, method, singleSwaptionThreshold),
31 minCpr_(minCpr), maxCpr_(maxCpr) {
32 registerWith(this->model());
33 registerWith(discountCurve_);
34 registerWith(minCpr_);
35 registerWith(maxCpr_);
36} // NumericLgmFlexiSwapEngine::NumericLgmFlexiSwapEngine
37
38namespace {
39Real getNotional(const std::vector<Real> nominals, const std::vector<Date> dates, const Date& d) {
40 QL_REQUIRE(nominals.size() + 1 == dates.size(), "getNominal(): nominals size (" << nominals.size()
41 << ") + 1 must be dates size ("
42 << dates.size() << ")");
43 if (d < dates.front() || d >= dates.back())
44 return 0.0;
45 Size l = std::upper_bound(dates.begin(), dates.end(), d) - dates.begin();
46 return nominals[l - 1];
47}
48} // namespace
49
51 Date today = Settings::instance().evaluationDate();
52 // compute the lower and upper notionals bounds, in terms of the tranche notional schedule
53 Real currentLowerNotionalAfterPrepayment = 0.0, currentUpperNotionalAfterPrepayment = 0.0,
54 lastAggregatePrincipal = 0.0;
55 std::vector<Real> tmpLowerNotionalBound, tmpUpperNotionalBound;
56 Real effectiveMinCpr = minCpr_->value() / static_cast<Real>(arguments_.trancheNominalFrequency);
57 Real effectiveMaxCpr = maxCpr_->value() / static_cast<Real>(arguments_.trancheNominalFrequency);
58 for (Size i = 0; i < arguments_.trancheNominalDates.size() - 1; ++i) {
59 Real aggregatePrincipal = 0.0;
60 for (Size j = 0; j < arguments_.trancheNominals.size(); ++j)
61 aggregatePrincipal += arguments_.trancheNominals[j][i];
62 if (i == 0) {
63 // in the first period we do not have a prepayment
64 currentUpperNotionalAfterPrepayment = aggregatePrincipal;
65 currentLowerNotionalAfterPrepayment = aggregatePrincipal;
66 }
67 if (i > 0) {
68 // ratio of zero-cpr notionals
69 Real amortisationRate =
70 QuantLib::close_enough(aggregatePrincipal, 0.0) ? 0.0 : (aggregatePrincipal / lastAggregatePrincipal);
71 // we only prepay if the start date of the nominal is in the future
72 currentUpperNotionalAfterPrepayment =
73 currentUpperNotionalAfterPrepayment *
74 std::max(amortisationRate - (arguments_.trancheNominalDates[i] > today ? effectiveMinCpr : 0.0), 0.0);
75 currentLowerNotionalAfterPrepayment =
76 currentLowerNotionalAfterPrepayment *
77 std::max(amortisationRate - (arguments_.trancheNominalDates[i] > today ? effectiveMaxCpr : 0.0), 0.0);
78 }
79 // now that we have the current notional of the period we can determine the swap notional
80 // for this, we subtract the notionals of all tranches which are less senior...
81 Real subordinatedPrincipal = 0;
82 for (Size k = arguments_.trancheNominals.size() - 1; k > arguments_.referencedTranche; --k) {
83 subordinatedPrincipal += arguments_.trancheNominals[k][i];
84 }
85 // and then cap the result at the referenced tranche's volume and floor it at zero
86 tmpLowerNotionalBound.push_back(
87 std::min(std::max(currentLowerNotionalAfterPrepayment - subordinatedPrincipal, 0.0),
88 arguments_.trancheNominals[arguments_.referencedTranche][i]));
89 tmpUpperNotionalBound.push_back(
90 std::min(std::max(currentUpperNotionalAfterPrepayment - subordinatedPrincipal, 0.0),
91 arguments_.trancheNominals[arguments_.referencedTranche][i]));
92 // update the aggregate principal for the next period
93 lastAggregatePrincipal = aggregatePrincipal;
94 }
95
96 // convert the bounds to notional vectors for the fixed and floating schedule
97 std::vector<Real> lowerNotionalFixedBound, upperNotionalFixedBound, upperNotionalFloatingBound;
98 for (Size i = 0; i < arguments_.fixedResetDates.size(); ++i) {
99 lowerNotionalFixedBound.push_back(
100 getNotional(tmpLowerNotionalBound, arguments_.trancheNominalDates, arguments_.fixedResetDates[i]));
101 upperNotionalFixedBound.push_back(
102 getNotional(tmpUpperNotionalBound, arguments_.trancheNominalDates, arguments_.fixedResetDates[i]));
103 }
104 // derive floating nominal schedule from fixed to ensure they match
105 Size ratio =
106 arguments_.floatingResetDates.size() / arguments_.fixedResetDates.size(); // we know there is no remainder
107 upperNotionalFloatingBound.resize(upperNotionalFixedBound.size() * ratio);
108 Size i = 0; // remove in C++14 and instead write [i=0, &fixedNotional] in the capture below
109 std::generate(upperNotionalFloatingBound.begin(), upperNotionalFloatingBound.end(),
110 [i, &upperNotionalFixedBound, ratio]() mutable { return upperNotionalFixedBound[i++ / ratio]; });
111
112 // recalculate the fixed and floating coupons belonging to the upper Notional
113 std::vector<Real> upperFixedCoupons, upperFloatingCoupons;
114 for (Size i = 0; i < arguments_.fixedLeg.size(); ++i) {
115 auto cp = QuantLib::ext::dynamic_pointer_cast<QuantLib::Coupon>(arguments_.fixedLeg[i]);
116 upperFixedCoupons.push_back(cp->accrualPeriod() * cp->rate() * upperNotionalFixedBound[i]);
117 }
118 for (Size i = 0; i < arguments_.floatingLeg.size(); ++i) {
119 auto cp = QuantLib::ext::dynamic_pointer_cast<QuantLib::Coupon>(arguments_.floatingLeg[i]);
120 try {
121 upperFloatingCoupons.push_back(cp->accrualPeriod() * cp->rate() * upperNotionalFloatingBound[i]);
122 } catch (...) {
123 upperFloatingCoupons.push_back(Null<Real>());
124 }
125 }
126
127 // determine the option position, the holder is the payer of the structured (i.e. fixed) leg
128 QuantLib::Position::Type flexiOptionPosition =
129 arguments_.type == VanillaSwap::Payer ? QuantLib::Position::Long : QuantLib::Position::Short;
130
131 // set arguments in base engine
132 type = arguments_.type;
133 fixedNominal = upperNotionalFixedBound; // computed above
134 floatingNominal = upperNotionalFloatingBound; // computed above
135 fixedResetDates = arguments_.fixedResetDates;
136 fixedPayDates = arguments_.fixedPayDates;
137 floatingAccrualTimes = arguments_.floatingAccrualTimes;
138 floatingResetDates = arguments_.floatingResetDates;
139 floatingFixingDates = arguments_.floatingFixingDates;
140 floatingPayDates = arguments_.floatingPayDates;
141 fixedCoupons = upperFixedCoupons;
142 fixedRate = arguments_.fixedRate;
143 floatingGearings = arguments_.floatingGearings;
144 floatingSpreads = arguments_.floatingSpreads;
145 cappedRate = arguments_.cappedRate;
146 flooredRate = arguments_.flooredRate;
147 floatingCoupons = upperFloatingCoupons;
148 iborIndex = arguments_.iborIndex;
149 lowerNotionalBound = lowerNotionalFixedBound; // computed above
150 optionPosition = flexiOptionPosition; // computed above
152 std::vector<bool>(fixedNominal.size(), true); // each period is eligable for notional decrease
153
154 // calculate and set results
156 results_.value = result.first;
157 results_.additionalResults = getAdditionalResultsMap(model()->getCalibrationInfo());
158} // NumericLgmBgsFlexiSwapEngine::calculate
159
160} // namespace QuantExt
const Instrument::results * results_
Definition: cdsoption.cpp:81
const QuantLib::ext::shared_ptr< LinearGaussMarkovModel > & model() const
NumericLgmBgsFlexiSwapEngine(const QuantLib::ext::shared_ptr< LinearGaussMarkovModel > &model, const Real sy, const Size ny, const Real sx, const Size nx, const Handle< Quote > &minCpr, const Handle< Quote > &maxCpr, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >(), const Method method=Method::Automatic, const Real singleSwaptionThreshold=20.0)
Numerical engine for flexi swaps in the LGM model.
const Handle< YieldTermStructure > discountCurve_
QuantLib::ext::shared_ptr< IborIndex > iborIndex
std::map< std::string, boost::any > getAdditionalResultsMap(const LgmCalibrationInfo &info)
numeric engine for balance guaranteed swaps using a flexi swap proxy in the LGM model
JY INF index sigma component.
Swap::arguments * arguments_