Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
averageonindexedcoupon.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
21
22#include <ql/cashflows/cashflowvectors.hpp>
23#include <ql/cashflows/couponpricer.hpp>
24#include <ql/cashflows/fixedratecoupon.hpp>
25#include <ql/termstructures/yieldtermstructure.hpp>
26#include <ql/time/calendars/weekendsonly.hpp>
27#include <ql/utilities/vectors.hpp>
28
29using namespace QuantLib;
30
31namespace QuantExt {
32
33// average on indexed coupon implementation
34
35AverageONIndexedCoupon::AverageONIndexedCoupon(const Date& paymentDate, Real nominal, const Date& startDate,
36 const Date& endDate,
37 const QuantLib::ext::shared_ptr<OvernightIndex>& overnightIndex, Real gearing,
38 Spread spread, Natural rateCutoff, const DayCounter& dayCounter,
39 const Period& lookback, const Size fixingDays,
40 const Date& rateComputationStartDate, const Date& rateComputationEndDate,
41 const bool telescopicValueDates)
42 : FloatingRateCoupon(paymentDate, nominal, startDate, endDate, fixingDays, overnightIndex, gearing, spread, Date(),
43 Date(), dayCounter, false),
44 overnightIndex_(overnightIndex), rateCutoff_(rateCutoff), lookback_(lookback),
45 rateComputationStartDate_(rateComputationStartDate), rateComputationEndDate_(rateComputationEndDate) {
46
47 Date valueStart = rateComputationStartDate_ == Null<Date>() ? startDate : rateComputationStartDate_;
48 Date valueEnd = rateComputationEndDate_ == Null<Date>() ? endDate : rateComputationEndDate_;
49 if (lookback != 0 * Days) {
50 BusinessDayConvention bdc = lookback.length() > 0 ? Preceding : Following;
51 valueStart = overnightIndex->fixingCalendar().advance(valueStart, -lookback, bdc);
52 valueEnd = overnightIndex->fixingCalendar().advance(valueEnd, -lookback, bdc);
53 }
54
55 // Populate the value dates.
56 Date tmpEndDate = valueEnd;
57 if(telescopicValueDates) {
58 // same optimization as in OvernightIndexedCoupon
59 Date evalDate = Settings::instance().evaluationDate();
60 tmpEndDate = overnightIndex->fixingCalendar().advance(std::max(valueStart, evalDate), 7, Days, Following);
61 tmpEndDate = std::min(tmpEndDate, valueEnd);
62 }
63
64 Schedule sch = MakeSchedule()
65 .from(valueStart)
66 .to(tmpEndDate)
67 .withTenor(1 * Days)
68 .withCalendar(overnightIndex->fixingCalendar())
69 .withConvention(overnightIndex->businessDayConvention())
70 .backwards();
71 valueDates_ = sch.dates();
72
73 if (telescopicValueDates) {
74 // build optimised value dates schedule: back stub
75 // contains at least two dates and enough periods to cover rate cutoff
76 Date tmp2 = overnightIndex->fixingCalendar().adjust(valueEnd, overnightIndex->businessDayConvention());
77 Date tmp1 = overnightIndex->fixingCalendar().advance(tmp2, -std::max<Size>(rateCutoff_, 1), Days, Preceding);
78 while (tmp1 <= tmp2) {
79 if (tmp1 > valueDates_.back())
80 valueDates_.push_back(tmp1);
81 tmp1 = overnightIndex->fixingCalendar().advance(tmp1, 1, Days, Following);
82 }
83 }
84
85 QL_ENSURE(valueDates_.size() >= 2 + rateCutoff_, "degenerate schedule");
86
87 // the first and last value date should be the unadjusted input value dates
88 if (valueDates_.front() != valueStart)
89 valueDates_.front() = valueStart;
90 if (valueDates_.back() != valueEnd)
91 valueDates_.back() = valueEnd;
92
93 numPeriods_ = valueDates_.size() - 1;
94
95 QL_REQUIRE(valueDates_[0] != valueDates_[1],
96 "internal error: first two value dates of on coupon are equal: " << valueDates_[0]);
98 "internal error: last two value dates of on coupon are equal: " << valueDates_[numPeriods_]);
99
100 // Populate the fixing dates.
102 for (Size i = 0; i < numPeriods_; ++i)
103 fixingDates_[i] = overnightIndex->fixingCalendar().advance(
104 valueDates_[i], -static_cast<Integer>(FloatingRateCoupon::fixingDays()), Days, Preceding);
105
106 // Populate the accrual periods.
107 dt_.resize(numPeriods_);
108 for (Size i = 0; i < numPeriods_; ++i)
109 dt_[i] = dayCounter.yearFraction(valueDates_[i], valueDates_[i + 1]);
110
111 // check that rate cutoff is < number of fixing dates
112 QL_REQUIRE(rateCutoff_ < numPeriods_, "rate cutoff (" << rateCutoff_
113 << ") must be less than number of fixings in period ("
114 << numPeriods_ << ")");
115}
116
117const std::vector<Rate>& AverageONIndexedCoupon::indexFixings() const {
118
119 fixings_.resize(numPeriods_);
120 Size i;
121
122 for (i = 0; i < numPeriods_ - rateCutoff_; ++i) {
123 fixings_[i] = index_->fixing(fixingDates_[i]);
124 }
125
126 Rate cutoffFixing = fixings_[i - 1];
127 while (i < numPeriods_) {
128 fixings_[i] = cutoffFixing;
129 i++;
130 }
131
132 return fixings_;
133}
134
136
137void AverageONIndexedCoupon::accept(AcyclicVisitor& v) {
138 Visitor<AverageONIndexedCoupon>* v1 = dynamic_cast<Visitor<AverageONIndexedCoupon>*>(&v);
139 if (v1 != 0) {
140 v1->visit(*this);
141 } else {
142 FloatingRateCoupon::accept(v);
143 }
144}
145
146// capped floored average on coupon implementation
147
149 LazyObject::alwaysForwardNotifications();
150 underlying_->alwaysForwardNotifications();
151}
152
154 update();
155 underlying_->deepUpdate();
156}
157
159 QL_REQUIRE(underlying_->pricer(), "pricer not set");
160 Rate swapletRate = nakedOption_ ? 0.0 : underlying_->rate();
161 if (floor_ != Null<Real>() || cap_ != Null<Real>())
162 pricer()->initialize(*this);
163 Rate floorletRate = 0.;
164 if (floor_ != Null<Real>())
165 floorletRate = pricer()->floorletRate(effectiveFloor());
166 Rate capletRate = 0.;
167 if (cap_ != Null<Real>())
168 capletRate = (nakedOption_ && floor_ == Null<Real>() ? -1.0 : 1.0) * pricer()->capletRate(effectiveCap());
169 rate_ = swapletRate + floorletRate - capletRate;
170 auto p = QuantLib::ext::dynamic_pointer_cast<CapFlooredAverageONIndexedCouponPricer>(pricer());
171 QL_REQUIRE(p, "CapFlooredAverageONIndexedCoupon::performCalculations(): internal error, could not cast to "
172 "CapFlooredAverageONIndexedCouponPricer");
173 effectiveCapletVolatility_ = p->effectiveCapletVolatility();
174 effectiveFloorletVolatility_ = p->effectiveFloorletVolatility();
175}
176
177Rate CappedFlooredAverageONIndexedCoupon::cap() const { return gearing_ > 0.0 ? cap_ : floor_; }
178
179Rate CappedFlooredAverageONIndexedCoupon::floor() const { return gearing_ > 0.0 ? floor_ : cap_; }
180
182 calculate();
183 return rate_;
184}
185
186Rate CappedFlooredAverageONIndexedCoupon::convexityAdjustment() const { return underlying_->convexityAdjustment(); }
187
189 if (cap_ == Null<Real>())
190 return Null<Real>();
191 /* We have four cases dependent on localCapFloor_ and includeSpread. Notation in the formulas:
192 g gearing,
193 s spread,
194 A coupon amount,
195 f_i daily fixings,
196 \tau_i daily accrual fractions,
197 \tau coupon accrual fraction,
198 C cap rate
199 F floor rate
200 */
201 if (localCapFloor_) {
202 if (includeSpread()) {
203 // A = \cdot \frac{\sum (\tau_i \min ( \max ( f_i + s , F), C))}{\tau}
204 return cap_ - underlying_->spread();
205 } else {
206 // A = g \cdot \frac{\sum (\tau_i \min ( \max ( f_i , F), C))}{\tau} + s
207 return cap_;
208 }
209 } else {
210 if (includeSpread()) {
211 // A = \min \left( \max \left( \frac{\sum (\tau_i f_i)}{\tau} + s, F \right), C \right)
212 return (cap_ / gearing() - underlying_->spread());
213 } else {
214 // A = \min \left( \max \left( g \cdot \frac{\sum (\tau_i f_i)}{\tau} + s, F \right), C \right)
215 return (cap_ - underlying_->spread()) / gearing();
216 }
217 }
218}
219
221 if (floor_ == Null<Real>())
222 return Null<Real>();
223 if (localCapFloor_) {
224 if (includeSpread()) {
225 return floor_ - underlying_->spread();
226 } else {
227 return floor_;
228 }
229 } else {
230 if (includeSpread()) {
231 return (floor_ - underlying_->spread());
232 } else {
233 return (floor_ - underlying_->spread()) / gearing();
234 }
235 }
236}
237
239 calculate();
241}
242
244 calculate();
246}
247
249 Visitor<CappedFlooredAverageONIndexedCoupon>* v1 = dynamic_cast<Visitor<CappedFlooredAverageONIndexedCoupon>*>(&v);
250 if (v1 != 0)
251 v1->visit(*this);
252 else
253 FloatingRateCoupon::accept(v);
254}
255
257 const ext::shared_ptr<AverageONIndexedCoupon>& underlying, Real cap, Real floor, bool nakedOption,
258 bool localCapFloor, bool includeSpread)
259 : FloatingRateCoupon(underlying->date(), underlying->nominal(), underlying->accrualStartDate(),
260 underlying->accrualEndDate(), underlying->fixingDays(), underlying->index(),
261 underlying->gearing(), underlying->spread(), underlying->referencePeriodStart(),
262 underlying->referencePeriodEnd(), underlying->dayCounter(), false),
263 underlying_(underlying), cap_(cap), floor_(floor), nakedOption_(nakedOption), localCapFloor_(localCapFloor),
264 includeSpread_(includeSpread) {
265 QL_REQUIRE(!includeSpread_ || close_enough(underlying_->gearing(), 1.0),
266 "CappedFlooredAverageONIndexedCoupon: if include spread = true, only a gearing 1.0 is allowed - scale "
267 "the notional in this case instead.");
268 registerWith(underlying_);
269 if (nakedOption_)
270 underlying_->alwaysForwardNotifications();
271}
272
273// capped floored average on coupon pricer base class implementation
274
276 const Handle<OptionletVolatilityStructure>& v, const bool effectiveVolatilityInput)
277 : capletVol_(v), effectiveVolatilityInput_(effectiveVolatilityInput) {
278 registerWith(capletVol_);
279}
280
282
284
287}
288
289Handle<OptionletVolatilityStructure> CapFlooredAverageONIndexedCouponPricer::capletVolatility() const {
290 return capletVol_;
291}
292
293// average on leg implementation
294
295AverageONLeg::AverageONLeg(const Schedule& schedule, const QuantLib::ext::shared_ptr<OvernightIndex>& i)
296 : schedule_(schedule), overnightIndex_(i), paymentAdjustment_(Following), paymentLag_(0),
297 telescopicValueDates_(false), paymentCalendar_(schedule.calendar()), rateCutoff_(0), lookback_(0 * Days),
298 fixingDays_(Null<Size>()), includeSpread_(false), nakedOption_(false), localCapFloor_(false), inArrears_(true) {}
299
301 notionals_ = std::vector<Real>(1, notional);
302 return *this;
303}
304
305AverageONLeg& AverageONLeg::withNotionals(const std::vector<Real>& notionals) {
306 notionals_ = notionals;
307 return *this;
308}
309
310AverageONLeg& AverageONLeg::withPaymentDayCounter(const DayCounter& dayCounter) {
311 paymentDayCounter_ = dayCounter;
312 return *this;
313}
314
315AverageONLeg& AverageONLeg::withPaymentAdjustment(BusinessDayConvention convention) {
316 paymentAdjustment_ = convention;
317 return *this;
318}
319
321 gearings_ = std::vector<Real>(1, gearing);
322 return *this;
323}
324
325AverageONLeg& AverageONLeg::withGearings(const std::vector<Real>& gearings) {
326 gearings_ = gearings;
327 return *this;
328}
329
331 spreads_ = std::vector<Spread>(1, spread);
332 return *this;
333}
334
335AverageONLeg& AverageONLeg::withSpreads(const std::vector<Spread>& spreads) {
336 spreads_ = spreads;
337 return *this;
338}
339
341 telescopicValueDates_ = telescopicValueDates;
342 return *this;
343}
344
346 rateCutoff_ = rateCutoff;
347 return *this;
348}
349
351 paymentCalendar_ = calendar;
352 return *this;
353}
354
356 paymentLag_ = lag;
357 return *this;
358}
359
360AverageONLeg& AverageONLeg::withLookback(const Period& lookback) {
361 lookback_ = lookback;
362 return *this;
363}
364
366 fixingDays_ = fixingDays;
367 return *this;
368}
369
371 caps_ = std::vector<Rate>(1, cap);
372 return *this;
373}
374
375AverageONLeg& AverageONLeg::withCaps(const std::vector<Rate>& caps) {
376 caps_ = caps;
377 return *this;
378}
379
381 floors_ = std::vector<Rate>(1, floor);
382 return *this;
383}
384
385AverageONLeg& AverageONLeg::withFloors(const std::vector<Rate>& floors) {
386 floors_ = floors;
387 return *this;
388}
389
391 includeSpread_ = includeSpread;
392 return *this;
393}
394
396 nakedOption_ = nakedOption;
397 return *this;
398}
399
401 localCapFloor_ = localCapFloor;
402 return *this;
403}
404
406 inArrears_ = inArrears;
407 return *this;
408}
409
410AverageONLeg& AverageONLeg::withLastRecentPeriod(const boost::optional<Period>& lastRecentPeriod) {
411 lastRecentPeriod_ = lastRecentPeriod;
412 return *this;
413}
414
415AverageONLeg& AverageONLeg::withLastRecentPeriodCalendar(const Calendar& lastRecentPeriodCalendar) {
416 lastRecentPeriodCalendar_ = lastRecentPeriodCalendar;
417 return *this;
418}
419
420AverageONLeg& AverageONLeg::withPaymentDates(const std::vector<Date>& paymentDates) {
421 paymentDates_ = paymentDates;
422 return *this;
423}
424
426AverageONLeg::withAverageONIndexedCouponPricer(const QuantLib::ext::shared_ptr<AverageONIndexedCouponPricer>& couponPricer) {
427 couponPricer_ = couponPricer;
428 return *this;
429}
430
432 const QuantLib::ext::shared_ptr<CapFlooredAverageONIndexedCouponPricer>& couponPricer) {
433 capFlooredCouponPricer_ = couponPricer;
434 return *this;
435}
436
437AverageONLeg::operator Leg() const {
438
439 QL_REQUIRE(!notionals_.empty(), "No notional given for average overnight leg.");
440
441 Leg cashflows;
442
443 Calendar calendar = schedule_.calendar();
444 Calendar paymentCalendar = paymentCalendar_;
445
446 if (calendar.empty())
447 calendar = paymentCalendar;
448 if (calendar.empty())
449 calendar = WeekendsOnly();
450 if (paymentCalendar.empty())
451 paymentCalendar = calendar;
452
453 Date refStart, start, refEnd, end;
454 Date paymentDate;
455
456 Size n = schedule_.size() - 1;
457
458 // Initial consistency checks
459 if (!paymentDates_.empty()) {
460 QL_REQUIRE(paymentDates_.size() == n, "Expected the number of explicit payment dates ("
461 << paymentDates_.size()
462 << ") to equal the number of calculation periods ("
463 << n << ")");
464 }
465
466 for (Size i = 0; i < n; ++i) {
467 refStart = start = schedule_.date(i);
468 refEnd = end = schedule_.date(i + 1);
469
470 // If explicit payment dates provided, use them.
471 if (!paymentDates_.empty()) {
472 paymentDate = paymentDates_[i];
473 } else {
474 paymentDate = paymentCalendar.advance(end, paymentLag_, Days, paymentAdjustment_);
475 }
476
477 // determine refStart and refEnd
478
479 if (i == 0 && schedule_.hasIsRegular() && !schedule_.isRegular(i + 1))
480 refStart = calendar.adjust(end - schedule_.tenor(), paymentAdjustment_);
481 if (i == n - 1 && schedule_.hasIsRegular() && !schedule_.isRegular(i + 1))
482 refEnd = calendar.adjust(start + schedule_.tenor(), paymentAdjustment_);
483
484 // Determine the rate computation start and end date as
485 // - the coupon start and end date, if in arrears, and
486 // - the previous coupon start and end date, if in advance.
487 // In addition, adjust the start date, if a last recent period is given.
488
489 Date rateComputationStartDate, rateComputationEndDate;
490 if (inArrears_) {
491 // in arrears fixing (i.e. the "classic" case)
492 rateComputationStartDate = start;
493 rateComputationEndDate = end;
494 } else {
495 // handle in advance fixing
496 if (i > 0) {
497 // if there is a previous period, we take that
498 rateComputationStartDate = schedule_.date(i - 1);
499 rateComputationEndDate = schedule_.date(i);
500 } else {
501 // otherwise we construct the previous period
502 rateComputationEndDate = start;
503 if (schedule_.hasTenor() && schedule_.tenor() != 0 * Days)
504 rateComputationStartDate = calendar.adjust(start - schedule_.tenor(), Preceding);
505 else
506 rateComputationStartDate = calendar.adjust(start - (end - start), Preceding);
507 }
508 }
509
510 if (lastRecentPeriod_) {
511 rateComputationStartDate = (lastRecentPeriodCalendar_.empty() ? calendar : lastRecentPeriodCalendar_)
512 .advance(rateComputationEndDate, -*lastRecentPeriod_);
513 }
514
515 // build coupon
516
517 if (close_enough(detail::get(gearings_, i, 1.0), 0.0)) {
518 // fixed coupon
519 cashflows.push_back(QuantLib::ext::make_shared<FixedRateCoupon>(paymentDate, detail::get(notionals_, i, 1.0),
520 detail::get(spreads_, i, 0.0), paymentDayCounter_,
521 start, end, refStart, refEnd));
522 } else {
523 // floating coupon
524 auto cpn = QuantLib::ext::make_shared<AverageONIndexedCoupon>(
525 paymentDate, detail::get(notionals_, i, notionals_.back()), start, end, overnightIndex_,
526 detail::get(gearings_, i, 1.0), detail::get(spreads_, i, 0.0), rateCutoff_, paymentDayCounter_,
527 lookback_, fixingDays_, rateComputationStartDate, rateComputationEndDate, telescopicValueDates_);
528 if (couponPricer_) {
529 cpn->setPricer(couponPricer_);
530 }
531 Real cap = detail::get(caps_, i, Null<Real>());
532 Real floor = detail::get(floors_, i, Null<Real>());
533 if (cap == Null<Real>() && floor == Null<Real>()) {
534 cashflows.push_back(cpn);
535 } else {
536 auto cfCpn = ext::make_shared<CappedFlooredAverageONIndexedCoupon>(cpn, cap, floor, nakedOption_,
537 localCapFloor_, includeSpread_);
538 if (capFlooredCouponPricer_) {
539 cfCpn->setPricer(capFlooredCouponPricer_);
540 }
541 cashflows.push_back(cfCpn);
542 }
543 }
544 }
545 return cashflows;
546}
547
548} // namespace QuantExt
coupon paying the weighted average of the daily overnight rate
Pricer for average overnight indexed coupons.
const ext::shared_ptr< OvernightIndex > & overnightIndex() const
the underlying index
AverageONIndexedCoupon(const Date &paymentDate, Real nominal, const Date &startDate, const Date &endDate, const QuantLib::ext::shared_ptr< OvernightIndex > &overnightIndex, Real gearing=1.0, Spread spread=0.0, Natural rateCutoff=0, const DayCounter &dayCounter=DayCounter(), const Period &lookback=0 *Days, const Size fixingDays=Null< Size >(), const Date &rateComputationStartDate=Null< Date >(), const Date &rateComputationEndDate=Null< Date >(), const bool telescopicValueDates=false)
void accept(AcyclicVisitor &) override
const Period & lookback() const
lookback period
const std::vector< Rate > & indexFixings() const
fixings to be averaged
Date fixingDate() const override
the date when the coupon is fully determined
helper class building a sequence of overnight coupons
AverageONLeg & withGearing(Real gearing)
AverageONLeg & withLastRecentPeriod(const boost::optional< Period > &lastRecentPeriod)
AverageONLeg & withPaymentDates(const std::vector< QuantLib::Date > &paymentDates)
BusinessDayConvention paymentAdjustment_
AverageONLeg & withSpread(Spread spread)
AverageONLeg & withPaymentDayCounter(const DayCounter &dayCounter)
AverageONLeg & includeSpreadInCapFloors(bool includeSpread)
AverageONLeg & withPaymentLag(Natural lag)
AverageONLeg & withNotional(Real notional)
AverageONLeg & withLookback(const Period &lookback)
AverageONLeg & withRateCutoff(Natural rateCutoff)
boost::optional< Period > lastRecentPeriod_
std::vector< QuantLib::Date > paymentDates_
AverageONLeg & withLastRecentPeriodCalendar(const Calendar &lastRecentPeriodCalendar)
AverageONLeg & withPaymentCalendar(const Calendar &calendar)
AverageONLeg & withNakedOption(const bool nakedOption)
QuantLib::ext::shared_ptr< CapFlooredAverageONIndexedCouponPricer > capFlooredCouponPricer_
std::vector< Spread > spreads_
AverageONLeg & withLocalCapFloor(const bool localCapFloor)
AverageONLeg & withTelescopicValueDates(bool telescopicValueDates)
AverageONLeg & withPaymentAdjustment(BusinessDayConvention convention)
AverageONLeg & withNotionals(const std::vector< Real > &notionals)
AverageONLeg & withSpreads(const std::vector< Spread > &spreads)
AverageONLeg & withInArrears(const bool inArrears)
AverageONLeg & withCaps(Rate cap)
AverageONLeg & withFloors(Rate floor)
AverageONLeg & withCapFlooredAverageONIndexedCouponPricer(const QuantLib::ext::shared_ptr< CapFlooredAverageONIndexedCouponPricer > &couponPricer)
QuantLib::ext::shared_ptr< AverageONIndexedCouponPricer > couponPricer_
AverageONLeg & withAverageONIndexedCouponPricer(const QuantLib::ext::shared_ptr< AverageONIndexedCouponPricer > &couponPricer)
AverageONLeg & withFixingDays(const Size fixingDays)
AverageONLeg & withGearings(const std::vector< Real > &gearings)
AverageONLeg(const Schedule &schedule, const QuantLib::ext::shared_ptr< OvernightIndex > &overnightIndex)
Handle< OptionletVolatilityStructure > capletVolatility() const
Handle< OptionletVolatilityStructure > capletVol_
CapFlooredAverageONIndexedCouponPricer(const Handle< OptionletVolatilityStructure > &v, const bool effectiveVolatilityInput=false)
Real effectiveCapletVolatility() const
effective caplet volatility
virtual void accept(AcyclicVisitor &) override
CappedFlooredAverageONIndexedCoupon(const ext::shared_ptr< AverageONIndexedCoupon > &underlying, Real cap=Null< Real >(), Real floor=Null< Real >(), bool nakedOption=false, bool localCapFloor=false, bool includeSpread=false)
Real effectiveFloorletVolatility() const
effective floorlet volatility
Rate effectiveCap() const
effective cap of fixing
Rate effectiveFloor() const
effective floor of fixing
ext::shared_ptr< AverageONIndexedCoupon > underlying_
Filter close_enough(const RandomVariable &x, const RandomVariable &y)