QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
actualactual.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#include <ql/time/daycounters/actualactual.hpp>
21#include <algorithm>
22#include <cmath>
23
24namespace QuantLib {
25
26 namespace {
27
28 // the template argument works around passing a protected type
29
30 template <class T>
31 Integer findCouponsPerYear(const T& impl,
32 Date refStart, Date refEnd) {
33 // This will only work for day counts longer than 15 days.
34 auto months = (Integer)std::lround(12 * Real(impl.dayCount(refStart, refEnd)) / 365.0);
35 return (Integer)std::lround(12.0 / Real(months));
36 }
37
38 /* An ISMA day counter either needs a schedule or to have
39 been explicitly passed a reference period. This usage
40 leads to inaccurate year fractions.
41 */
42 template <class T>
43 Time yearFractionGuess(const T& impl,
44 const Date& start, const Date& end) {
45 // asymptotically correct.
46 return Real(impl.dayCount(start, end)) / 365.0;
47 }
48
49 std::vector<Date> getListOfPeriodDatesIncludingQuasiPayments(
50 const Schedule& schedule) {
51 // Process the schedule into an array of dates.
52 Date issueDate = schedule.date(0);
53 std::vector<Date> newDates = schedule.dates();
54
55 if (!schedule.hasIsRegular() || !schedule.isRegular(1))
56 {
57 Date firstCoupon = schedule.date(1);
58
59 Date notionalFirstCoupon =
60 schedule.calendar().advance(firstCoupon,
61 -schedule.tenor(),
62 schedule.businessDayConvention(),
63 schedule.endOfMonth());
64
65 newDates[0] = notionalFirstCoupon;
66
67 //long first coupon
68 if (notionalFirstCoupon > issueDate) {
69 Date priorNotionalCoupon =
70 schedule.calendar().advance(notionalFirstCoupon,
71 -schedule.tenor(),
72 schedule.businessDayConvention(),
73 schedule.endOfMonth());
74 newDates.insert(newDates.begin(),
75 priorNotionalCoupon); //insert as the first element?
76 }
77 }
78
79 if (!schedule.hasIsRegular() || !schedule.isRegular(schedule.size() - 1))
80 {
81 Date notionalLastCoupon =
82 schedule.calendar().advance(schedule.date(schedule.size() - 2),
83 schedule.tenor(),
84 schedule.businessDayConvention(),
85 schedule.endOfMonth());
86
87 newDates[schedule.size() - 1] = notionalLastCoupon;
88
89 if (notionalLastCoupon < schedule.endDate())
90 {
91 Date nextNotionalCoupon =
92 schedule.calendar().advance(notionalLastCoupon,
93 schedule.tenor(),
94 schedule.businessDayConvention(),
95 schedule.endOfMonth());
96 newDates.push_back(nextNotionalCoupon);
97 }
98 }
99
100 return newDates;
101 }
102
103 template <class T>
104 Time yearFractionWithReferenceDates(const T& impl,
105 const Date& d1, const Date& d2,
106 const Date& d3, const Date& d4) {
107 QL_REQUIRE(d1 <= d2,
108 "This function is only correct if d1 <= d2\n"
109 "d1: " << d1 << " d2: " << d2);
110
111 Real referenceDayCount = Real(impl.dayCount(d3, d4));
112 //guess how many coupon periods per year:
113 Integer couponsPerYear;
114 if (referenceDayCount < 16) {
115 couponsPerYear = 1;
116 referenceDayCount = impl.dayCount(d1, d1 + 1 * Years);
117 }
118 else {
119 couponsPerYear = findCouponsPerYear(impl, d3, d4);
120 }
121 return Real(impl.dayCount(d1, d2)) / (referenceDayCount*couponsPerYear);
122 }
123
124 }
125
126 ext::shared_ptr<DayCounter::Impl>
128 const Schedule& schedule) {
129 switch (c) {
130 case ISMA:
131 case Bond:
132 if (!schedule.empty())
133 return ext::shared_ptr<DayCounter::Impl>(new ISMA_Impl(schedule));
134 else
135 return ext::shared_ptr<DayCounter::Impl>(new Old_ISMA_Impl);
136 case ISDA:
137 case Historical:
138 case Actual365:
139 return ext::shared_ptr<DayCounter::Impl>(new ISDA_Impl);
140 case AFB:
141 case Euro:
142 return ext::shared_ptr<DayCounter::Impl>(new AFB_Impl);
143 default:
144 QL_FAIL("unknown act/act convention");
145 }
146 }
147
148
150 const Date& d2,
151 const Date& d3,
152 const Date& d4) const {
153 if (d1 == d2) {
154 return 0.0;
155 } else if (d2 < d1) {
156 return -yearFraction(d2, d1, d3, d4);
157 }
158
159 std::vector<Date> couponDates =
160 getListOfPeriodDatesIncludingQuasiPayments(schedule_);
161
162 Date firstDate = *std::min_element(couponDates.begin(), couponDates.end());
163 Date lastDate = *std::max_element(couponDates.begin(), couponDates.end());
164
165 QL_REQUIRE(d1 >= firstDate && d2 <= lastDate, "Dates out of range of schedule: "
166 << "date 1: " << d1 << ", date 2: " << d2 << ", first date: "
167 << firstDate << ", last date: " << lastDate);
168
169 Real yearFractionSum = 0.0;
170 for (Size i = 0; i < couponDates.size() - 1; i++) {
171 Date startReferencePeriod = couponDates[i];
172 Date endReferencePeriod = couponDates[i + 1];
173 if (d1 < endReferencePeriod && d2 > startReferencePeriod) {
174 yearFractionSum +=
175 yearFractionWithReferenceDates(*this,
176 std::max(d1, startReferencePeriod),
177 std::min(d2, endReferencePeriod),
178 startReferencePeriod,
179 endReferencePeriod);
180 }
181 }
182 return yearFractionSum;
183 }
184
185
187 const Date& d2,
188 const Date& d3,
189 const Date& d4) const {
190 if (d1 == d2)
191 return 0.0;
192
193 if (d1 > d2)
194 return -yearFraction(d2,d1,d3,d4);
195
196 // when the reference period is not specified, try taking
197 // it equal to (d1,d2)
198 Date refPeriodStart = (d3 != Date() ? d3 : d1);
199 Date refPeriodEnd = (d4 != Date() ? d4 : d2);
200
201 QL_REQUIRE(refPeriodEnd > refPeriodStart && refPeriodEnd > d1,
202 "invalid reference period: "
203 << "date 1: " << d1
204 << ", date 2: " << d2
205 << ", reference period start: " << refPeriodStart
206 << ", reference period end: " << refPeriodEnd);
207
208 // estimate roughly the length in months of a period
209 auto months = (Integer)std::lround(12 * Real(refPeriodEnd - refPeriodStart) / 365);
210
211 // for short periods...
212 if (months == 0) {
213 // ...take the reference period as 1 year from d1
214 refPeriodStart = d1;
215 refPeriodEnd = d1 + 1*Years;
216 months = 12;
217 }
218
219 Time period = Real(months)/12.0;
220
221 if (d2 <= refPeriodEnd) {
222 // here refPeriodEnd is a future (notional?) payment date
223 if (d1 >= refPeriodStart) {
224 // here refPeriodStart is the last (maybe notional)
225 // payment date.
226 // refPeriodStart <= d1 <= d2 <= refPeriodEnd
227 // [maybe the equality should be enforced, since
228 // refPeriodStart < d1 <= d2 < refPeriodEnd
229 // could give wrong results] ???
230 return period*Real(daysBetween(d1,d2)) /
231 daysBetween(refPeriodStart,refPeriodEnd);
232 } else {
233 // here refPeriodStart is the next (maybe notional)
234 // payment date and refPeriodEnd is the second next
235 // (maybe notional) payment date.
236 // d1 < refPeriodStart < refPeriodEnd
237 // AND d2 <= refPeriodEnd
238 // this case is long first coupon
239
240 // the last notional payment date
241 Date previousRef = refPeriodStart - months*Months;
242
243 if (d2 > refPeriodStart)
244 return yearFraction(d1, refPeriodStart, previousRef,
245 refPeriodStart) +
246 yearFraction(refPeriodStart, d2, refPeriodStart,
247 refPeriodEnd);
248 else
249 return yearFraction(d1,d2,previousRef,refPeriodStart);
250 }
251 } else {
252 // here refPeriodEnd is the last (notional?) payment date
253 // d1 < refPeriodEnd < d2 AND refPeriodStart < refPeriodEnd
254 QL_REQUIRE(refPeriodStart<=d1,
255 "invalid dates: "
256 "d1 < refPeriodStart < refPeriodEnd < d2");
257 // now it is: refPeriodStart <= d1 < refPeriodEnd < d2
258
259 // the part from d1 to refPeriodEnd
260 Time sum = yearFraction(d1, refPeriodEnd,
261 refPeriodStart, refPeriodEnd);
262
263 // the part from refPeriodEnd to d2
264 // count how many regular periods are in [refPeriodEnd, d2],
265 // then add the remaining time
266 Integer i=0;
267 Date newRefStart, newRefEnd;
268 for (;;) {
269 newRefStart = refPeriodEnd + (months*i)*Months;
270 newRefEnd = refPeriodEnd + (months*(i+1))*Months;
271 if (d2 < newRefEnd) {
272 break;
273 } else {
274 sum += period;
275 i++;
276 }
277 }
278 sum += yearFraction(newRefStart,d2,newRefStart,newRefEnd);
279 return sum;
280 }
281 }
282
283
285 const Date& d2,
286 const Date&,
287 const Date&) const {
288 if (d1 == d2)
289 return 0.0;
290
291 if (d1 > d2)
292 return -yearFraction(d2,d1,Date(),Date());
293
294 Integer y1 = d1.year(), y2 = d2.year();
295 Real dib1 = (Date::isLeap(y1) ? 366.0 : 365.0),
296 dib2 = (Date::isLeap(y2) ? 366.0 : 365.0);
297
298 Time sum = y2 - y1 - 1;
299 sum += daysBetween(d1, Date(1,January,y1+1))/dib1;
300 sum += daysBetween(Date(1,January,y2),d2)/dib2;
301 return sum;
302 }
303
304
306 const Date& d2,
307 const Date&,
308 const Date&) const {
309 if (d1 == d2)
310 return 0.0;
311
312 if (d1 > d2)
313 return -yearFraction(d2,d1,Date(),Date());
314
315 Date newD2=d2, temp=d2;
316 Time sum = 0.0;
317 while (temp > d1) {
318 temp = newD2 - 1*Years;
319 if (temp.dayOfMonth()==28 && temp.month()==2
320 && Date::isLeap(temp.year())) {
321 temp += 1;
322 }
323 if (temp>=d1) {
324 sum += 1.0;
325 newD2 = temp;
326 }
327 }
328
329 Real den = 365.0;
330
331 if (Date::isLeap(newD2.year())) {
332 temp = Date(29, February, newD2.year());
333 if (newD2>temp && d1<=temp)
334 den += 1.0;
335 } else if (Date::isLeap(d1.year())) {
336 temp = Date(29, February, d1.year());
337 if (newD2>temp && d1<=temp)
338 den += 1.0;
339 }
340
341 return sum+daysBetween(d1, newD2)/den;
342 }
343
344}
Time yearFraction(const Date &d1, const Date &d2, const Date &, const Date &) const override
Time yearFraction(const Date &d1, const Date &d2, const Date &, const Date &) const override
Time yearFraction(const Date &d1, const Date &d2, const Date &refPeriodStart, const Date &refPeriodEnd) const override
Time yearFraction(const Date &d1, const Date &d2, const Date &refPeriodStart, const Date &refPeriodEnd) const override
static ext::shared_ptr< DayCounter::Impl > implementation(Convention c, const Schedule &schedule)
Concrete date class.
Definition: date.hpp:125
Year year() const
Definition: date.cpp:93
static bool isLeap(Year y)
whether the given year is a leap one
Definition: date.cpp:187
Time yearFraction(const Date &, const Date &, const Date &refPeriodStart=Date(), const Date &refPeriodEnd=Date()) const
Returns the period between two dates as a fraction of year.
Definition: daycounter.hpp:128
Payment schedule.
Definition: schedule.hpp:40
bool empty() const
Definition: schedule.hpp:82
@ January
Definition: date.hpp:57
@ February
Definition: date.hpp:58
Real Time
continuous quantity with 1-year units
Definition: types.hpp:62
QL_REAL Real
real number
Definition: types.hpp:50
QL_INTEGER Integer
integer number
Definition: types.hpp:35
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:35
Real months(const Period &p)
Definition: period.cpp:296
Time daysBetween(const Date &d1, const Date &d2)
Definition: date.hpp:442