QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.34
Loading...
Searching...
No Matches
actualactual.cpp
Go to the documentation of this file.
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
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 switch (c) {
129 case ISMA:
130 case Bond:
131 if (!schedule.empty())
132 return ext::shared_ptr<DayCounter::Impl>(new ISMA_Impl(std::move(schedule)));
133 else
134 return ext::shared_ptr<DayCounter::Impl>(new Old_ISMA_Impl);
135 case ISDA:
136 case Historical:
137 case Actual365:
138 return ext::shared_ptr<DayCounter::Impl>(new ISDA_Impl);
139 case AFB:
140 case Euro:
141 return ext::shared_ptr<DayCounter::Impl>(new AFB_Impl);
142 default:
143 QL_FAIL("unknown act/act convention");
144 }
145 }
146
147
149 const Date& d2,
150 const Date& d3,
151 const Date& d4) const {
152 if (d1 == d2) {
153 return 0.0;
154 } else if (d2 < d1) {
155 return -yearFraction(d2, d1, d3, d4);
156 }
157
158 std::vector<Date> couponDates =
159 getListOfPeriodDatesIncludingQuasiPayments(schedule_);
160
161 Date firstDate = *std::min_element(couponDates.begin(), couponDates.end());
162 Date lastDate = *std::max_element(couponDates.begin(), couponDates.end());
163
164 QL_REQUIRE(d1 >= firstDate && d2 <= lastDate, "Dates out of range of schedule: "
165 << "date 1: " << d1 << ", date 2: " << d2 << ", first date: "
166 << firstDate << ", last date: " << lastDate);
167
168 Real yearFractionSum = 0.0;
169 for (Size i = 0; i < couponDates.size() - 1; i++) {
170 Date startReferencePeriod = couponDates[i];
171 Date endReferencePeriod = couponDates[i + 1];
172 if (d1 < endReferencePeriod && d2 > startReferencePeriod) {
173 yearFractionSum +=
174 yearFractionWithReferenceDates(*this,
175 std::max(d1, startReferencePeriod),
176 std::min(d2, endReferencePeriod),
177 startReferencePeriod,
178 endReferencePeriod);
179 }
180 }
181 return yearFractionSum;
182 }
183
184
186 const Date& d2,
187 const Date& d3,
188 const Date& d4) const {
189 if (d1 == d2)
190 return 0.0;
191
192 if (d1 > d2)
193 return -yearFraction(d2,d1,d3,d4);
194
195 // when the reference period is not specified, try taking
196 // it equal to (d1,d2)
197 Date refPeriodStart = (d3 != Date() ? d3 : d1);
198 Date refPeriodEnd = (d4 != Date() ? d4 : d2);
199
200 QL_REQUIRE(refPeriodEnd > refPeriodStart && refPeriodEnd > d1,
201 "invalid reference period: "
202 << "date 1: " << d1
203 << ", date 2: " << d2
204 << ", reference period start: " << refPeriodStart
205 << ", reference period end: " << refPeriodEnd);
206
207 // estimate roughly the length in months of a period
208 auto months = (Integer)std::lround(12 * Real(refPeriodEnd - refPeriodStart) / 365);
209
210 // for short periods...
211 if (months == 0) {
212 // ...take the reference period as 1 year from d1
213 refPeriodStart = d1;
214 refPeriodEnd = d1 + 1*Years;
215 months = 12;
216 }
217
218 Time period = Real(months)/12.0;
219
220 if (d2 <= refPeriodEnd) {
221 // here refPeriodEnd is a future (notional?) payment date
222 if (d1 >= refPeriodStart) {
223 // here refPeriodStart is the last (maybe notional)
224 // payment date.
225 // refPeriodStart <= d1 <= d2 <= refPeriodEnd
226 // [maybe the equality should be enforced, since
227 // refPeriodStart < d1 <= d2 < refPeriodEnd
228 // could give wrong results] ???
229 return period*Real(daysBetween(d1,d2)) /
230 daysBetween(refPeriodStart,refPeriodEnd);
231 } else {
232 // here refPeriodStart is the next (maybe notional)
233 // payment date and refPeriodEnd is the second next
234 // (maybe notional) payment date.
235 // d1 < refPeriodStart < refPeriodEnd
236 // AND d2 <= refPeriodEnd
237 // this case is long first coupon
238
239 // the last notional payment date
240 Date previousRef = refPeriodStart - months*Months;
241
242 if (d2 > refPeriodStart)
243 return yearFraction(d1, refPeriodStart, previousRef,
244 refPeriodStart) +
245 yearFraction(refPeriodStart, d2, refPeriodStart,
246 refPeriodEnd);
247 else
248 return yearFraction(d1,d2,previousRef,refPeriodStart);
249 }
250 } else {
251 // here refPeriodEnd is the last (notional?) payment date
252 // d1 < refPeriodEnd < d2 AND refPeriodStart < refPeriodEnd
253 QL_REQUIRE(refPeriodStart<=d1,
254 "invalid dates: "
255 "d1 < refPeriodStart < refPeriodEnd < d2");
256 // now it is: refPeriodStart <= d1 < refPeriodEnd < d2
257
258 // the part from d1 to refPeriodEnd
259 Time sum = yearFraction(d1, refPeriodEnd,
260 refPeriodStart, refPeriodEnd);
261
262 // the part from refPeriodEnd to d2
263 // count how many regular periods are in [refPeriodEnd, d2],
264 // then add the remaining time
265 Integer i=0;
266 Date newRefStart, newRefEnd;
267 for (;;) {
268 newRefStart = refPeriodEnd + (months*i)*Months;
269 newRefEnd = refPeriodEnd + (months*(i+1))*Months;
270 if (d2 < newRefEnd) {
271 break;
272 } else {
273 sum += period;
274 i++;
275 }
276 }
277 sum += yearFraction(newRefStart,d2,newRefStart,newRefEnd);
278 return sum;
279 }
280 }
281
282
284 const Date& d2,
285 const Date&,
286 const Date&) const {
287 if (d1 == d2)
288 return 0.0;
289
290 if (d1 > d2)
291 return -yearFraction(d2,d1,Date(),Date());
292
293 Integer y1 = d1.year(), y2 = d2.year();
294 Real dib1 = (Date::isLeap(y1) ? 366.0 : 365.0),
295 dib2 = (Date::isLeap(y2) ? 366.0 : 365.0);
296
297 Time sum = y2 - y1 - 1;
298 sum += daysBetween(d1, Date(1,January,y1+1))/dib1;
299 sum += daysBetween(Date(1,January,y2),d2)/dib2;
300 return sum;
301 }
302
303
305 const Date& d2,
306 const Date&,
307 const Date&) const {
308 if (d1 == d2)
309 return 0.0;
310
311 if (d1 > d2)
312 return -yearFraction(d2,d1,Date(),Date());
313
314 Date newD2=d2, temp=d2;
315 Time sum = 0.0;
316 while (temp > d1) {
317 temp = newD2 - 1*Years;
318 if (temp.dayOfMonth()==28 && temp.month()==2
319 && Date::isLeap(temp.year())) {
320 temp += 1;
321 }
322 if (temp>=d1) {
323 sum += 1.0;
324 newD2 = temp;
325 }
326 }
327
328 Real den = 365.0;
329
330 if (Date::isLeap(newD2.year())) {
331 temp = Date(29, February, newD2.year());
332 if (newD2>temp && d1<=temp)
333 den += 1.0;
334 } else if (Date::isLeap(d1.year())) {
335 temp = Date(29, February, d1.year());
336 if (newD2>temp && d1<=temp)
337 den += 1.0;
338 }
339
340 return sum+daysBetween(d1, newD2)/den;
341 }
342
343}
act/act day counters
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, 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
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
#define QL_FAIL(message)
throw an error (possibly with file and line information)
Definition: errors.hpp:92
@ 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