Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
tenorbasisswap.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 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
19#include <ql/cashflows/iborcoupon.hpp>
20#include <ql/cashflows/overnightindexedcoupon.hpp>
21#include <ql/indexes/ibor/libor.hpp>
22#include <ql/math/solvers1d/brent.hpp>
23#include <ql/pricingengines/swap/discountingswapengine.hpp>
24
26
27using namespace QuantLib;
28
29namespace QuantExt {
30
31namespace {
32class FairSpreadHelper {
33public:
34 FairSpreadHelper(const TenorBasisSwap& swap, const Handle<YieldTermStructure>& discountCurve, Real nonSpreadLegNPV)
35 : nonSpreadLegNPV_(nonSpreadLegNPV) {
36
37 engine_ = QuantLib::ext::shared_ptr<PricingEngine>(new DiscountingSwapEngine(discountCurve));
38 arguments_ = dynamic_cast<Swap::arguments*>(engine_->getArguments());
39 swap.setupArguments(arguments_);
40 spreadLeg_ = arguments_->legs[0];
41 results_ = dynamic_cast<const Swap::results*>(engine_->getResults());
42 }
43
44 Real operator()(Spread tempSpread) const {
45 // Change the spread on the leg and recalculate
46 Leg::const_iterator it;
47 for (it = spreadLeg_.begin(); it != spreadLeg_.end(); ++it) {
48 QuantLib::ext::shared_ptr<QuantExt::SubPeriodsCoupon1> c = QuantLib::ext::dynamic_pointer_cast<QuantExt::SubPeriodsCoupon1>(*it);
49 c->spread() = tempSpread;
50 }
51 engine_->calculate();
52 return results_->legNPV[0] + nonSpreadLegNPV_;
53 }
54
55private:
56 QuantLib::ext::shared_ptr<PricingEngine> engine_;
61};
62} // namespace
63
64TenorBasisSwap::TenorBasisSwap(const Date& effectiveDate, Real nominal, const Period& swapTenor,
65 const QuantLib::ext::shared_ptr<IborIndex>& payIndex, Spread paySpread,
66 const Period& payFrequency, const QuantLib::ext::shared_ptr<IborIndex>& recIndex,
67 Spread recSpread, const Period& recFrequency, DateGeneration::Rule rule,
68 bool includeSpread, bool spreadOnRec, QuantExt::SubPeriodsCoupon1::Type type,
69 const bool telescopicValueDates)
70 : Swap(2), nominals_(std::vector<Real>(1, nominal)), payIndex_(payIndex), paySpread_(paySpread),
71 payFrequency_(payFrequency), recIndex_(recIndex), recSpread_(recSpread), recFrequency_(recFrequency),
72 includeSpread_(includeSpread), spreadOnRec_(spreadOnRec), type_(type),
73 telescopicValueDates_(telescopicValueDates) {
74
75 // Create the default pay and rec schedules
76 Date terminationDate = effectiveDate + swapTenor;
77
78 QuantLib::ext::shared_ptr<Libor> payIndexAsLibor = QuantLib::ext::dynamic_pointer_cast<Libor>(payIndex_);
79 payIndexCalendar_ = payIndexAsLibor != NULL ? payIndexAsLibor->jointCalendar() : payIndex_->fixingCalendar();
80 QuantLib::ext::shared_ptr<Libor> recIndexAsLibor = QuantLib::ext::dynamic_pointer_cast<Libor>(recIndex_);
82 recIndexAsLibor != NULL ? recIndexAsLibor->jointCalendar() : recIndex_->fixingCalendar();
83
84 paySchedule_ = MakeSchedule()
85 .from(effectiveDate)
86 .to(terminationDate)
87 .withTenor(payFrequency_)
88 .withCalendar(payIndexCalendar_)
89 .withConvention(payIndex_->businessDayConvention())
90 .withTerminationDateConvention(payIndex_->businessDayConvention())
91 .withRule(rule)
92 .endOfMonth(payIndex_->endOfMonth());
93
94 recSchedule_ = MakeSchedule()
95 .from(effectiveDate)
96 .to(terminationDate)
97 .withTenor(recFrequency_)
98 .withCalendar(recIndexCalendar_)
99 .withConvention(recIndex_->businessDayConvention())
100 .withTerminationDateConvention(recIndex_->businessDayConvention())
101 .withRule(rule)
102 .endOfMonth(recIndex_->endOfMonth());
103
104 // Create legs
106}
107
108TenorBasisSwap::TenorBasisSwap(Real nominal, const Schedule& paySchedule,
109 const QuantLib::ext::shared_ptr<IborIndex>& payIndex, Spread paySpread,
110 const Schedule& recSchedule, const QuantLib::ext::shared_ptr<IborIndex>& recIndex,
111 Spread recSpread, bool includeSpread, bool spreadOnRec, QuantExt::SubPeriodsCoupon1::Type type,
112 const bool telescopicValueDates)
113 : Swap(2), nominals_(std::vector<Real>(1, nominal)), paySchedule_(paySchedule), payIndex_(payIndex),
114 paySpread_(paySpread), recSchedule_(recSchedule), recIndex_(recIndex), recSpread_(recSpread),
115 includeSpread_(includeSpread), spreadOnRec_(spreadOnRec), type_(type), telescopicValueDates_(telescopicValueDates) {
116
117 // Create legs
119}
120
121TenorBasisSwap::TenorBasisSwap(const std::vector<Real>& nominals, const Schedule& paySchedule,
122 const QuantLib::ext::shared_ptr<IborIndex>& payIndex, Spread paySpread,
123 const Schedule& recSchedule, const QuantLib::ext::shared_ptr<IborIndex>& recIndex,
124 Spread recSpread, bool includeSpread, bool spreadOnRec, QuantExt::SubPeriodsCoupon1::Type type,
125 const bool telescopicValueDates)
126 : Swap(2), nominals_(nominals), paySchedule_(paySchedule), payIndex_(payIndex),
127 paySpread_(paySpread), recSchedule_(recSchedule), recIndex_(recIndex), recSpread_(recSpread),
128 includeSpread_(includeSpread), spreadOnRec_(spreadOnRec), type_(type), telescopicValueDates_(telescopicValueDates) {
129
130 // Create legs
132}
133
135
136 // Checks
137 QL_REQUIRE(paySchedule_.tenor() >= payIndex_->tenor(), "Expected paySchedule tenor to exceed/equal payIndex tenor");
138 QL_REQUIRE(recSchedule_.tenor() >= recIndex_->tenor(), "Expected recSchedule tenor to exceed/equal recIndex tenor");
139
140 noSubPeriod_ = true;
141
142 // pay leg
143 QuantLib::ext::shared_ptr<OvernightIndex> payIndexON = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(payIndex_);
144 Leg payLeg;
145
146 if (payIndexON) {
147 payLeg = OvernightLeg(paySchedule_, payIndexON)
151 } else {
152 if (paySchedule_.tenor() == payIndex_->tenor()) {
153 payLeg = IborLeg(paySchedule_, payIndex_)
154 .withNotionals(nominals_)
155 .withSpreads(paySpread_)
156 .withPaymentAdjustment(payIndex_->businessDayConvention())
157 .withPaymentDayCounter(payIndex_->dayCounter())
158 .withPaymentCalendar(payIndexCalendar_);
159 } else {
160 if (!spreadOnRec_) {
161 //if spread leg and no overnight, the leg may be a subperiod leg
165 .withPaymentAdjustment(payIndex_->businessDayConvention())
166 .withPaymentDayCounter(payIndex_->dayCounter())
169 .withType(type_);
170 noSubPeriod_ = false;
171 } else {
172 QL_FAIL(
173 "Pay Leg could not be created. Neither overnight nor schedule index tenor match nor spread leg.");
174 } // spreadOnRec
175 } // paySchedule_.tenor() == payIndex_->tenor()
176 } // payIndexON
177
178 // receive leg
179 QuantLib::ext::shared_ptr<OvernightIndex> recIndexON = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(recIndex_);
180 Leg recLeg;
181
182 if (recIndexON) {
183 recLeg = OvernightLeg(recSchedule_, recIndexON)
187 } else {
188 if (recSchedule_.tenor() == recIndex_->tenor()) {
189 recLeg = IborLeg(recSchedule_, recIndex_)
190 .withNotionals(nominals_)
191 .withSpreads(recSpread_)
192 .withPaymentAdjustment(recIndex_->businessDayConvention())
193 .withPaymentDayCounter(recIndex_->dayCounter())
194 .withPaymentCalendar(recIndexCalendar_);
195 } else {
196 if (spreadOnRec_) {
197 //if spread leg and no overnight, the leg may be a subperiod leg
201 .withPaymentAdjustment(recIndex_->businessDayConvention())
202 .withPaymentDayCounter(recIndex_->dayCounter())
205 .withType(type_);
206 noSubPeriod_ = false;
207 } else {
208 QL_FAIL(
209 "Rec Leg could not be created. Neither overnight nor schedule index tenor match nor spread leg.");
210 } //!spreadOnRec
211 } //recSchedule_.tenor() == recIndex_->tenor()
212 } //recIndexON
213
214 //Allocate leg idx : spread leg = 0
215 idxPay_ = 0;
216 idxRec_ = 1;
217 if(spreadOnRec_){
218 idxPay_ = 1;
219 idxRec_ = 0;
220 }
221
222 payer_[idxPay_] = -1.0;
223 payer_[idxRec_] = +1.0;
224 legs_[idxPay_] = payLeg;
225 legs_[idxRec_] = recLeg;
226
227 // Register this instrument with its coupons
228 Leg::const_iterator it;
229 for (it = legs_[0].begin(); it != legs_[0].end(); ++it)
230 registerWith(*it);
231 for (it = legs_[1].begin(); it != legs_[1].end(); ++it)
232 registerWith(*it);
233
234
235}
236
238 calculate();
239 QL_REQUIRE(legBPS_[idxPay_] != Null<Real>(), "Pay leg BPS not available");
240 return legBPS_[idxPay_];
241}
242
244 calculate();
245 QL_REQUIRE(legNPV_[idxPay_] != Null<Real>(), "Pay leg NPV not available");
246 return legNPV_[idxPay_];
247}
248
250 calculate();
251 QL_REQUIRE(fairSpread_[idxPay_] != Null<Spread>(), "Pay leg fair spread not available");
252 return fairSpread_[idxPay_];
253}
254
256 calculate();
257 QL_REQUIRE(legBPS_[idxRec_] != Null<Real>(), "Receive leg BPS not available");
258 return legBPS_[idxRec_];
259}
260
262 calculate();
263 QL_REQUIRE(legNPV_[idxRec_] != Null<Real>(), "Receive leg NPV not available");
264 return legNPV_[idxRec_];
265}
266
268 calculate();
269 QL_REQUIRE(fairSpread_[idxRec_] != Null<Spread>(), "Receive leg fair spread not available");
270 return fairSpread_[idxRec_];
271}
272
274 Swap::setupExpired();
275 fairSpread_ = { Null<Spread>(),Null<Spread>() };
276}
277
278void TenorBasisSwap::fetchResults(const PricingEngine::results* r) const {
279
280 static const Spread basisPoint = 1.0e-4;
281 Swap::fetchResults(r);
282
283 const TenorBasisSwap::results* results = dynamic_cast<const TenorBasisSwap::results*>(r);
284
285 if (results) {
287 } else {
288 fairSpread_ = { Null<Spread>(),Null<Spread>() };
289 }
290
291 //non spread leg (idx 1) should be fine - no averaging or compounding
292 if (fairSpread_[1] == Null<Spread>()) {
293 if (legBPS_[1] != Null<Real>()) {
294 double s = spreadOnRec_ ? recSpread_ : paySpread_;
295 fairSpread_[1] = s - NPV_ / (legBPS_[1] / basisPoint);
296 }
297 }
298
299 /* spread leg (idx 0) fair spread calculation ok if no averaging/compounding OR
300 if there is averaging/compounding and the spread is added after */
301 if (fairSpread_[0] == Null<Spread>()) {
303 if (legBPS_[0] != Null<Real>()) {
304 double s = spreadOnRec_ ? paySpread_ : recSpread_;
305 fairSpread_[0] = s - NPV_ / (legBPS_[0] / basisPoint);
306 }
307 } else {
308 // Need the discount curve
309 Handle<YieldTermStructure> discountCurve;
310 QuantLib::ext::shared_ptr<DiscountingSwapEngine> engine =
311 QuantLib::ext::dynamic_pointer_cast<DiscountingSwapEngine>(engine_);
312 if (engine) {
313 discountCurve = engine->discountCurve();
314 // Calculate a guess
315 Spread guess = 0.0;
316 if (legBPS_[0] != Null<Real>()) {
317 double s = spreadOnRec_ ? paySpread_ : recSpread_;
318 guess = s - NPV_ / (legBPS_[0] / basisPoint);
319 }
320 // Attempt to solve for fair spread
321 Spread step = 1e-4;
322 Real accuracy = 1e-8;
323 FairSpreadHelper f(*this, discountCurve, legNPV_[1]);
324 Brent solver;
325 fairSpread_[0] = solver.solve(f, accuracy, guess, step);
326 }
327 }
328 }
329}
330
332 Swap::results::reset();
333 fairSpread = { Null<Spread>(),Null<Spread>() };
334}
335} // namespace QuantExt
QuantLib::ext::shared_ptr< PricingEngine > engine_
Definition: cdsoption.cpp:78
const Instrument::results * results_
Definition: cdsoption.cpp:81
helper class building a sequence of overnight coupons
OvernightLeg & withTelescopicValueDates(bool telescopicValueDates)
OvernightLeg & withNotionals(Real notional)
OvernightLeg & withSpreads(Spread spread)
helper class building a sequence of sub-period coupons
SubPeriodsLeg1 & withPaymentDayCounter(const DayCounter &dayCounter)
SubPeriodsLeg1 & withType(SubPeriodsCoupon1::Type type)
SubPeriodsLeg1 & withSpread(Spread spread)
SubPeriodsLeg1 & includeSpread(bool includeSpread)
SubPeriodsLeg1 & withPaymentCalendar(const Calendar &calendar)
SubPeriodsLeg1 & withNotionals(const std::vector< Real > &notionals)
SubPeriodsLeg1 & withPaymentAdjustment(BusinessDayConvention convention)
TenorBasisSwap(const Date &effectiveDate, Real nominal, const Period &swapTenor, const QuantLib::ext::shared_ptr< IborIndex > &payIndex, Spread paySpread, const Period &payFrequency, const QuantLib::ext::shared_ptr< IborIndex > &recIndex, Spread recSpread, const Period &recFrequency, DateGeneration::Rule rule=DateGeneration::Backward, bool includeSpread=false, bool spreadOnRec=true, QuantExt::SubPeriodsCoupon1::Type type=QuantExt::SubPeriodsCoupon1::Compounding, const bool telescopicValueDates=false)
Constructor with conventions deduced from the indices.
QuantLib::ext::shared_ptr< IborIndex > payIndex_
QuantLib::ext::shared_ptr< IborIndex > recIndex_
const Leg & payLeg() const
std::vector< Spread > fairSpread_
Spread fairRecLegSpread() const
const Leg & recLeg() const
void setupExpired() const override
void fetchResults(const PricingEngine::results *) const override
QuantExt::SubPeriodsCoupon1::Type type_
std::vector< Real > nominals_
Real nonSpreadLegNPV_
Swap::arguments * arguments_
Leg spreadLeg_
Single currency tenor basis swap instrument.