| 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 | Copyright (C) 2003, 2004, 2005, 2006, 2007 StatPro Italia srl |
| 6 | Copyright (C) 2004 Jeff Yu |
| 7 | Copyright (C) 2014 Paolo Mazzocchi |
| 8 | Copyright (C) 2020 Leonardo Arcari |
| 9 | Copyright (C) 2020 Kline s.r.l. |
| 10 | |
| 11 | This file is part of QuantLib, a free-software/open-source library |
| 12 | for financial quantitative analysts and developers - http://quantlib.org/ |
| 13 | |
| 14 | QuantLib is free software: you can redistribute it and/or modify it |
| 15 | under the terms of the QuantLib license. You should have received a |
| 16 | copy of the license along with this program; if not, please email |
| 17 | <quantlib-dev@lists.sf.net>. The license is also available online at |
| 18 | <http://quantlib.org/license.shtml>. |
| 19 | |
| 20 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 21 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 22 | FOR A PARTICULAR PURPOSE. See the license for more details. |
| 23 | */ |
| 24 | |
| 25 | #include <ql/time/calendar.hpp> |
| 26 | #include <ql/errors.hpp> |
| 27 | |
| 28 | namespace QuantLib { |
| 29 | |
| 30 | namespace { |
| 31 | |
| 32 | // Requires: from < to. |
| 33 | Date::serial_type daysBetweenImpl(const Calendar& cal, |
| 34 | const Date& from, const Date& to, |
| 35 | bool includeFirst, bool includeLast) { |
| 36 | auto res = static_cast<Date::serial_type>(includeLast && cal.isBusinessDay(d: to)); |
| 37 | for (Date d = includeFirst ? from : from + 1; d < to; ++d) { |
| 38 | res += static_cast<Date::serial_type>(cal.isBusinessDay(d)); |
| 39 | } |
| 40 | return res; |
| 41 | } |
| 42 | |
| 43 | } |
| 44 | |
| 45 | void Calendar::addHoliday(const Date& d) { |
| 46 | QL_REQUIRE(impl_, "no calendar implementation provided" ); |
| 47 | |
| 48 | #ifdef QL_HIGH_RESOLUTION_DATE |
| 49 | const Date _d(d.dayOfMonth(), d.month(), d.year()); |
| 50 | #else |
| 51 | const Date& _d = d; |
| 52 | #endif |
| 53 | |
| 54 | // if d was a genuine holiday previously removed, revert the change |
| 55 | impl_->removedHolidays.erase(x: _d); |
| 56 | // if it's already a holiday, leave the calendar alone. |
| 57 | // Otherwise, add it. |
| 58 | if (impl_->isBusinessDay(_d)) |
| 59 | impl_->addedHolidays.insert(x: _d); |
| 60 | } |
| 61 | |
| 62 | void Calendar::removeHoliday(const Date& d) { |
| 63 | QL_REQUIRE(impl_, "no calendar implementation provided" ); |
| 64 | |
| 65 | #ifdef QL_HIGH_RESOLUTION_DATE |
| 66 | const Date _d(d.dayOfMonth(), d.month(), d.year()); |
| 67 | #else |
| 68 | const Date& _d = d; |
| 69 | #endif |
| 70 | |
| 71 | // if d was an artificially-added holiday, revert the change |
| 72 | impl_->addedHolidays.erase(x: _d); |
| 73 | // if it's already a business day, leave the calendar alone. |
| 74 | // Otherwise, add it. |
| 75 | if (!impl_->isBusinessDay(_d)) |
| 76 | impl_->removedHolidays.insert(x: _d); |
| 77 | } |
| 78 | |
| 79 | void Calendar::resetAddedAndRemovedHolidays() { |
| 80 | impl_->addedHolidays.clear(); |
| 81 | impl_->removedHolidays.clear(); |
| 82 | } |
| 83 | |
| 84 | Date Calendar::adjust(const Date& d, |
| 85 | BusinessDayConvention c) const { |
| 86 | QL_REQUIRE(d != Date(), "null date" ); |
| 87 | |
| 88 | if (c == Unadjusted) |
| 89 | return d; |
| 90 | |
| 91 | Date d1 = d; |
| 92 | if (c == Following || c == ModifiedFollowing |
| 93 | || c == HalfMonthModifiedFollowing) { |
| 94 | while (isHoliday(d: d1)) |
| 95 | ++d1; |
| 96 | if (c == ModifiedFollowing |
| 97 | || c == HalfMonthModifiedFollowing) { |
| 98 | if (d1.month() != d.month()) { |
| 99 | return adjust(d, c: Preceding); |
| 100 | } |
| 101 | if (c == HalfMonthModifiedFollowing) { |
| 102 | if (d.dayOfMonth() <= 15 && d1.dayOfMonth() > 15) { |
| 103 | return adjust(d, c: Preceding); |
| 104 | } |
| 105 | } |
| 106 | } |
| 107 | } else if (c == Preceding || c == ModifiedPreceding) { |
| 108 | while (isHoliday(d: d1)) |
| 109 | --d1; |
| 110 | if (c == ModifiedPreceding && d1.month() != d.month()) { |
| 111 | return adjust(d,c: Following); |
| 112 | } |
| 113 | } else if (c == Nearest) { |
| 114 | Date d2 = d; |
| 115 | while (isHoliday(d: d1) && isHoliday(d: d2)) |
| 116 | { |
| 117 | ++d1; |
| 118 | --d2; |
| 119 | } |
| 120 | if (isHoliday(d: d1)) |
| 121 | return d2; |
| 122 | else |
| 123 | return d1; |
| 124 | } else { |
| 125 | QL_FAIL("unknown business-day convention" ); |
| 126 | } |
| 127 | return d1; |
| 128 | } |
| 129 | |
| 130 | Date Calendar::advance(const Date& d, |
| 131 | Integer n, TimeUnit unit, |
| 132 | BusinessDayConvention c, |
| 133 | bool endOfMonth) const { |
| 134 | QL_REQUIRE(d!=Date(), "null date" ); |
| 135 | if (n == 0) { |
| 136 | return adjust(d,c); |
| 137 | } else if (unit == Days) { |
| 138 | Date d1 = d; |
| 139 | if (n > 0) { |
| 140 | while (n > 0) { |
| 141 | ++d1; |
| 142 | while (isHoliday(d: d1)) |
| 143 | ++d1; |
| 144 | --n; |
| 145 | } |
| 146 | } else { |
| 147 | while (n < 0) { |
| 148 | --d1; |
| 149 | while(isHoliday(d: d1)) |
| 150 | --d1; |
| 151 | ++n; |
| 152 | } |
| 153 | } |
| 154 | return d1; |
| 155 | } else if (unit == Weeks) { |
| 156 | Date d1 = d + n*unit; |
| 157 | return adjust(d: d1,c); |
| 158 | } else { |
| 159 | Date d1 = d + n*unit; |
| 160 | |
| 161 | // we are sure the unit is Months or Years |
| 162 | if (endOfMonth && isEndOfMonth(d)) |
| 163 | return Calendar::endOfMonth(d: d1); |
| 164 | |
| 165 | return adjust(d: d1, c); |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | Date Calendar::advance(const Date & d, |
| 170 | const Period & p, |
| 171 | BusinessDayConvention c, |
| 172 | bool endOfMonth) const { |
| 173 | return advance(d, n: p.length(), unit: p.units(), c, endOfMonth); |
| 174 | } |
| 175 | |
| 176 | Date::serial_type Calendar::businessDaysBetween(const Date& from, |
| 177 | const Date& to, |
| 178 | bool includeFirst, |
| 179 | bool includeLast) const { |
| 180 | return (from < to) ? daysBetweenImpl(cal: *this, from, to, includeFirst, includeLast) : |
| 181 | (from > to) ? -daysBetweenImpl(cal: *this, from: to, to: from, includeFirst: includeLast, includeLast: includeFirst) : |
| 182 | Date::serial_type(includeFirst && includeLast && isBusinessDay(d: from)); |
| 183 | } |
| 184 | |
| 185 | |
| 186 | |
| 187 | // Western calendars |
| 188 | |
| 189 | bool Calendar::WesternImpl::isWeekend(Weekday w) const { |
| 190 | return w == Saturday || w == Sunday; |
| 191 | } |
| 192 | |
| 193 | Day Calendar::WesternImpl::easterMonday(Year y) { |
| 194 | static const Day EasterMonday[] = { |
| 195 | 98, 90, 103, 95, 114, 106, 91, 111, 102, // 1901-1909 |
| 196 | 87, 107, 99, 83, 103, 95, 115, 99, 91, 111, // 1910-1919 |
| 197 | 96, 87, 107, 92, 112, 103, 95, 108, 100, 91, // 1920-1929 |
| 198 | 111, 96, 88, 107, 92, 112, 104, 88, 108, 100, // 1930-1939 |
| 199 | 85, 104, 96, 116, 101, 92, 112, 97, 89, 108, // 1940-1949 |
| 200 | 100, 85, 105, 96, 109, 101, 93, 112, 97, 89, // 1950-1959 |
| 201 | 109, 93, 113, 105, 90, 109, 101, 86, 106, 97, // 1960-1969 |
| 202 | 89, 102, 94, 113, 105, 90, 110, 101, 86, 106, // 1970-1979 |
| 203 | 98, 110, 102, 94, 114, 98, 90, 110, 95, 86, // 1980-1989 |
| 204 | 106, 91, 111, 102, 94, 107, 99, 90, 103, 95, // 1990-1999 |
| 205 | 115, 106, 91, 111, 103, 87, 107, 99, 84, 103, // 2000-2009 |
| 206 | 95, 115, 100, 91, 111, 96, 88, 107, 92, 112, // 2010-2019 |
| 207 | 104, 95, 108, 100, 92, 111, 96, 88, 108, 92, // 2020-2029 |
| 208 | 112, 104, 89, 108, 100, 85, 105, 96, 116, 101, // 2030-2039 |
| 209 | 93, 112, 97, 89, 109, 100, 85, 105, 97, 109, // 2040-2049 |
| 210 | 101, 93, 113, 97, 89, 109, 94, 113, 105, 90, // 2050-2059 |
| 211 | 110, 101, 86, 106, 98, 89, 102, 94, 114, 105, // 2060-2069 |
| 212 | 90, 110, 102, 86, 106, 98, 111, 102, 94, 114, // 2070-2079 |
| 213 | 99, 90, 110, 95, 87, 106, 91, 111, 103, 94, // 2080-2089 |
| 214 | 107, 99, 91, 103, 95, 115, 107, 91, 111, 103, // 2090-2099 |
| 215 | 88, 108, 100, 85, 105, 96, 109, 101, 93, 112, // 2100-2109 |
| 216 | 97, 89, 109, 93, 113, 105, 90, 109, 101, 86, // 2110-2119 |
| 217 | 106, 97, 89, 102, 94, 113, 105, 90, 110, 101, // 2120-2129 |
| 218 | 86, 106, 98, 110, 102, 94, 114, 98, 90, 110, // 2130-2139 |
| 219 | 95, 86, 106, 91, 111, 102, 94, 107, 99, 90, // 2140-2149 |
| 220 | 103, 95, 115, 106, 91, 111, 103, 87, 107, 99, // 2150-2159 |
| 221 | 84, 103, 95, 115, 100, 91, 111, 96, 88, 107, // 2160-2169 |
| 222 | 92, 112, 104, 95, 108, 100, 92, 111, 96, 88, // 2170-2179 |
| 223 | 108, 92, 112, 104, 89, 108, 100, 85, 105, 96, // 2180-2189 |
| 224 | 116, 101, 93, 112, 97, 89, 109, 100, 85, 105 // 2190-2199 |
| 225 | }; |
| 226 | return EasterMonday[y-1901]; |
| 227 | } |
| 228 | |
| 229 | // Orthodox calendars |
| 230 | |
| 231 | bool Calendar::OrthodoxImpl::isWeekend(Weekday w) const { |
| 232 | return w == Saturday || w == Sunday; |
| 233 | } |
| 234 | |
| 235 | Day Calendar::OrthodoxImpl::easterMonday(Year y) { |
| 236 | static const Day EasterMonday[] = { |
| 237 | 105, 118, 110, 102, 121, 106, 126, 118, 102, // 1901-1909 |
| 238 | 122, 114, 99, 118, 110, 95, 115, 106, 126, 111, // 1910-1919 |
| 239 | 103, 122, 107, 99, 119, 110, 123, 115, 107, 126, // 1920-1929 |
| 240 | 111, 103, 123, 107, 99, 119, 104, 123, 115, 100, // 1930-1939 |
| 241 | 120, 111, 96, 116, 108, 127, 112, 104, 124, 115, // 1940-1949 |
| 242 | 100, 120, 112, 96, 116, 108, 128, 112, 104, 124, // 1950-1959 |
| 243 | 109, 100, 120, 105, 125, 116, 101, 121, 113, 104, // 1960-1969 |
| 244 | 117, 109, 101, 120, 105, 125, 117, 101, 121, 113, // 1970-1979 |
| 245 | 98, 117, 109, 129, 114, 105, 125, 110, 102, 121, // 1980-1989 |
| 246 | 106, 98, 118, 109, 122, 114, 106, 118, 110, 102, // 1990-1999 |
| 247 | 122, 106, 126, 118, 103, 122, 114, 99, 119, 110, // 2000-2009 |
| 248 | 95, 115, 107, 126, 111, 103, 123, 107, 99, 119, // 2010-2019 |
| 249 | 111, 123, 115, 107, 127, 111, 103, 123, 108, 99, // 2020-2029 |
| 250 | 119, 104, 124, 115, 100, 120, 112, 96, 116, 108, // 2030-2039 |
| 251 | 128, 112, 104, 124, 116, 100, 120, 112, 97, 116, // 2040-2049 |
| 252 | 108, 128, 113, 104, 124, 109, 101, 120, 105, 125, // 2050-2059 |
| 253 | 117, 101, 121, 113, 105, 117, 109, 101, 121, 105, // 2060-2069 |
| 254 | 125, 110, 102, 121, 113, 98, 118, 109, 129, 114, // 2070-2079 |
| 255 | 106, 125, 110, 102, 122, 106, 98, 118, 110, 122, // 2080-2089 |
| 256 | 114, 99, 119, 110, 102, 115, 107, 126, 118, 103, // 2090-2099 |
| 257 | 123, 115, 100, 120, 112, 96, 116, 108, 128, 112, // 2100-2109 |
| 258 | 104, 124, 109, 100, 120, 105, 125, 116, 108, 121, // 2110-2119 |
| 259 | 113, 104, 124, 109, 101, 120, 105, 125, 117, 101, // 2120-2129 |
| 260 | 121, 113, 98, 117, 109, 129, 114, 105, 125, 110, // 2130-2139 |
| 261 | 102, 121, 113, 98, 118, 109, 129, 114, 106, 125, // 2140-2149 |
| 262 | 110, 102, 122, 106, 126, 118, 103, 122, 114, 99, // 2150-2159 |
| 263 | 119, 110, 102, 115, 107, 126, 111, 103, 123, 114, // 2160-2169 |
| 264 | 99, 119, 111, 130, 115, 107, 127, 111, 103, 123, // 2170-2179 |
| 265 | 108, 99, 119, 104, 124, 115, 100, 120, 112, 103, // 2180-2189 |
| 266 | 116, 108, 128, 119, 104, 124, 116, 100, 120, 112 // 2190-2199 |
| 267 | }; |
| 268 | return EasterMonday[y-1901]; |
| 269 | } |
| 270 | |
| 271 | std::vector<Date> Calendar::holidayList( |
| 272 | const Date& from, const Date& to, bool includeWeekEnds) const { |
| 273 | |
| 274 | QL_REQUIRE(to>=from, "'from' date (" |
| 275 | << from << ") must be equal to or earlier than 'to' date (" |
| 276 | << to << ")" ); |
| 277 | std::vector<Date> result; |
| 278 | for (Date d = from; d <= to; ++d) { |
| 279 | if (isHoliday(d) && (includeWeekEnds || !isWeekend(w: d.weekday()))) |
| 280 | result.push_back(x: d); |
| 281 | } |
| 282 | return result; |
| 283 | } |
| 284 | |
| 285 | std::vector<Date> Calendar::businessDayList( |
| 286 | const Date& from, const Date& to) const { |
| 287 | |
| 288 | QL_REQUIRE(to>=from, "'from' date (" |
| 289 | << from << ") must be equal to or earlier than 'to' date (" |
| 290 | << to << ")" ); |
| 291 | std::vector<Date> result; |
| 292 | for (Date d = from; d <= to; ++d) { |
| 293 | if (isBusinessDay(d)) |
| 294 | result.push_back(x: d); |
| 295 | } |
| 296 | return result; |
| 297 | } |
| 298 | } |
| 299 | |