1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2009 Roland Lichters
5 Copyright (C) 2009 Ferdinando Ametrano
6 Copyright (C) 2014 Peter Caspers
7 Copyright (C) 2017 Joseph Jeisman
8 Copyright (C) 2017 Fabrice Lecuyer
9
10 This file is part of QuantLib, a free-software/open-source library
11 for financial quantitative analysts and developers - http://quantlib.org/
12
13 QuantLib is free software: you can redistribute it and/or modify it
14 under the terms of the QuantLib license. You should have received a
15 copy of the license along with this program; if not, please email
16 <quantlib-dev@lists.sf.net>. The license is also available online at
17 <http://quantlib.org/license.shtml>.
18
19 This program is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21 FOR A PARTICULAR PURPOSE. See the license for more details.
22*/
23
24#include <ql/cashflows/couponpricer.hpp>
25#include <ql/experimental/averageois/averageoiscouponpricer.hpp>
26#include <ql/cashflows/overnightindexedcoupon.hpp>
27#include <ql/termstructures/yieldtermstructure.hpp>
28#include <ql/utilities/vectors.hpp>
29#include <utility>
30#include <algorithm>
31
32using std::vector;
33
34namespace QuantLib {
35
36 namespace {
37
38 class OvernightIndexedCouponPricer : public FloatingRateCouponPricer {
39 public:
40 void initialize(const FloatingRateCoupon& coupon) override {
41 coupon_ = dynamic_cast<const OvernightIndexedCoupon*>(&coupon);
42 QL_ENSURE(coupon_, "wrong coupon type");
43 }
44
45 Rate averageRate(const Date& date) const {
46
47 const Date today = Settings::instance().evaluationDate();
48
49 const ext::shared_ptr<OvernightIndex> index =
50 ext::dynamic_pointer_cast<OvernightIndex>(r: coupon_->index());
51 const auto& pastFixings = IndexManager::instance().getHistory(name: index->name());
52
53 const vector<Date>& fixingDates = coupon_->fixingDates();
54 const vector<Date>& valueDates = coupon_->valueDates();
55 const vector<Time>& dt = coupon_->dt();
56
57 Size i = 0;
58 const size_t n = std::lower_bound(first: valueDates.begin(), last: valueDates.end(), val: date) - valueDates.begin();
59 Real compoundFactor = 1.0;
60
61 // already fixed part
62 while (i < n && fixingDates[i] < today) {
63 // rate must have been fixed
64 const Rate fixing = pastFixings[fixingDates[i]];
65 QL_REQUIRE(fixing != Null<Real>(),
66 "Missing " << index->name() <<
67 " fixing for " << fixingDates[i]);
68 Time span = (date >= valueDates[i+1] ?
69 dt[i] :
70 index->dayCounter().yearFraction(d1: valueDates[i], d2: date));
71 compoundFactor *= (1.0 + fixing * span);
72 ++i;
73 }
74
75 // today is a border case
76 if (i < n && fixingDates[i] == today) {
77 // might have been fixed
78 try {
79 Rate fixing = pastFixings[fixingDates[i]];
80 if (fixing != Null<Real>()) {
81 Time span = (date >= valueDates[i+1] ?
82 dt[i] :
83 index->dayCounter().yearFraction(d1: valueDates[i], d2: date));
84 compoundFactor *= (1.0 + fixing * span);
85 ++i;
86 } else {
87 ; // fall through and forecast
88 }
89 } catch (Error&) {
90 ; // fall through and forecast
91 }
92 }
93
94 // forward part using telescopic property in order
95 // to avoid the evaluation of multiple forward fixings
96 if (i<n) {
97 const Handle<YieldTermStructure> curve = index->forwardingTermStructure();
98 QL_REQUIRE(!curve.empty(),
99 "null term structure set to this instance of " << index->name());
100
101 const DiscountFactor startDiscount = curve->discount(d: valueDates[i]);
102 if (valueDates[n] == date) {
103 // full telescopic formula
104 const DiscountFactor endDiscount = curve->discount(d: valueDates[n]);
105 compoundFactor *= startDiscount / endDiscount;
106 } else {
107 // The last fixing is not used for its full period (the date is between its
108 // start and end date). We can use the telescopic formula until the previous
109 // date, then we'll add the missing bit.
110 const DiscountFactor endDiscount = curve->discount(d: valueDates[n-1]);
111 compoundFactor *= startDiscount / endDiscount;
112
113 Rate fixing = index->fixing(fixingDate: fixingDates[n-1]);
114 Time span = index->dayCounter().yearFraction(d1: valueDates[n-1], d2: date);
115 compoundFactor *= (1.0 + fixing * span);
116 }
117 }
118
119 const Rate rate = (compoundFactor - 1.0) / coupon_->accruedPeriod(date);
120 return coupon_->gearing() * rate + coupon_->spread();
121 }
122
123 Rate swapletRate() const override {
124 return averageRate(date: coupon_->accrualEndDate());
125 }
126
127 Real swapletPrice() const override { QL_FAIL("swapletPrice not available"); }
128 Real capletPrice(Rate) const override { QL_FAIL("capletPrice not available"); }
129 Rate capletRate(Rate) const override { QL_FAIL("capletRate not available"); }
130 Real floorletPrice(Rate) const override { QL_FAIL("floorletPrice not available"); }
131 Rate floorletRate(Rate) const override { QL_FAIL("floorletRate not available"); }
132
133 protected:
134 const OvernightIndexedCoupon* coupon_;
135 };
136 }
137
138 OvernightIndexedCoupon::OvernightIndexedCoupon(
139 const Date& paymentDate,
140 Real nominal,
141 const Date& startDate,
142 const Date& endDate,
143 const ext::shared_ptr<OvernightIndex>& overnightIndex,
144 Real gearing,
145 Spread spread,
146 const Date& refPeriodStart,
147 const Date& refPeriodEnd,
148 const DayCounter& dayCounter,
149 bool telescopicValueDates,
150 RateAveraging::Type averagingMethod)
151 : FloatingRateCoupon(paymentDate, nominal, startDate, endDate,
152 overnightIndex ? overnightIndex->fixingDays() : 0,
153 overnightIndex,
154 gearing, spread,
155 refPeriodStart, refPeriodEnd,
156 dayCounter, false) {
157
158 // value dates
159 Date tmpEndDate = endDate;
160
161 /* For the coupon's valuation only the first and last future valuation
162 dates matter, therefore we can avoid to construct the whole series
163 of valuation dates, a front and back stub will do. However notice
164 that if the global evaluation date moves forward it might run past
165 the front stub of valuation dates we build here (which incorporates
166 a grace period of 7 business after the evaluation date). This will
167 lead to false coupon projections (see the warning the class header). */
168
169 if (telescopicValueDates) {
170 // build optimised value dates schedule: front stub goes
171 // from start date to max(evalDate,startDate) + 7bd
172 Date evalDate = Settings::instance().evaluationDate();
173 tmpEndDate = overnightIndex->fixingCalendar().advance(
174 std::max(a: startDate, b: evalDate), n: 7, unit: Days, convention: Following);
175 tmpEndDate = std::min(a: tmpEndDate, b: endDate);
176 }
177 Schedule sch =
178 MakeSchedule()
179 .from(effectiveDate: startDate)
180 // .to(endDate)
181 .to(terminationDate: tmpEndDate)
182 .withTenor(1 * Days)
183 .withCalendar(overnightIndex->fixingCalendar())
184 .withConvention(overnightIndex->businessDayConvention())
185 .backwards();
186 valueDates_ = sch.dates();
187
188 if (telescopicValueDates) {
189 // build optimised value dates schedule: back stub
190 // contains at least two dates
191 Date tmp = overnightIndex->fixingCalendar().advance(
192 endDate, n: -1, unit: Days, convention: Preceding);
193 if (tmp != valueDates_.back())
194 valueDates_.push_back(x: tmp);
195 tmp = overnightIndex->fixingCalendar().adjust(
196 endDate, convention: overnightIndex->businessDayConvention());
197 if (tmp != valueDates_.back())
198 valueDates_.push_back(x: tmp);
199 }
200
201 QL_ENSURE(valueDates_.size()>=2, "degenerate schedule");
202
203 // fixing dates
204 n_ = valueDates_.size()-1;
205 if (overnightIndex->fixingDays()==0) {
206 fixingDates_ = vector<Date>(valueDates_.begin(),
207 valueDates_.end() - 1);
208 } else {
209 fixingDates_.resize(new_size: n_);
210 for (Size i=0; i<n_; ++i)
211 fixingDates_[i] = overnightIndex->fixingDate(valueDate: valueDates_[i]);
212 }
213
214 // accrual (compounding) periods
215 dt_.resize(new_size: n_);
216 const DayCounter& dc = overnightIndex->dayCounter();
217 for (Size i=0; i<n_; ++i)
218 dt_[i] = dc.yearFraction(d1: valueDates_[i], d2: valueDates_[i+1]);
219
220 switch (averagingMethod) {
221 case RateAveraging::Simple:
222 setPricer(ext::shared_ptr<FloatingRateCouponPricer>(
223 new ArithmeticAveragedOvernightIndexedCouponPricer(telescopicValueDates)));
224 break;
225 case RateAveraging::Compound:
226 setPricer(
227 ext::shared_ptr<FloatingRateCouponPricer>(new OvernightIndexedCouponPricer));
228 break;
229 default:
230 QL_FAIL("unknown compounding convention (" << Integer(averagingMethod) << ")");
231 }
232 }
233
234 Real OvernightIndexedCoupon::accruedAmount(const Date& d) const {
235 if (d <= accrualStartDate_ || d > paymentDate_) {
236 // out of coupon range
237 return 0.0;
238 } else if (tradingExCoupon(refDate: d)) {
239 return nominal() * averageRate(date: d) * accruedPeriod(d);
240 } else {
241 // usual case
242 return nominal() * averageRate(date: std::min(a: d, b: accrualEndDate_)) * accruedPeriod(d);
243 }
244 }
245
246 Rate OvernightIndexedCoupon::averageRate(const Date& d) const {
247 QL_REQUIRE(pricer_, "pricer not set");
248 pricer_->initialize(coupon: *this);
249 const auto overnightIndexPricer = ext::dynamic_pointer_cast<OvernightIndexedCouponPricer>(r: pricer_);
250 if (overnightIndexPricer)
251 return overnightIndexPricer->averageRate(date: d);
252
253 return pricer_->swapletRate();
254 }
255
256 const vector<Rate>& OvernightIndexedCoupon::indexFixings() const {
257 fixings_.resize(new_size: n_);
258 for (Size i=0; i<n_; ++i)
259 fixings_[i] = index_->fixing(fixingDate: fixingDates_[i]);
260 return fixings_;
261 }
262
263 void OvernightIndexedCoupon::accept(AcyclicVisitor& v) {
264 auto* v1 = dynamic_cast<Visitor<OvernightIndexedCoupon>*>(&v);
265 if (v1 != nullptr) {
266 v1->visit(*this);
267 } else {
268 FloatingRateCoupon::accept(v);
269 }
270 }
271
272 OvernightLeg::OvernightLeg(const Schedule& schedule, ext::shared_ptr<OvernightIndex> i)
273 : schedule_(schedule), overnightIndex_(std::move(i)), paymentCalendar_(schedule.calendar()) {
274 QL_REQUIRE(overnightIndex_, "no index provided");
275 }
276
277 OvernightLeg& OvernightLeg::withNotionals(Real notional) {
278 notionals_ = vector<Real>(1, notional);
279 return *this;
280 }
281
282 OvernightLeg& OvernightLeg::withNotionals(const vector<Real>& notionals) {
283 notionals_ = notionals;
284 return *this;
285 }
286
287 OvernightLeg& OvernightLeg::withPaymentDayCounter(const DayCounter& dc) {
288 paymentDayCounter_ = dc;
289 return *this;
290 }
291
292 OvernightLeg&
293 OvernightLeg::withPaymentAdjustment(BusinessDayConvention convention) {
294 paymentAdjustment_ = convention;
295 return *this;
296 }
297
298 OvernightLeg& OvernightLeg::withPaymentCalendar(const Calendar& cal) {
299 paymentCalendar_ = cal;
300 return *this;
301 }
302
303 OvernightLeg& OvernightLeg::withPaymentLag(Natural lag) {
304 paymentLag_ = lag;
305 return *this;
306 }
307
308 OvernightLeg& OvernightLeg::withGearings(Real gearing) {
309 gearings_ = vector<Real>(1,gearing);
310 return *this;
311 }
312
313 OvernightLeg& OvernightLeg::withGearings(const vector<Real>& gearings) {
314 gearings_ = gearings;
315 return *this;
316 }
317
318 OvernightLeg& OvernightLeg::withSpreads(Spread spread) {
319 spreads_ = vector<Spread>(1,spread);
320 return *this;
321 }
322
323 OvernightLeg& OvernightLeg::withSpreads(const vector<Spread>& spreads) {
324 spreads_ = spreads;
325 return *this;
326 }
327
328 OvernightLeg& OvernightLeg::withTelescopicValueDates(bool telescopicValueDates) {
329 telescopicValueDates_ = telescopicValueDates;
330 return *this;
331 }
332
333 OvernightLeg& OvernightLeg::withAveragingMethod(RateAveraging::Type averagingMethod) {
334 averagingMethod_ = averagingMethod;
335 return *this;
336 }
337
338 OvernightLeg::operator Leg() const {
339
340 QL_REQUIRE(!notionals_.empty(), "no notional given");
341
342 Leg cashflows;
343
344 // the following is not always correct
345 Calendar calendar = schedule_.calendar();
346
347 Date refStart, start, refEnd, end;
348 Date paymentDate;
349
350 Size n = schedule_.size()-1;
351 for (Size i=0; i<n; ++i) {
352 refStart = start = schedule_.date(i);
353 refEnd = end = schedule_.date(i: i+1);
354 paymentDate = paymentCalendar_.advance(end, n: paymentLag_, unit: Days, convention: paymentAdjustment_);
355
356 if (i == 0 && schedule_.hasIsRegular() && !schedule_.isRegular(i: i+1))
357 refStart = calendar.adjust(end - schedule_.tenor(),
358 convention: paymentAdjustment_);
359 if (i == n-1 && schedule_.hasIsRegular() && !schedule_.isRegular(i: i+1))
360 refEnd = calendar.adjust(start + schedule_.tenor(),
361 convention: paymentAdjustment_);
362
363 cashflows.push_back(x: ext::shared_ptr<CashFlow>(new
364 OvernightIndexedCoupon(paymentDate,
365 detail::get(v: notionals_, i,
366 defaultValue: notionals_.back()),
367 start, end,
368 overnightIndex_,
369 detail::get(v: gearings_, i, defaultValue: 1.0),
370 detail::get(v: spreads_, i, defaultValue: 0.0),
371 refStart, refEnd,
372 paymentDayCounter_,
373 telescopicValueDates_,
374 averagingMethod_)));
375 }
376 return cashflows;
377 }
378
379}
380

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of quantlib/ql/cashflows/overnightindexedcoupon.cpp