Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
cpicoupon.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
19/*! \file cpicoupon.cpp
20 \brief CPI leg builder extending QuantLib's to handle caps and floors
21*/
22
23#include <ql/cashflows/cashflowvectors.hpp>
24#include <ql/time/daycounters/thirty360.hpp>
26
27#include <ql/cashflows/cpicouponpricer.hpp>
30//#include <iostream>
31
32namespace QuantExt {
33
34Rate CPICoupon::rate() const {
35 Rate r = QuantLib::CPICoupon::rate() ;
37 Rate adjusted_r = r / fixedRate_;
38 r = (adjusted_r - 1) * fixedRate_;
39 }
40 return r;
41 }
42
43void CappedFlooredCPICoupon::setCommon(Rate cap, Rate floor) {
44
45 isCapped_ = false;
46 isFloored_ = false;
47
48 if (fixedRate_ > 0) {
49 if (cap != Null<Rate>()) {
50 isCapped_ = true;
51 cap_ = cap;
52 }
53 if (floor != Null<Rate>()) {
54 floor_ = floor;
55 isFloored_ = true;
56 }
57 } else {
58 if (cap != Null<Rate>()) {
59 floor_ = cap;
60 isFloored_ = true;
61 }
62 if (floor != Null<Rate>()) {
63 isCapped_ = true;
64 cap_ = floor;
65 }
66 }
67 if (isCapped_ && isFloored_) {
68 QL_REQUIRE(cap >= floor, "cap level (" << cap << ") less than floor level (" << floor << ")");
69 }
70}
71
72CappedFlooredCPICoupon::CappedFlooredCPICoupon(const ext::shared_ptr<CPICoupon>& underlying, Date startDate, Rate cap,
73 Rate floor)
74 : CPICoupon(underlying->baseCPI(), underlying->baseDate(), underlying->date(), underlying->nominal(), underlying->accrualStartDate(),
75 underlying->accrualEndDate(), underlying->cpiIndex(),
76 underlying->observationLag(), underlying->observationInterpolation(), underlying->dayCounter(),
77 underlying->fixedRate(), underlying->referencePeriodStart(),
78 underlying->referencePeriodEnd(), underlying->exCouponDate(), underlying->subtractInflationNotional()),
79 underlying_(underlying), startDate_(startDate), isFloored_(false), isCapped_(false) {
80
81 setCommon(cap, floor);
82 registerWith(underlying);
83
84 Calendar cal = underlying->cpiIndex()->fixingCalendar(); // not used by the CPICapFloor engine
85 BusinessDayConvention conv = Unadjusted; // not used by the CPICapFloor engine
86
87 if (isCapped_) {
88 Rate effectiveCap = cap_;
89 cpiCap_ = QuantLib::ext::make_shared<CPICapFloor>(
90 Option::Call, underlying_->nominal(), startDate_, underlying_->baseCPI(), underlying_->date(), cal, conv,
91 cal, conv, effectiveCap, underlying_->cpiIndex(), underlying_->observationLag(),
92 underlying_->observationInterpolation());
93 // std::cout << "Capped/Floored CPI Coupon" << std::endl
94 // << " nominal = " << underlying_->nominal() << std::endl
95 // << " paymentDate = " << QuantLib::io::iso_date(underlying_->date()) << std::endl
96 // << " startDate = " << QuantLib::io::iso_date(startDate_) << std::endl
97 // << " baseCPI = " << underlying_->baseCPI() << std::endl
98 // << " lag = " << underlying_->observationLag() << std::endl
99 // << " interpolation = " << underlying_->observationInterpolation() << std::endl;
100 }
101 if (isFloored_) {
102 Rate effectiveFloor = floor_;
103 cpiFloor_ = QuantLib::ext::make_shared<CPICapFloor>(
104 Option::Put, underlying_->nominal(), startDate_, underlying_->baseCPI(), underlying_->date(), cal, conv,
105 cal, conv, effectiveFloor, underlying_->cpiIndex(),
106 underlying_->observationLag(),
107 underlying_->observationInterpolation());
108 }
109}
110
112 // rate = fixedRate * capped/floored index
113 QuantLib::ext::shared_ptr<CappedFlooredCPICouponPricer> blackPricer =
114 QuantLib::ext::dynamic_pointer_cast<CappedFlooredCPICouponPricer>(pricer_);
115 QL_REQUIRE(blackPricer, "BlackCPICouponPricer or BachelierCPICouponPricer expected");
116 Real capValue = 0.0, floorValue = 0.0;
117 if (isCapped_) {
118 cpiCap_->setPricingEngine(blackPricer->engine());
119 capValue = cpiCap_->NPV();
120 }
121 if (isFloored_) {
122 cpiFloor_->setPricingEngine(blackPricer->engine());
123 floorValue = cpiFloor_->NPV();
124 }
125 Real discount = blackPricer->yieldCurve()->discount(underlying_->date());
126 Real nominal = underlying_->nominal();
127 // normalize, multiplication with nominal, year fraction, discount follows
128 Real capAmount = capValue / (nominal * discount);
129 Real floorAmount = floorValue / (nominal * discount);
130
131 Rate swapletRate = underlying_->rate();
132 // apply gearing
133 Rate floorletRate = floorAmount * underlying_->fixedRate();
134 Rate capletRate = capAmount * underlying_->fixedRate();
135 // long floor, short cap
136 Rate totalRate = swapletRate + floorletRate - capletRate;
137
138 return totalRate;
139}
140
141void CappedFlooredCPICoupon::update() { notifyObservers(); }
142
143void CappedFlooredCPICoupon::accept(AcyclicVisitor& v) {
144 Visitor<CappedFlooredCPICoupon>* v1 = dynamic_cast<Visitor<CappedFlooredCPICoupon>*>(&v);
145 if (v1 != 0)
146 v1->visit(*this);
147 else
148 CPICoupon::accept(v);
149}
150
151CappedFlooredCPICashFlow::CappedFlooredCPICashFlow(const ext::shared_ptr<CPICashFlow>& underlying, Date startDate,
152 Period observationLag, Rate cap, Rate floor)
153 : CPICashFlow(underlying->notional(), underlying->cpiIndex(),
154 startDate - observationLag, underlying->baseFixing(), underlying->observationDate(),
155 underlying->observationLag(), underlying->interpolation(),
156 underlying->date(),
157 underlying->growthOnly()),
158 underlying_(underlying), startDate_(startDate), observationLag_(observationLag), isFloored_(false),
159 isCapped_(false) {
160
161 setCommon(cap, floor);
162 registerWith(underlying);
163
164 auto index = QuantLib::ext::dynamic_pointer_cast<ZeroInflationIndex>(underlying->index());
165 Calendar cal = index->fixingCalendar(); // not used by the CPICapFloor engine
166 BusinessDayConvention conv = Unadjusted; // not used by the CPICapFloor engine
167
168 if (isCapped_) {
169 cpiCap_ = QuantLib::ext::make_shared<CPICapFloor>(Option::Call, underlying_->notional(), startDate_,
170 underlying_->baseFixing(), underlying_->date(), cal, conv, cal, conv,
171 cap_, index, observationLag_, underlying_->interpolation());
172 }
173 if (isFloored_) {
174 cpiFloor_ = QuantLib::ext::make_shared<CPICapFloor>(Option::Put, underlying_->notional(), startDate_,
175 underlying_->baseFixing(), underlying_->date(), cal, conv, cal,
176 conv, floor_, index, observationLag_, underlying_->interpolation());
177 }
178}
179
180void CappedFlooredCPICashFlow::setCommon(Rate cap, Rate floor) {
181 isCapped_ = false;
182 isFloored_ = false;
183
184 if (cap != Null<Rate>()) {
185 isCapped_ = true;
186 cap_ = cap;
187 }
188 if (floor != Null<Rate>()) {
189 floor_ = floor;
190 isFloored_ = true;
191 }
192 if (isCapped_ && isFloored_) {
193 QL_REQUIRE(cap >= floor, "cap level (" << cap << ") less than floor level (" << floor << ")");
194 }
195}
196
197void CappedFlooredCPICashFlow::setPricer(const ext::shared_ptr<InflationCashFlowPricer>& pricer) {
198 if (pricer_)
199 unregisterWith(pricer_);
200 pricer_ = pricer;
201 if (pricer_)
202 registerWith(pricer_);
203 update();
204}
205
207 QL_REQUIRE(pricer_, "pricer not set for capped/floored CPI cashflow");
208 Real capValue = 0.0, floorValue = 0.0;
209 if (isCapped_) {
210 cpiCap_->setPricingEngine(pricer_->engine());
211 capValue = cpiCap_->NPV();
212 }
213 if (isFloored_) {
214 cpiFloor_->setPricingEngine(pricer_->engine());
215 floorValue = cpiFloor_->NPV();
216 }
217 Real discount = pricer_->yieldCurve()->discount(underlying_->date());
218 Real capAmount = capValue / discount;
219 Real floorAmount = floorValue / discount;
220 Real underlyingAmount = underlying_->amount();
221 Real totalAmount = underlyingAmount - capAmount + floorAmount;
222 return totalAmount;
223}
224
225CPILeg::CPILeg(const Schedule& schedule, const ext::shared_ptr<ZeroInflationIndex>& index,
226 const Handle<YieldTermStructure>& rateCurve, const Real baseCPI, const Period& observationLag)
227 : schedule_(schedule), index_(index), rateCurve_(rateCurve), baseCPI_(baseCPI), observationLag_(observationLag),
228 paymentDayCounter_(Thirty360(Thirty360::BondBasis)), paymentAdjustment_(ModifiedFollowing), paymentCalendar_(schedule.calendar()),
229 fixingDays_(std::vector<Natural>(1, 0)), observationInterpolation_(CPI::AsIndex), subtractInflationNominal_(true),
230 finalFlowCap_(Null<Real>()), finalFlowFloor_(Null<Real>()), subtractInflationNominalAllCoupons_(false),
231 startDate_(schedule_.dates().front()) {
232 QL_REQUIRE(schedule_.dates().size() > 0, "empty schedule passed to CPILeg");
233}
234
235CPILeg& CPILeg::withObservationInterpolation(CPI::InterpolationType interp) {
237 return *this;
238}
239
241 fixedRates_ = std::vector<Real>(1, fixedRate);
242 return *this;
243}
244
245CPILeg& CPILeg::withFixedRates(const std::vector<Real>& fixedRates) {
246 fixedRates_ = fixedRates;
247 return *this;
248}
249
251 notionals_ = std::vector<Real>(1, notional);
252 return *this;
253}
254
255CPILeg& CPILeg::withNotionals(const std::vector<Real>& notionals) {
256 notionals_ = notionals;
257 return *this;
258}
259
261 subtractInflationNominal_ = growthOnly;
262 return *this;
263}
264
265CPILeg& CPILeg::withPaymentDayCounter(const DayCounter& dayCounter) {
266 paymentDayCounter_ = dayCounter;
267 return *this;
268}
269
270CPILeg& CPILeg::withPaymentAdjustment(BusinessDayConvention convention) {
271 paymentAdjustment_ = convention;
272 return *this;
273}
274
275CPILeg& CPILeg::withPaymentCalendar(const Calendar& cal) {
276 paymentCalendar_ = cal;
277 return *this;
278}
279
281 paymentLag_ = lag;
282 return *this;
283}
284
285CPILeg& CPILeg::withFixingDays(Natural fixingDays) {
286 fixingDays_ = std::vector<Natural>(1, fixingDays);
287 return *this;
288}
289
290CPILeg& CPILeg::withFixingDays(const std::vector<Natural>& fixingDays) {
291 fixingDays_ = fixingDays;
292 return *this;
293}
294
296 caps_ = std::vector<Rate>(1, cap);
297 return *this;
298}
299
300CPILeg& CPILeg::withCaps(const std::vector<Rate>& caps) {
301 caps_ = caps;
302 return *this;
303}
304
306 floors_ = std::vector<Rate>(1, floor);
307 return *this;
308}
309
311 finalFlowCap_ = cap;
312 return *this;
313}
314
316 finalFlowFloor_ = floor;
317 return *this;
318}
319
320CPILeg& CPILeg::withFloors(const std::vector<Rate>& floors) {
321 floors_ = floors;
322 return *this;
323}
324
325CPILeg& CPILeg::withStartDate(const Date& startDate) {
326 startDate_ = startDate;
327 return *this;
328}
329
330CPILeg& CPILeg::withObservationLag(const Period& observationLag) {
331 observationLag_ = observationLag;
332 return *this;
333}
334
335CPILeg& CPILeg::withExCouponPeriod(const Period& period, const Calendar& cal, BusinessDayConvention convention,
336 bool endOfMonth) {
337 exCouponPeriod_ = period;
338 exCouponCalendar_ = cal;
339 exCouponAdjustment_ = convention;
340 exCouponEndOfMonth_ = endOfMonth;
341 return *this;
342}
343
344CPILeg& CPILeg::withSubtractInflationNominalAllCoupons(bool subtractInflationNominalAllCoupons) {
345 subtractInflationNominalAllCoupons_ = subtractInflationNominalAllCoupons;
346 return *this;
347}
348
349CPILeg::operator Leg() const {
350
351 QL_REQUIRE(!notionals_.empty(), "no notional given");
352 Size n = schedule_.size() - 1;
353 Leg leg;
354 leg.reserve(n + 1); // +1 for notional, we always have some sort ...
355 Date baseDate = baseDate_ == Date() ? startDate_ - observationLag_ : baseDate_;
356 if (n > 0) {
357 QL_REQUIRE(!fixedRates_.empty(), "no fixedRates given");
358
359 Date refStart, start, refEnd, end;
360
361 for (Size i = 0; i < n; ++i) {
362 refStart = start = schedule_.date(i);
363 refEnd = end = schedule_.date(i + 1);
364 Date paymentDate = paymentCalendar_.advance(end, paymentLag_, Days, paymentAdjustment_);
365
366 Date exCouponDate;
367 if (exCouponPeriod_ != Period()) {
368 exCouponDate =
369 exCouponCalendar_.advance(paymentDate, -exCouponPeriod_, exCouponAdjustment_, exCouponEndOfMonth_);
370 }
371
372 if (i == 0 && schedule_.hasIsRegular() && !schedule_.isRegular(i + 1)) {
373 BusinessDayConvention bdc = schedule_.businessDayConvention();
374 refStart = schedule_.calendar().adjust(end - schedule_.tenor(), bdc);
375 }
376 if (i == n - 1 && schedule_.hasIsRegular() && !schedule_.isRegular(i + 1)) {
377 BusinessDayConvention bdc = schedule_.businessDayConvention();
378 refEnd = schedule_.calendar().adjust(start + schedule_.tenor(), bdc);
379 }
380
381 auto coup = ext::make_shared<CPICoupon>(
382 baseCPI_, // all have same base for ratio
383 baseDate, paymentDate, detail::get(notionals_, i, 0.0), start, end, index_, observationLag_,
384 observationInterpolation_, paymentDayCounter_, detail::get(fixedRates_, i, 0.0),
385 refStart, refEnd, exCouponDate, subtractInflationNominalAllCoupons_);
386
387 // set a pricer for the underlying coupon straight away because it only provides computation - not data
388 auto pricer = ext::make_shared<CPICouponPricer>(Handle<YieldTermStructure>(rateCurve_));
389 coup->setPricer(pricer);
390
391 if (detail::noOption(caps_, floors_, i)) { // just swaplet
392 leg.push_back(coup);
393 } else { // cap/floorlet
394 auto cfCoup = ext::make_shared<CappedFlooredCPICoupon>(
395 coup, startDate_, detail::get(caps_, i, Null<Rate>()), detail::get(floors_, i, Null<Rate>()));
396 // in this case we need to set the "outer" pricer later that handles cap and floor
397 leg.push_back(cfCoup);
398 }
399 }
400 }
401
402 // in CPI legs you always have a notional flow of some sort
403
404 // Previous implementations didn't differentiate the observation and payment dates
405 Date observationDate = paymentCalendar_.adjust(schedule_.date(n), paymentAdjustment_);
406 Date paymentDate = paymentCalendar_.advance(schedule_.date(n), paymentLag_, Days, paymentAdjustment_);
407
408 ext::shared_ptr<CPICashFlow> xnl = ext::make_shared<CPICashFlow>(
409 detail::get(notionals_, n, 0.0), index_, baseDate, baseCPI_, observationDate, observationLag_,
410 observationInterpolation_, paymentDate, subtractInflationNominal_);
411
412 if (finalFlowCap_ == Null<Real>() && finalFlowFloor_ == Null<Real>()) {
413 leg.push_back(xnl);
414 } else {
415 ext::shared_ptr<CappedFlooredCPICashFlow> cfxnl = ext::make_shared<CappedFlooredCPICashFlow>(
416 xnl, startDate_, observationLag_, finalFlowCap_, finalFlowFloor_);
417 leg.push_back(cfxnl);
418 }
419
420 return leg;
421}
422
423} // namespace QuantExt
virtual Rate rate() const override
Definition: cpicoupon.cpp:34
bool subtractInflationNominal_
Definition: cpicoupon.hpp:67
Helper class building a sequence of capped/floored CPI coupons.
Definition: cpicoupon.hpp:138
CPILeg & withNotionals(Real notional)
Definition: cpicoupon.cpp:250
Schedule schedule_
Definition: cpicoupon.hpp:168
BusinessDayConvention paymentAdjustment_
Definition: cpicoupon.hpp:176
std::vector< Rate > caps_
Definition: cpicoupon.hpp:182
CPILeg & withPaymentAdjustment(BusinessDayConvention)
Definition: cpicoupon.cpp:270
BusinessDayConvention exCouponAdjustment_
Definition: cpicoupon.hpp:186
CPILeg & withFinalFlowCap(Rate cap)
Definition: cpicoupon.cpp:310
CPILeg & withSubtractInflationNominal(bool)
Definition: cpicoupon.cpp:260
Period observationLag_
Definition: cpicoupon.hpp:172
Calendar paymentCalendar_
Definition: cpicoupon.hpp:177
CPILeg & withFixingDays(Natural fixingDays)
Definition: cpicoupon.cpp:285
CPILeg & withObservationLag(const Period &observationLag)
Definition: cpicoupon.cpp:330
Natural paymentLag_
Definition: cpicoupon.hpp:178
CPILeg & withFixedRates(Real fixedRate)
Definition: cpicoupon.cpp:240
CPILeg & withFinalFlowFloor(Rate floor)
Definition: cpicoupon.cpp:315
CPILeg & withExCouponPeriod(const Period &, const Calendar &, BusinessDayConvention, bool endOfMonth=false)
Definition: cpicoupon.cpp:335
CPILeg(const Schedule &schedule, const ext::shared_ptr< ZeroInflationIndex > &index, const Handle< YieldTermStructure > &rateCurve, const Real baseCPI, const Period &observationLag)
Definition: cpicoupon.cpp:225
std::vector< Real > notionals_
Definition: cpicoupon.hpp:173
CPILeg & withCaps(Rate cap)
Definition: cpicoupon.cpp:295
CPILeg & withPaymentDayCounter(const DayCounter &)
Definition: cpicoupon.cpp:265
CPILeg & withStartDate(const Date &startDate)
Definition: cpicoupon.cpp:325
CPILeg & withPaymentCalendar(const Calendar &)
Definition: cpicoupon.cpp:275
CPILeg & withFloors(Rate floor)
Definition: cpicoupon.cpp:305
double finalFlowCap_
Definition: cpicoupon.hpp:183
bool exCouponEndOfMonth_
Definition: cpicoupon.hpp:187
std::vector< Real > fixedRates_
Definition: cpicoupon.hpp:174
std::vector< Natural > fixingDays_
Definition: cpicoupon.hpp:179
CPI::InterpolationType observationInterpolation_
Definition: cpicoupon.hpp:180
Period exCouponPeriod_
Definition: cpicoupon.hpp:184
bool subtractInflationNominalAllCoupons_
Definition: cpicoupon.hpp:188
Calendar exCouponCalendar_
Definition: cpicoupon.hpp:185
CPILeg & withSubtractInflationNominalAllCoupons(bool subtractInflationNominalAllCoupons)
Definition: cpicoupon.cpp:344
double finalFlowFloor_
Definition: cpicoupon.hpp:183
std::vector< Rate > floors_
Definition: cpicoupon.hpp:182
CPILeg & withPaymentLag(Natural lag)
Definition: cpicoupon.cpp:280
bool subtractInflationNominal_
Definition: cpicoupon.hpp:181
CPILeg & withObservationInterpolation(CPI::InterpolationType)
Definition: cpicoupon.cpp:235
DayCounter paymentDayCounter_
Definition: cpicoupon.hpp:175
ext::shared_ptr< CPICashFlow > underlying_
Definition: cpicoupon.hpp:88
ext::shared_ptr< CPICashFlow > underlying() const
Definition: cpicoupon.hpp:83
void setPricer(const ext::shared_ptr< InflationCashFlowPricer > &pricer)
Definition: cpicoupon.cpp:197
ext::shared_ptr< CPICapFloor > cpiCap_
Definition: cpicoupon.hpp:89
void setCommon(Rate cap, Rate floor)
Definition: cpicoupon.cpp:180
CappedFlooredCPICashFlow(const ext::shared_ptr< CPICashFlow > &underlying, Date startDate=Date(), Period observationLag=0 *Days, Rate cap=Null< Rate >(), Rate floor=Null< Rate >())
Definition: cpicoupon.cpp:151
ext::shared_ptr< InflationCashFlowPricer > pricer_
Definition: cpicoupon.hpp:93
virtual Real amount() const override
Definition: cpicoupon.cpp:206
ext::shared_ptr< CPICapFloor > cpiFloor_
Definition: cpicoupon.hpp:89
CappedFlooredCPICoupon(const ext::shared_ptr< CPICoupon > &underlying, Date startDate=Date(), Rate cap=Null< Rate >(), Rate floor=Null< Rate >())
Definition: cpicoupon.cpp:72
ext::shared_ptr< CPICapFloor > cpiCap_
Definition: cpicoupon.hpp:124
virtual void setCommon(Rate cap, Rate floor)
Definition: cpicoupon.cpp:43
virtual Rate rate() const override
Definition: cpicoupon.cpp:111
ext::shared_ptr< CPICoupon > underlying() const
Definition: cpicoupon.hpp:105
ext::shared_ptr< CPICoupon > underlying_
Definition: cpicoupon.hpp:123
virtual void accept(AcyclicVisitor &v) override
Definition: cpicoupon.cpp:143
ext::shared_ptr< CPICapFloor > cpiFloor_
Definition: cpicoupon.hpp:124
const QuantLib::ext::shared_ptr< FloatingRateCouponPricer > pricer_
CPI cap/floor engine using the Black pricing formula and interpreting the volatility data as lognorma...
CPI leg builder extending QuantLib's to handle caps and floors.
CPI cash flow and coupon pricers that handle caps/floors using a CpiCapFloorEngine.