/src/quantlib/ql/cashflows/multipleresetscoupon.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2008 Toyin Akin |
5 | | Copyright (C) 2021 Marcin Rybacki |
6 | | |
7 | | This file is part of QuantLib, a free-software/open-source library |
8 | | for financial quantitative analysts and developers - http://quantlib.org/ |
9 | | |
10 | | QuantLib is free software: you can redistribute it and/or modify it |
11 | | under the terms of the QuantLib license. You should have received a |
12 | | copy of the license along with this program; if not, please email |
13 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
14 | | <https://www.quantlib.org/license.shtml>. |
15 | | |
16 | | This program is distributed in the hope that it will be useful, but WITHOUT |
17 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
19 | | */ |
20 | | |
21 | | #include <ql/cashflows/multipleresetscoupon.hpp> |
22 | | #include <ql/cashflows/cashflowvectors.hpp> |
23 | | #include <ql/time/schedule.hpp> |
24 | | #include <ql/indexes/iborindex.hpp> |
25 | | #include <ql/termstructures/yieldtermstructure.hpp> |
26 | | #include <cmath> |
27 | | |
28 | | namespace QuantLib { |
29 | | |
30 | | MultipleResetsCoupon::MultipleResetsCoupon(const Date& paymentDate, |
31 | | Real nominal, |
32 | | const Schedule& resetSchedule, |
33 | | Natural fixingDays, |
34 | | const ext::shared_ptr<IborIndex>& index, |
35 | | Real gearing, |
36 | | Rate couponSpread, |
37 | | Rate rateSpread, |
38 | | const Date& refPeriodStart, |
39 | | const Date& refPeriodEnd, |
40 | | const DayCounter& dayCounter, |
41 | | const Date& exCouponDate) |
42 | 0 | : FloatingRateCoupon(paymentDate, nominal, |
43 | 0 | resetSchedule.front(), resetSchedule.back(), |
44 | 0 | fixingDays, index, gearing, couponSpread, |
45 | 0 | refPeriodStart, refPeriodEnd, dayCounter, |
46 | 0 | false, exCouponDate), |
47 | 0 | rateSpread_(rateSpread) { |
48 | 0 | valueDates_ = resetSchedule.dates(); |
49 | | |
50 | | // fixing dates |
51 | 0 | n_ = valueDates_.size() - 1; |
52 | 0 | if (fixingDays_ == 0) { |
53 | 0 | fixingDates_ = std::vector<Date>(valueDates_.begin(), valueDates_.end() - 1); |
54 | 0 | } else { |
55 | 0 | fixingDates_.resize(n_); |
56 | 0 | for (Size i = 0; i < n_; ++i) |
57 | 0 | fixingDates_[i] = fixingDate(valueDates_[i]); |
58 | 0 | } |
59 | | |
60 | | // accrual times of sub-periods |
61 | 0 | dt_.resize(n_); |
62 | 0 | const DayCounter& dc = index->dayCounter(); |
63 | 0 | for (Size i = 0; i < n_; ++i) |
64 | 0 | dt_[i] = dc.yearFraction(valueDates_[i], valueDates_[i + 1]); |
65 | 0 | } Unexecuted instantiation: QuantLib::MultipleResetsCoupon::MultipleResetsCoupon(QuantLib::Date const&, double, QuantLib::Schedule const&, unsigned int, boost::shared_ptr<QuantLib::IborIndex> const&, double, double, double, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, QuantLib::Date const&) Unexecuted instantiation: QuantLib::MultipleResetsCoupon::MultipleResetsCoupon(QuantLib::Date const&, double, QuantLib::Schedule const&, unsigned int, boost::shared_ptr<QuantLib::IborIndex> const&, double, double, double, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, QuantLib::Date const&) |
66 | | |
67 | 0 | void MultipleResetsCoupon::accept(AcyclicVisitor& v) { |
68 | 0 | auto* v1 = dynamic_cast<Visitor<MultipleResetsCoupon>*>(&v); |
69 | 0 | if (v1 != nullptr) |
70 | 0 | v1->visit(*this); |
71 | 0 | else |
72 | 0 | FloatingRateCoupon::accept(v); |
73 | 0 | } |
74 | | |
75 | 0 | Date MultipleResetsCoupon::fixingDate(const Date& valueDate) const { |
76 | 0 | Date fixingDate = |
77 | 0 | index_->fixingCalendar().advance(valueDate, -static_cast<Integer>(fixingDays_), Days); |
78 | 0 | return fixingDate; |
79 | 0 | } |
80 | | |
81 | 0 | void MultipleResetsPricer::initialize(const FloatingRateCoupon& coupon) { |
82 | 0 | coupon_ = dynamic_cast<const MultipleResetsCoupon*>(&coupon); |
83 | 0 | QL_REQUIRE(coupon_, "sub-periods coupon required"); |
84 | | |
85 | 0 | ext::shared_ptr<IborIndex> index = ext::dynamic_pointer_cast<IborIndex>(coupon_->index()); |
86 | 0 | if (!index) { |
87 | | // coupon was right, index is not |
88 | 0 | QL_FAIL("IborIndex required"); |
89 | 0 | } |
90 | | |
91 | 0 | QL_REQUIRE(coupon_->accrualPeriod() != 0.0, "null accrual period"); |
92 | | |
93 | 0 | const std::vector<Date>& fixingDates = coupon_->fixingDates(); |
94 | 0 | Size n = fixingDates.size(); |
95 | 0 | subPeriodFixings_.resize(n); |
96 | |
|
97 | 0 | for (Size i = 0; i < n; i++) { |
98 | 0 | subPeriodFixings_[i] = index->fixing(fixingDates[i]) + coupon_->rateSpread(); |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | 0 | Real MultipleResetsPricer::swapletPrice() const { |
103 | 0 | QL_FAIL("MultipleResetsPricer::swapletPrice not implemented"); |
104 | 0 | } |
105 | | |
106 | 0 | Real MultipleResetsPricer::capletPrice(Rate) const { |
107 | 0 | QL_FAIL("MultipleResetsPricer::capletPrice not implemented"); |
108 | 0 | } |
109 | | |
110 | 0 | Rate MultipleResetsPricer::capletRate(Rate) const { |
111 | 0 | QL_FAIL("MultipleResetsPricer::capletRate not implemented"); |
112 | 0 | } |
113 | | |
114 | 0 | Real MultipleResetsPricer::floorletPrice(Rate) const { |
115 | 0 | QL_FAIL("MultipleResetsPricer::floorletPrice not implemented"); |
116 | 0 | } |
117 | | |
118 | 0 | Rate MultipleResetsPricer::floorletRate(Rate) const { |
119 | 0 | QL_FAIL("MultipleResetsPricer::floorletRate not implemented"); |
120 | 0 | } |
121 | | |
122 | 0 | Real AveragingMultipleResetsPricer::swapletRate() const { |
123 | | // past or future fixing is managed in InterestRateIndex::fixing() |
124 | |
|
125 | 0 | Size nCount = subPeriodFixings_.size(); |
126 | 0 | const std::vector<Time>& subPeriodFractions = coupon_->dt(); |
127 | 0 | Real aggregateFactor = 0.0; |
128 | 0 | for (Size i = 0; i < nCount; i++) { |
129 | 0 | aggregateFactor += subPeriodFixings_[i] * subPeriodFractions[i]; |
130 | 0 | } |
131 | |
|
132 | 0 | Real rate = aggregateFactor / coupon_->accrualPeriod(); |
133 | 0 | return coupon_->gearing() * rate + coupon_->spread(); |
134 | 0 | } |
135 | | |
136 | 0 | Real CompoundingMultipleResetsPricer::swapletRate() const { |
137 | | // past or future fixing is managed in InterestRateIndex::fixing() |
138 | |
|
139 | 0 | Real compoundFactor = 1.0; |
140 | 0 | const std::vector<Time>& subPeriodFractions = coupon_->dt(); |
141 | 0 | Size nCount = subPeriodFixings_.size(); |
142 | 0 | for (Size i = 0; i < nCount; i++) { |
143 | 0 | compoundFactor *= (1.0 + subPeriodFixings_[i] * subPeriodFractions[i]); |
144 | 0 | } |
145 | |
|
146 | 0 | Real rate = (compoundFactor - 1.0) / coupon_->accrualPeriod(); |
147 | 0 | return coupon_->gearing() * rate + coupon_->spread(); |
148 | 0 | } |
149 | | |
150 | | |
151 | | |
152 | | MultipleResetsLeg::MultipleResetsLeg(Schedule schedule, |
153 | | ext::shared_ptr<IborIndex> index, |
154 | | Size resetsPerCoupon) |
155 | 0 | : schedule_(std::move(schedule)), index_(std::move(index)), resetsPerCoupon_(resetsPerCoupon), |
156 | 0 | paymentCalendar_(schedule_.calendar()) { |
157 | 0 | QL_REQUIRE(index_, "no index provided"); |
158 | 0 | QL_REQUIRE(!schedule_.empty(), "empty schedule provided"); |
159 | 0 | QL_REQUIRE((schedule_.size() - 1) % resetsPerCoupon_ == 0, |
160 | 0 | "number of resets per coupon does not divide exactly number of periods in schedule"); |
161 | 0 | } |
162 | | |
163 | 0 | MultipleResetsLeg& MultipleResetsLeg::withNotionals(Real notional) { |
164 | 0 | notionals_ = std::vector<Real>(1, notional); |
165 | 0 | return *this; |
166 | 0 | } |
167 | | |
168 | 0 | MultipleResetsLeg& MultipleResetsLeg::withNotionals(const std::vector<Real>& notionals) { |
169 | 0 | notionals_ = notionals; |
170 | 0 | return *this; |
171 | 0 | } |
172 | | |
173 | 0 | MultipleResetsLeg& MultipleResetsLeg::withPaymentDayCounter(const DayCounter& dc) { |
174 | 0 | paymentDayCounter_ = dc; |
175 | 0 | return *this; |
176 | 0 | } |
177 | | |
178 | 0 | MultipleResetsLeg& MultipleResetsLeg::withPaymentAdjustment(BusinessDayConvention convention) { |
179 | 0 | paymentAdjustment_ = convention; |
180 | 0 | return *this; |
181 | 0 | } |
182 | | |
183 | 0 | MultipleResetsLeg& MultipleResetsLeg::withPaymentCalendar(const Calendar& cal) { |
184 | 0 | paymentCalendar_ = cal; |
185 | 0 | return *this; |
186 | 0 | } |
187 | | |
188 | 0 | MultipleResetsLeg& MultipleResetsLeg::withPaymentLag(Integer lag) { |
189 | 0 | paymentLag_ = lag; |
190 | 0 | return *this; |
191 | 0 | } |
192 | | |
193 | 0 | MultipleResetsLeg& MultipleResetsLeg::withFixingDays(Natural fixingDays) { |
194 | 0 | fixingDays_ = std::vector<Natural>(1, fixingDays); |
195 | 0 | return *this; |
196 | 0 | } |
197 | | |
198 | 0 | MultipleResetsLeg& MultipleResetsLeg::withFixingDays(const std::vector<Natural>& fixingDays) { |
199 | 0 | fixingDays_ = fixingDays; |
200 | 0 | return *this; |
201 | 0 | } |
202 | | |
203 | 0 | MultipleResetsLeg& MultipleResetsLeg::withGearings(Real gearing) { |
204 | 0 | gearings_ = std::vector<Real>(1, gearing); |
205 | 0 | return *this; |
206 | 0 | } |
207 | | |
208 | 0 | MultipleResetsLeg& MultipleResetsLeg::withGearings(const std::vector<Real>& gearings) { |
209 | 0 | gearings_ = gearings; |
210 | 0 | return *this; |
211 | 0 | } |
212 | | |
213 | 0 | MultipleResetsLeg& MultipleResetsLeg::withCouponSpreads(Spread spread) { |
214 | 0 | couponSpreads_ = std::vector<Spread>(1, spread); |
215 | 0 | return *this; |
216 | 0 | } |
217 | | |
218 | 0 | MultipleResetsLeg& MultipleResetsLeg::withCouponSpreads(const std::vector<Spread>& spreads) { |
219 | 0 | couponSpreads_ = spreads; |
220 | 0 | return *this; |
221 | 0 | } |
222 | | |
223 | 0 | MultipleResetsLeg& MultipleResetsLeg::withRateSpreads(Spread spread) { |
224 | 0 | rateSpreads_ = std::vector<Spread>(1, spread); |
225 | 0 | return *this; |
226 | 0 | } |
227 | | |
228 | 0 | MultipleResetsLeg& MultipleResetsLeg::withRateSpreads(const std::vector<Spread>& spreads) { |
229 | 0 | rateSpreads_ = spreads; |
230 | 0 | return *this; |
231 | 0 | } |
232 | | |
233 | 0 | MultipleResetsLeg& MultipleResetsLeg::withAveragingMethod(RateAveraging::Type averagingMethod) { |
234 | 0 | averagingMethod_ = averagingMethod; |
235 | 0 | return *this; |
236 | 0 | } |
237 | | |
238 | | MultipleResetsLeg& MultipleResetsLeg::withExCouponPeriod(const Period& period, |
239 | | const Calendar& cal, |
240 | | BusinessDayConvention convention, |
241 | 0 | bool endOfMonth) { |
242 | 0 | exCouponPeriod_ = period; |
243 | 0 | exCouponCalendar_ = cal; |
244 | 0 | exCouponAdjustment_ = convention; |
245 | 0 | exCouponEndOfMonth_ = endOfMonth; |
246 | 0 | return *this; |
247 | 0 | } |
248 | | |
249 | 0 | MultipleResetsLeg::operator Leg() const { |
250 | 0 | Leg cashflows; |
251 | 0 | Calendar calendar = schedule_.calendar(); |
252 | |
|
253 | 0 | Size n = (schedule_.size() - 1) / resetsPerCoupon_; |
254 | 0 | QL_REQUIRE(!notionals_.empty(), "no notional given"); |
255 | 0 | QL_REQUIRE(notionals_.size() <= n, |
256 | 0 | "too many nominals (" << notionals_.size() << "), only " << n << " required"); |
257 | 0 | QL_REQUIRE(gearings_.size() <= n, |
258 | 0 | "too many gearings (" << gearings_.size() << "), only " << n << " required"); |
259 | 0 | QL_REQUIRE(couponSpreads_.size() <= n, |
260 | 0 | "too many coupon spreads (" << couponSpreads_.size() << "), only " << n << " required"); |
261 | 0 | QL_REQUIRE(rateSpreads_.size() <= n, |
262 | 0 | "too many rate spreads (" << rateSpreads_.size() << "), only " << n << " required"); |
263 | 0 | QL_REQUIRE(fixingDays_.size() <= n, |
264 | 0 | "too many fixing days (" << fixingDays_.size() << "), only " << n << " required"); |
265 | | |
266 | 0 | for (Size i = 0; i < n; ++i) { |
267 | 0 | Date start = schedule_.date(i * resetsPerCoupon_); |
268 | 0 | Date end = schedule_.date((i + 1) * resetsPerCoupon_); |
269 | 0 | auto subSchedule = schedule_.after(start).until(end); |
270 | 0 | Date paymentDate = paymentCalendar_.advance(end, paymentLag_, Days, paymentAdjustment_); |
271 | 0 | Date exCouponDate; |
272 | 0 | if (exCouponPeriod_ != Period()) { |
273 | 0 | if (exCouponCalendar_.empty()) { |
274 | 0 | exCouponDate = calendar.advance(paymentDate, -exCouponPeriod_, |
275 | 0 | exCouponAdjustment_, exCouponEndOfMonth_); |
276 | 0 | } else { |
277 | 0 | exCouponDate = exCouponCalendar_.advance( |
278 | 0 | paymentDate, -exCouponPeriod_, exCouponAdjustment_, exCouponEndOfMonth_); |
279 | 0 | } |
280 | 0 | } |
281 | |
|
282 | 0 | cashflows.push_back(ext::make_shared<MultipleResetsCoupon>( |
283 | 0 | paymentDate, detail::get(notionals_, i, notionals_.back()), subSchedule, |
284 | 0 | detail::get(fixingDays_, i, index_->fixingDays()), index_, |
285 | 0 | detail::get(gearings_, i, 1.0), detail::get(couponSpreads_, i, 0.0), |
286 | 0 | detail::get(rateSpreads_, i, 0.0), start, end, paymentDayCounter_, |
287 | 0 | exCouponDate)); |
288 | 0 | } |
289 | |
|
290 | 0 | switch (averagingMethod_) { |
291 | 0 | case RateAveraging::Simple: |
292 | 0 | setCouponPricer(cashflows, ext::make_shared<AveragingMultipleResetsPricer>()); |
293 | 0 | break; |
294 | 0 | case RateAveraging::Compound: |
295 | 0 | setCouponPricer(cashflows, ext::make_shared<CompoundingMultipleResetsPricer>()); |
296 | 0 | break; |
297 | 0 | default: |
298 | 0 | QL_FAIL("unknown compounding convention (" << Integer(averagingMethod_) << ")"); |
299 | 0 | } |
300 | 0 | return cashflows; |
301 | 0 | } |
302 | | |
303 | | } |