Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
overnightindexedcoupon.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/*
20 Copyright (C) 2009 Roland Lichters
21 Copyright (C) 2009 Ferdinando Ametrano
22 Copyright (C) 2014 Peter Caspers
23 Copyright (C) 2017 Joseph Jeisman
24 Copyright (C) 2017 Fabrice Lecuyer
25
26 This file is part of QuantLib, a free-software/open-source library
27 for financial quantitative analysts and developers - http://quantlib.org/
28
29 QuantLib is free software: you can redistribute it and/or modify it
30 under the terms of the QuantLib license. You should have received a
31 copy of the license along with this program; if not, please email
32 <quantlib-dev@lists.sf.net>. The license is also available online at
33 <http://quantlib.org/license.shtml>.
34
35 This program is distributed in the hope that it will be useful, but WITHOUT
36 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
37 FOR A PARTICULAR PURPOSE. See the license for more details.
38*/
39
41
42#include <ql/cashflows/cashflowvectors.hpp>
43#include <ql/cashflows/couponpricer.hpp>
44#include <ql/termstructures/yieldtermstructure.hpp>
45#include <ql/time/calendars/weekendsonly.hpp>
46#include <ql/utilities/vectors.hpp>
47
48using std::vector;
49
50namespace QuantExt {
51
52// OvernightIndexedCoupon implementation
53
54OvernightIndexedCoupon::OvernightIndexedCoupon(const Date& paymentDate, Real nominal, const Date& startDate,
55 const Date& endDate,
56 const ext::shared_ptr<OvernightIndex>& overnightIndex, Real gearing,
57 Spread spread, const Date& refPeriodStart, const Date& refPeriodEnd,
58 const DayCounter& dayCounter, bool telescopicValueDates,
59 bool includeSpread, const Period& lookback, const Natural rateCutoff,
60 const Natural fixingDays, const Date& rateComputationStartDate,
61 const Date& rateComputationEndDate)
62 : FloatingRateCoupon(paymentDate, nominal, startDate, endDate, fixingDays, overnightIndex, gearing, spread,
63 refPeriodStart, refPeriodEnd, dayCounter, false),
64 overnightIndex_(overnightIndex), includeSpread_(includeSpread), lookback_(lookback), rateCutoff_(rateCutoff),
65 rateComputationStartDate_(rateComputationStartDate), rateComputationEndDate_(rateComputationEndDate) {
66
67 Date valueStart = rateComputationStartDate_ == Null<Date>() ? startDate : rateComputationStartDate_;
68 Date valueEnd = rateComputationEndDate_ == Null<Date>() ? endDate : rateComputationEndDate_;
69 if (lookback != 0 * Days) {
70 BusinessDayConvention bdc = lookback.length() > 0 ? Preceding : Following;
71 valueStart = overnightIndex->fixingCalendar().advance(valueStart, -lookback, bdc);
72 valueEnd = overnightIndex->fixingCalendar().advance(valueEnd, -lookback, bdc);
73 }
74
75 // value dates
76 Date tmpEndDate = valueEnd;
77
78 /* For the coupon's valuation only the first and last future valuation
79 dates matter, therefore we can avoid to construct the whole series
80 of valuation dates, a front and back stub will do. However notice
81 that if the global evaluation date moves forward it might run past
82 the front stub of valuation dates we build here (which incorporates
83 a grace period of 7 business after the evaluation date). This will
84 lead to false coupon projections (see the warning the class header). */
85
86 if (telescopicValueDates) {
87 // build optimised value dates schedule: front stub goes
88 // from start date to max(evalDate,valueStart) + 7bd
89 Date evalDate = Settings::instance().evaluationDate();
90 tmpEndDate = overnightIndex->fixingCalendar().advance(std::max(valueStart, evalDate), 7, Days, Following);
91 tmpEndDate = std::min(tmpEndDate, valueEnd);
92 }
93 Schedule sch = MakeSchedule()
94 .from(valueStart)
95 // .to(valueEnd)
96 .to(tmpEndDate)
97 .withTenor(1 * Days)
98 .withCalendar(overnightIndex->fixingCalendar())
99 .withConvention(overnightIndex->businessDayConvention())
100 .backwards();
101 valueDates_ = sch.dates();
102
103 if (telescopicValueDates) {
104 // build optimised value dates schedule: back stub
105 // contains at least two dates and enough periods to cover rate cutoff
106 Date tmp2 = overnightIndex->fixingCalendar().adjust(valueEnd, overnightIndex->businessDayConvention());
107 Date tmp1 = overnightIndex->fixingCalendar().advance(tmp2, -std::max<Size>(rateCutoff_, 1), Days, Preceding);
108 while (tmp1 <= tmp2) {
109 if (tmp1 > valueDates_.back())
110 valueDates_.push_back(tmp1);
111 tmp1 = overnightIndex->fixingCalendar().advance(tmp1, 1, Days, Following);
112 }
113 }
114
115 QL_ENSURE(valueDates_.size() >= 2 + rateCutoff_, "degenerate schedule");
116
117 // the first and last value date should be the unadjusted input value dates
118 if (valueDates_.front() != valueStart)
119 valueDates_.front() = valueStart;
120 if (valueDates_.back() != valueEnd)
121 valueDates_.back() = valueEnd;
122
123 n_ = valueDates_.size() - 1;
124
125 QL_REQUIRE(valueDates_[0] != valueDates_[1],
126 "internal error: first two value dates of on coupon are equal: " << valueDates_[0]);
127 QL_REQUIRE(valueDates_[n_] != valueDates_[n_ - 1],
128 "internal error: last two value dates of on coupon are equal: " << valueDates_[n_]);
129
130 // fixing dates
131 fixingDates_.resize(n_);
132 for (Size i = 0; i < n_; ++i)
133 fixingDates_[i] = overnightIndex->fixingCalendar().advance(
134 valueDates_[i], -static_cast<Integer>(FloatingRateCoupon::fixingDays()), Days, Preceding);
135
136 // accrual (compounding) periods
137 dt_.resize(n_);
138 const DayCounter& dc = overnightIndex->dayCounter();
139 for (Size i = 0; i < n_; ++i)
140 dt_[i] = dc.yearFraction(valueDates_[i], valueDates_[i + 1]);
141
142 setPricer(ext::shared_ptr<FloatingRateCouponPricer>(new OvernightIndexedCouponPricer));
143
144 // check that rate cutoff is < number of fixing dates
145 QL_REQUIRE(rateCutoff_ < n_,
146 "rate cutoff (" << rateCutoff_ << ") must be less than number of fixings in period (" << n_ << ")");
147}
148
149const vector<Rate>& OvernightIndexedCoupon::indexFixings() const {
150 fixings_.resize(n_);
151 Size i;
152 for (i = 0; i < n_ - rateCutoff_; ++i) {
153 fixings_[i] = index_->fixing(fixingDates_[i]);
154 }
155 Rate cutoffFixing = fixings_[i - 1];
156 while (i < n_) {
157 fixings_[i] = cutoffFixing;
158 i++;
159 }
160 return fixings_;
161}
162
163void OvernightIndexedCoupon::accept(AcyclicVisitor& v) {
164 Visitor<OvernightIndexedCoupon>* v1 = dynamic_cast<Visitor<OvernightIndexedCoupon>*>(&v);
165 if (v1 != 0) {
166 v1->visit(*this);
167 } else {
168 FloatingRateCoupon::accept(v);
169 }
170}
171
173 if (!includeSpread_)
174 return spread();
175 auto p = ext::dynamic_pointer_cast<OvernightIndexedCouponPricer>(pricer());
176 QL_REQUIRE(p, "OvernightIndexedCoupon::effectiveSpread(): expected OvernightIndexedCouponPricer");
177 p->initialize(*this);
178 return p->effectiveSpread();
179}
180
182 auto p = ext::dynamic_pointer_cast<OvernightIndexedCouponPricer>(pricer());
183 QL_REQUIRE(p, "OvernightIndexedCoupon::effectiveSpread(): expected OvernightIndexedCouponPricer");
184 p->initialize(*this);
185 return p->effectiveIndexFixing();
186}
187
188// OvernightIndexedCouponPricer implementation
189
191 coupon_ = dynamic_cast<const OvernightIndexedCoupon*>(&coupon);
192 QL_ENSURE(coupon_, "wrong coupon type");
193}
194
196 ext::shared_ptr<OvernightIndex> index = ext::dynamic_pointer_cast<OvernightIndex>(coupon_->index());
197
198 const vector<Date>& fixingDates = coupon_->fixingDates();
199 const vector<Time>& dt = coupon_->dt();
200
201 Size n = dt.size();
202 Size i = 0;
203 QL_REQUIRE(coupon_->rateCutoff() < n, "rate cutoff (" << coupon_->rateCutoff()
204 << ") must be less than number of fixings in period (" << n
205 << ")");
206 Size nCutoff = n - coupon_->rateCutoff();
207
208 Real compoundFactor = 1.0, compoundFactorWithoutSpread = 1.0;
209
210 // already fixed part
211 Date today = Settings::instance().evaluationDate();
212 while (i < n && fixingDates[std::min(i, nCutoff)] < today) {
213 // rate must have been fixed
214 Rate pastFixing = index->pastFixing(fixingDates[std::min(i, nCutoff)]);
215 QL_REQUIRE(pastFixing != Null<Real>(),
216 "Missing " << index->name() << " fixing for " << fixingDates[std::min(i, nCutoff)]);
217 if (coupon_->includeSpread()) {
218 compoundFactorWithoutSpread *= (1.0 + pastFixing * dt[i]);
219 pastFixing += coupon_->spread();
220 }
221 compoundFactor *= (1.0 + pastFixing * dt[i]);
222 ++i;
223 }
224
225 // today is a border case
226 if (i < n && fixingDates[std::min(i, nCutoff)] == today) {
227 // might have been fixed
228 try {
229 Rate pastFixing = index->pastFixing(fixingDates[std::min(i, nCutoff)]);
230 if (pastFixing != Null<Real>()) {
231 if (coupon_->includeSpread()) {
232 compoundFactorWithoutSpread *= (1.0 + pastFixing * dt[i]);
233 pastFixing += coupon_->spread();
234 }
235 compoundFactor *= (1.0 + pastFixing * dt[i]);
236 ++i;
237 } else {
238 ; // fall through and forecast
239 }
240 } catch (Error&) {
241 ; // fall through and forecast
242 }
243 }
244
245 // forward part using telescopic property in order
246 // to avoid the evaluation of multiple forward fixings
247 const vector<Date>& dates = coupon_->valueDates();
248 if (i < n) {
249 Handle<YieldTermStructure> curve = index->forwardingTermStructure();
250 QL_REQUIRE(!curve.empty(), "null term structure set to this instance of " << index->name());
251
252 // handle the part until the rate cutoff (might be empty, i.e. startDiscount = endDiscount)
253 DiscountFactor startDiscount = curve->discount(dates[i]);
254 DiscountFactor endDiscount = curve->discount(dates[std::max(nCutoff, i)]);
255
256 // handle the rate cutoff period (if there is any, i.e. if nCutoff < n)
257 if (nCutoff < n) {
258 // forward discount factor for one calendar day on the cutoff date
259 DiscountFactor discountCutoffDate = curve->discount(dates[nCutoff] + 1) / curve->discount(dates[nCutoff]);
260 // keep the above forward discount factor constant during the cutoff period
261 endDiscount *= std::pow(discountCutoffDate, dates[n] - dates[nCutoff]);
262 }
263
264 compoundFactor *= startDiscount / endDiscount;
265
266 if (coupon_->includeSpread()) {
267 compoundFactorWithoutSpread *= startDiscount / endDiscount;
268 // this is an approximation, see "Ester / Daily Spread Curve Setup in ORE":
269 // set tau to an average value
270 Real tau = index->dayCounter().yearFraction(dates[i], dates.back()) / (dates.back() - dates[i]);
271 // now use formula (4) from the paper
272 compoundFactor *= std::pow(1.0 + tau * coupon_->spread(), static_cast<int>(dates.back() - dates[i]));
273 }
274 }
275
276 Rate tau = index->dayCounter().yearFraction(dates.front(), dates.back());
277 Rate rate = (compoundFactor - 1.0) / tau;
278 swapletRate_ = coupon_->gearing() * rate;
279 if (!coupon_->includeSpread()) {
280 swapletRate_ += coupon_->spread();
281 effectiveSpread_ = coupon_->spread();
283 } else {
284 effectiveSpread_ = rate - (compoundFactorWithoutSpread - 1.0) / tau;
286 }
287}
288
290 compute();
291 return swapletRate_;
292}
293
295 compute();
296 return effectiveSpread_;
297}
298
300 compute();
302}
303
304// CappedFlooredOvernightIndexedCoupon implementation
305
307 const ext::shared_ptr<OvernightIndexedCoupon>& underlying, Real cap, Real floor, bool nakedOption,
308 bool localCapFloor)
309 : FloatingRateCoupon(underlying->date(), underlying->nominal(), underlying->accrualStartDate(),
310 underlying->accrualEndDate(), underlying->fixingDays(), underlying->index(),
311 underlying->gearing(), underlying->spread(), underlying->referencePeriodStart(),
312 underlying->referencePeriodEnd(), underlying->dayCounter(), false),
313 underlying_(underlying), nakedOption_(nakedOption), localCapFloor_(localCapFloor) {
314
315 QL_REQUIRE(!underlying_->includeSpread() || close_enough(underlying_->gearing(), 1.0),
316 "CappedFlooredOvernightIndexedCoupon: if include spread = true, only a gearing 1.0 is allowed - scale "
317 "the notional in this case instead.");
318
319 if (!localCapFloor) {
320 if (gearing_ > 0.0) {
321 cap_ = cap;
322 floor_ = floor;
323 } else {
324 cap_ = floor;
325 floor_ = cap;
326 }
327 } else {
328 cap_ = cap;
329 floor_ = floor;
330 }
331 if (cap_ != Null<Real>() && floor_ != Null<Real>()) {
332 QL_REQUIRE(cap_ >= floor, "cap level (" << cap_ << ") less than floor level (" << floor_ << ")");
333 }
334 registerWith(underlying_);
335 if (nakedOption_)
336 underlying_->alwaysForwardNotifications();
337}
338
340 LazyObject::alwaysForwardNotifications();
341 underlying_->alwaysForwardNotifications();
342}
343
345 update();
346 underlying_->deepUpdate();
347}
348
350 QL_REQUIRE(underlying_->pricer(), "pricer not set");
351 Rate swapletRate = nakedOption_ ? 0.0 : underlying_->rate();
352 if (floor_ != Null<Real>() || cap_ != Null<Real>())
353 pricer()->initialize(*this);
354 Rate floorletRate = 0.;
355 if (floor_ != Null<Real>())
356 floorletRate = pricer()->floorletRate(effectiveFloor());
357 Rate capletRate = 0.;
358 if (cap_ != Null<Real>())
359 capletRate = (nakedOption_ && floor_ == Null<Real>() ? -1.0 : 1.0) * pricer()->capletRate(effectiveCap());
360 rate_ = swapletRate + floorletRate - capletRate;
361 auto p = QuantLib::ext::dynamic_pointer_cast<CappedFlooredOvernightIndexedCouponPricer>(pricer());
362 QL_REQUIRE(p, "CappedFlooredOvernightIndexedCoupon::performCalculations(): internal error, could not cast to "
363 "CappedFlooredOvernightIndexedCouponPricer");
364 effectiveCapletVolatility_ = p->effectiveCapletVolatility();
365 effectiveFloorletVolatility_ = p->effectiveFloorletVolatility();
366}
367
368Rate CappedFlooredOvernightIndexedCoupon::cap() const { return gearing_ > 0.0 ? cap_ : floor_; }
369
370Rate CappedFlooredOvernightIndexedCoupon::floor() const { return gearing_ > 0.0 ? floor_ : cap_; }
371
373 calculate();
374 return rate_;
375}
376
377Rate CappedFlooredOvernightIndexedCoupon::convexityAdjustment() const { return underlying_->convexityAdjustment(); }
378
380 if (cap_ == Null<Real>())
381 return Null<Real>();
382 /* We have four cases dependent on localCapFloor_ and includeSpread. Notation in the formulas:
383 g gearing,
384 s spread,
385 A coupon amount,
386 f_i daily fixings,
387 \tau_i daily accrual fractions,
388 \tau coupon accrual fraction,
389 C cap rate
390 F floor rate
391 */
392 if (localCapFloor_) {
393 if (underlying_->includeSpread()) {
394 // A = g \cdot \frac{\prod (1 + \tau_i \min ( \max ( f_i + s , F), C)) - 1}{\tau}
395 return cap_ - underlying_->spread();
396 } else {
397 // A = g \cdot \frac{\prod (1 + \tau_i \min ( \max ( f_i , F), C)) - 1}{\tau} + s
398 return cap_;
399 }
400 } else {
401 if (underlying_->includeSpread()) {
402 // A = \min \left( \max \left( g \cdot \frac{\prod (1 + \tau_i(f_i + s)) - 1}{\tau}, F \right), C \right)
403 return (cap_ / gearing() - underlying_->effectiveSpread());
404 } else {
405 // A = \min \left( \max \left( g \cdot \frac{\prod (1 + \tau_i f_i) - 1}{\tau} + s, F \right), C \right)
406 return (cap_ - underlying_->effectiveSpread()) / gearing();
407 }
408 }
409}
410
412 if (floor_ == Null<Real>())
413 return Null<Real>();
414 if (localCapFloor_) {
415 if (underlying_->includeSpread()) {
416 return floor_ - underlying_->spread();
417 } else {
418 return floor_;
419 }
420 } else {
421 if (underlying_->includeSpread()) {
422 return (floor_ - underlying_->effectiveSpread());
423 } else {
424 return (floor_ - underlying_->effectiveSpread()) / gearing();
425 }
426 }
427}
428
430 calculate();
432}
433
435 calculate();
437}
438
440 Visitor<CappedFlooredOvernightIndexedCoupon>* v1 = dynamic_cast<Visitor<CappedFlooredOvernightIndexedCoupon>*>(&v);
441 if (v1 != 0)
442 v1->visit(*this);
443 else
444 FloatingRateCoupon::accept(v);
445}
446
447// CappedFlooredOvernightIndexedCouponPricer implementation (this is the base class only)
448
450 const Handle<OptionletVolatilityStructure>& v, const bool effectiveVolatilityInput)
451 : capletVol_(v), effectiveVolatilityInput_(effectiveVolatilityInput) {
452 registerWith(capletVol_);
453}
454
456
458
461}
462
463Handle<OptionletVolatilityStructure> CappedFlooredOvernightIndexedCouponPricer::capletVolatility() const {
464 return capletVol_;
465}
466
467// OvernightLeg implementation
468
469OvernightLeg::OvernightLeg(const Schedule& schedule, const ext::shared_ptr<OvernightIndex>& i)
470 : schedule_(schedule), overnightIndex_(i), paymentCalendar_(schedule.calendar()), paymentAdjustment_(Following),
471 paymentLag_(0), telescopicValueDates_(false), includeSpread_(false), lookback_(0 * Days), rateCutoff_(0),
472 fixingDays_(Null<Size>()), nakedOption_(false), localCapFloor_(false), inArrears_(true) {}
473
475 notionals_ = vector<Real>(1, notional);
476 return *this;
477}
478
479OvernightLeg& OvernightLeg::withNotionals(const vector<Real>& notionals) {
480 notionals_ = notionals;
481 return *this;
482}
483
486 return *this;
487}
488
489OvernightLeg& OvernightLeg::withPaymentAdjustment(BusinessDayConvention convention) {
490 paymentAdjustment_ = convention;
491 return *this;
492}
493
495 paymentCalendar_ = cal;
496 return *this;
497}
498
500 paymentLag_ = lag;
501 return *this;
502}
503
505 gearings_ = vector<Real>(1, gearing);
506 return *this;
507}
508
509OvernightLeg& OvernightLeg::withGearings(const vector<Real>& gearings) {
510 gearings_ = gearings;
511 return *this;
512}
513
515 spreads_ = vector<Spread>(1, spread);
516 return *this;
517}
518
519OvernightLeg& OvernightLeg::withSpreads(const vector<Spread>& spreads) {
520 spreads_ = spreads;
521 return *this;
522}
523
525 telescopicValueDates_ = telescopicValueDates;
526 return *this;
527}
528
531 return *this;
532}
533
534OvernightLeg& OvernightLeg::withLookback(const Period& lookback) {
535 lookback_ = lookback;
536 return *this;
537}
538
539OvernightLeg& OvernightLeg::withRateCutoff(const Natural rateCutoff) {
540 rateCutoff_ = rateCutoff;
541 return *this;
542}
543
544OvernightLeg& OvernightLeg::withFixingDays(const Natural fixingDays) {
545 fixingDays_ = fixingDays;
546 return *this;
547}
548
550 caps_ = std::vector<Rate>(1, cap);
551 return *this;
552}
553
554OvernightLeg& OvernightLeg::withCaps(const std::vector<Rate>& caps) {
555 caps_ = caps;
556 return *this;
557}
558
560 floors_ = std::vector<Rate>(1, floor);
561 return *this;
562}
563
564OvernightLeg& OvernightLeg::withFloors(const std::vector<Rate>& floors) {
565 floors_ = floors;
566 return *this;
567}
568
570 nakedOption_ = nakedOption;
571 return *this;
572}
573
575 localCapFloor_ = localCapFloor;
576 return *this;
577}
578
580 inArrears_ = inArrears;
581 return *this;
582}
583
584OvernightLeg& OvernightLeg::withLastRecentPeriod(const boost::optional<Period>& lastRecentPeriod) {
585 lastRecentPeriod_ = lastRecentPeriod;
586 return *this;
587}
588
589OvernightLeg& OvernightLeg::withLastRecentPeriodCalendar(const Calendar& lastRecentPeriodCalendar) {
590 lastRecentPeriodCalendar_ = lastRecentPeriodCalendar;
591 return *this;
592}
593
594OvernightLeg& OvernightLeg::withPaymentDates(const std::vector<Date>& paymentDates) {
595 paymentDates_ = paymentDates;
596 return *this;
597}
598
600OvernightLeg::withOvernightIndexedCouponPricer(const QuantLib::ext::shared_ptr<OvernightIndexedCouponPricer>& couponPricer) {
601 couponPricer_ = couponPricer;
602 return *this;
603}
604
606 const QuantLib::ext::shared_ptr<CappedFlooredOvernightIndexedCouponPricer>& couponPricer) {
607 capFlooredCouponPricer_ = couponPricer;
608 return *this;
609}
610
611OvernightLeg::operator Leg() const {
612
613 QL_REQUIRE(!notionals_.empty(), "no notional given for compounding overnight leg");
614
615 Leg cashflows;
616
617 Calendar calendar = schedule_.calendar();
618 Calendar paymentCalendar = paymentCalendar_;
619
620 if (calendar.empty())
621 calendar = paymentCalendar;
622 if (calendar.empty())
623 calendar = WeekendsOnly();
624 if (paymentCalendar.empty())
625 paymentCalendar = calendar;
626
627 Date refStart, start, refEnd, end;
628 Date paymentDate;
629
630 Size n = schedule_.size() - 1;
631
632 // Initial consistency checks
633 if (!paymentDates_.empty()) {
634 QL_REQUIRE(paymentDates_.size() == n, "Expected the number of explicit payment dates ("
635 << paymentDates_.size()
636 << ") to equal the number of calculation periods ("
637 << n << ")");
638 }
639
640 for (Size i = 0; i < n; ++i) {
641 refStart = start = schedule_.date(i);
642 refEnd = end = schedule_.date(i + 1);
643
644 // If explicit payment dates provided, use them.
645 if (!paymentDates_.empty()) {
646 paymentDate = paymentDates_[i];
647 } else {
648 paymentDate = paymentCalendar.advance(end, paymentLag_, Days, paymentAdjustment_);
649 }
650
651 // determine refStart and refEnd
652
653 if (i == 0 && schedule_.hasIsRegular() && !schedule_.isRegular(i + 1))
654 refStart = calendar.adjust(end - schedule_.tenor(), paymentAdjustment_);
655 if (i == n - 1 && schedule_.hasIsRegular() && !schedule_.isRegular(i + 1))
656 refEnd = calendar.adjust(start + schedule_.tenor(), paymentAdjustment_);
657
658 // Determine the rate computation start and end date as
659 // - the coupon start and end date, if in arrears, and
660 // - the previous coupon start and end date, if in advance.
661 // In addition, adjust the start date, if a last recent period is given.
662
663 Date rateComputationStartDate, rateComputationEndDate;
664 if (inArrears_) {
665 // in arrears fixing (i.e. the "classic" case)
666 rateComputationStartDate = start;
667 rateComputationEndDate = end;
668 } else {
669 // handle in advance fixing
670 if (i > 0) {
671 // if there is a previous period, we take that
672 rateComputationStartDate = schedule_.date(i - 1);
673 rateComputationEndDate = schedule_.date(i);
674 } else {
675 // otherwise we construct the previous period
676 rateComputationEndDate = start;
677 if (schedule_.hasTenor() && schedule_.tenor() != 0 * Days)
678 rateComputationStartDate = calendar.adjust(start - schedule_.tenor(), Preceding);
679 else
680 rateComputationStartDate = calendar.adjust(start - (end - start), Preceding);
681 }
682 }
683
684 if (lastRecentPeriod_) {
685 rateComputationStartDate = (lastRecentPeriodCalendar_.empty() ? calendar : lastRecentPeriodCalendar_)
686 .advance(rateComputationEndDate, -*lastRecentPeriod_);
687 }
688
689 // build coupon
690
691 if (close_enough(detail::get(gearings_, i, 1.0), 0.0)) {
692 // fixed coupon
693 cashflows.push_back(QuantLib::ext::make_shared<FixedRateCoupon>(
694 paymentDate, detail::get(notionals_, i, 1.0), detail::effectiveFixedRate(spreads_, caps_, floors_, i),
695 paymentDayCounter_, start, end, refStart, refEnd));
696 } else {
697 // floating coupon
698 auto cpn = ext::make_shared<OvernightIndexedCoupon>(
699 paymentDate, detail::get(notionals_, i, 1.0), start, end, overnightIndex_,
700 detail::get(gearings_, i, 1.0), detail::get(spreads_, i, 0.0), refStart, refEnd, paymentDayCounter_,
701 telescopicValueDates_, includeSpread_, lookback_, rateCutoff_, fixingDays_, rateComputationStartDate,
702 rateComputationEndDate);
703 if (couponPricer_) {
704 cpn->setPricer(couponPricer_);
705 }
706 Real cap = detail::get(caps_, i, Null<Real>());
707 Real floor = detail::get(floors_, i, Null<Real>());
708 if (cap == Null<Real>() && floor == Null<Real>()) {
709 cashflows.push_back(cpn);
710 } else {
711 auto cfCpn = ext::make_shared<CappedFlooredOvernightIndexedCoupon>(cpn, cap, floor, nakedOption_,
712 localCapFloor_);
713 if (capFlooredCouponPricer_) {
714 cfCpn->setPricer(capFlooredCouponPricer_);
715 }
716 cashflows.push_back(cfCpn);
717 }
718 }
719 }
720 return cashflows;
721}
722
723} // namespace QuantExt
CappedFlooredOvernightIndexedCoupon(const ext::shared_ptr< OvernightIndexedCoupon > &underlying, Real cap=Null< Real >(), Real floor=Null< Real >(), bool nakedOption=false, bool localCapFloor=false)
Real effectiveCapletVolatility() const
effective caplet volatility
virtual void accept(AcyclicVisitor &) override
Real effectiveFloorletVolatility() const
effective floorlet volatility
Rate effectiveCap() const
effective cap of fixing
ext::shared_ptr< OvernightIndexedCoupon > underlying_
Rate effectiveFloor() const
effective floor of fixing
Handle< OptionletVolatilityStructure > capletVolatility() const
CappedFlooredOvernightIndexedCouponPricer(const Handle< OptionletVolatilityStructure > &v, const bool effectiveVolatilityInput=false)
OvernightIndexedCoupon(const Date &paymentDate, Real nominal, const Date &startDate, const Date &endDate, const ext::shared_ptr< OvernightIndex > &overnightIndex, Real gearing=1.0, Spread spread=0.0, const Date &refPeriodStart=Date(), const Date &refPeriodEnd=Date(), const DayCounter &dayCounter=DayCounter(), bool telescopicValueDates=false, bool includeSpread=false, const Period &lookback=0 *Days, const Natural rateCutoff=0, const Natural fixingDays=Null< Size >(), const Date &rateComputationStartDate=Null< Date >(), const Date &rateComputationEndDate=Null< Date >())
bool includeSpread() const
include spread in compounding?
const ext::shared_ptr< OvernightIndex > & overnightIndex() const
the underlying index
const std::vector< Date > & valueDates() const
value dates for the rates to be compounded
const std::vector< Rate > & indexFixings() const
fixings to be compounded
void accept(AcyclicVisitor &) override
const Period & lookback() const
lookback period
const std::vector< Time > & dt() const
accrual (compounding) periods
const std::vector< Date > & fixingDates() const
fixing dates for the rates to be compounded
void initialize(const FloatingRateCoupon &coupon) override
helper class building a sequence of overnight coupons
OvernightLeg & withLookback(const Period &lookback)
BusinessDayConvention paymentAdjustment_
OvernightLeg(const Schedule &schedule, const ext::shared_ptr< OvernightIndex > &overnightIndex)
OvernightLeg & withGearings(Real gearing)
QuantLib::ext::shared_ptr< CappedFlooredOvernightIndexedCouponPricer > capFlooredCouponPricer_
OvernightLeg & withOvernightIndexedCouponPricer(const QuantLib::ext::shared_ptr< OvernightIndexedCouponPricer > &couponPricer)
OvernightLeg & withCapFlooredOvernightIndexedCouponPricer(const QuantLib::ext::shared_ptr< CappedFlooredOvernightIndexedCouponPricer > &couponPricer)
OvernightLeg & withPaymentCalendar(const Calendar &)
OvernightLeg & withLastRecentPeriod(const boost::optional< Period > &lastRecentPeriod)
OvernightLeg & withFloors(Rate floor)
OvernightLeg & withTelescopicValueDates(bool telescopicValueDates)
OvernightLeg & withPaymentAdjustment(BusinessDayConvention)
boost::optional< Period > lastRecentPeriod_
std::vector< QuantLib::Date > paymentDates_
OvernightLeg & withNakedOption(const bool nakedOption)
std::vector< Spread > spreads_
OvernightLeg & withFixingDays(const Natural fixingDays)
OvernightLeg & withNotionals(Real notional)
OvernightLeg & withRateCutoff(const Natural rateCutoff)
OvernightLeg & withCaps(Rate cap)
OvernightLeg & withPaymentDayCounter(const DayCounter &)
OvernightLeg & withInArrears(const bool inArrears)
OvernightLeg & withSpreads(Spread spread)
OvernightLeg & withPaymentDates(const std::vector< Date > &paymentDates)
OvernightLeg & withLocalCapFloor(const bool localCapFloor)
OvernightLeg & includeSpread(bool includeSpread)
OvernightLeg & withPaymentLag(Natural lag)
QuantLib::ext::shared_ptr< OvernightIndexedCouponPricer > couponPricer_
OvernightLeg & withLastRecentPeriodCalendar(const Calendar &lastRecentPeriodCalendar)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
coupon paying the compounded daily overnight rate, copy of QL class, added includeSpread flag