| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2009, 2011 Ferdinando Ametrano |
| 5 | Copyright (C) 2015 Paolo Mazzocchi |
| 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 | <http://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/time/ecb.hpp> |
| 22 | #include <ql/settings.hpp> |
| 23 | #include <ql/utilities/dataparsers.hpp> |
| 24 | #include <boost/algorithm/string/case_conv.hpp> |
| 25 | #include <algorithm> |
| 26 | #include <string> |
| 27 | |
| 28 | using boost::algorithm::to_upper_copy; |
| 29 | using std::string; |
| 30 | |
| 31 | namespace QuantLib { |
| 32 | |
| 33 | namespace detail { |
| 34 | static std::set<Date> ecbKnownDateSet = { |
| 35 | Date(38371), Date(38391), Date(38420), Date(38455), Date(38483), Date(38511), |
| 36 | Date(38546), Date(38574), Date(38602), Date(38637), Date(38665), |
| 37 | Date(38692), // 2005 |
| 38 | Date(38735), Date(38756), Date(38784), Date(38819), Date(38847), Date(38883), |
| 39 | Date(38910), Date(38938), Date(38966), Date(39001), Date(39029), |
| 40 | Date(39064), // 2006 |
| 41 | Date(39099), Date(39127), Date(39155), Date(39190), Date(39217), Date(39246), |
| 42 | Date(39274), Date(39302), Date(39337), Date(39365), Date(39400), |
| 43 | Date(39428), // 2007 |
| 44 | Date(39463), Date(39491), Date(39519), Date(39554), Date(39582), Date(39610), |
| 45 | Date(39638), Date(39673), Date(39701), Date(39729), Date(39764), |
| 46 | Date(39792), // 2008 |
| 47 | Date(39834), Date(39855), Date(39883), Date(39911), Date(39946), Date(39974), |
| 48 | Date(40002), Date(40037), Date(40065), Date(40100), Date(40128), |
| 49 | Date(40155), // 2009 |
| 50 | Date(40198), Date(40219), Date(40247), Date(40282), Date(40310), Date(40345), |
| 51 | Date(40373), Date(40401), Date(40429), Date(40464), Date(40492), |
| 52 | Date(40520), // 2010 |
| 53 | Date(40562), Date(40583), Date(40611), Date(40646), Date(40674), Date(40709), |
| 54 | Date(40737), Date(40765), Date(40800), Date(40828), Date(40856), |
| 55 | Date(40891), // 2011 |
| 56 | // http://www.ecb.europa.eu/press/pr/date/2011/html/pr110520.en.html |
| 57 | Date(40926), Date(40954), Date(40982), Date(41010), Date(41038), Date(41073), |
| 58 | Date(41101), Date(41129), Date(41164), Date(41192), Date(41227), |
| 59 | Date(41255), // 2012 |
| 60 | Date(41290), Date(41318), Date(41346), Date(41374), Date(41402), Date(41437), |
| 61 | Date(41465), Date(41493), Date(41528), Date(41556), Date(41591), |
| 62 | Date(41619), // 2013 |
| 63 | // http://www.ecb.europa.eu/press/pr/date/2013/html/pr130610.en.html |
| 64 | Date(41654), Date(41682), Date(41710), Date(41738), Date(41773), Date(41801), |
| 65 | Date(41829), Date(41864), Date(41892), Date(41920), Date(41955), |
| 66 | Date(41983), // 2014 |
| 67 | // http://www.ecb.europa.eu/press/pr/date/2014/html/pr140717_1.en.html |
| 68 | Date(42032), Date(42074), Date(42116), Date(42165), Date(42207), Date(42256), |
| 69 | Date(42305), |
| 70 | Date(42347), // 2015 |
| 71 | // https://www.ecb.europa.eu/press/pr/date/2015/html/pr150622.en.html |
| 72 | Date(42396), Date(42445), Date(42487), Date(42529), Date(42578), Date(42627), |
| 73 | Date(42669), |
| 74 | Date(42718), // 2016 |
| 75 | // https://www.ecb.europa.eu/press/calendars/reserve/html/index.en.html |
| 76 | Date(42760), Date(42809), Date(42858), Date(42900), Date(42942), Date(42991), |
| 77 | Date(43040), |
| 78 | Date(43089) // 2017 |
| 79 | }; |
| 80 | } |
| 81 | |
| 82 | const std::set<Date>& ECB::knownDates() { |
| 83 | return detail::ecbKnownDateSet; |
| 84 | } |
| 85 | |
| 86 | void ECB::addDate(const Date& d) { |
| 87 | detail::ecbKnownDateSet.insert(x: d); |
| 88 | } |
| 89 | |
| 90 | void ECB::removeDate(const Date& d) { |
| 91 | detail::ecbKnownDateSet.erase(x: d); |
| 92 | } |
| 93 | |
| 94 | Date ECB::date(const string& ecbCode, |
| 95 | const Date& refDate) { |
| 96 | |
| 97 | QL_REQUIRE(isECBcode(ecbCode), |
| 98 | ecbCode << " is not a valid ECB code" ); |
| 99 | |
| 100 | string code = to_upper_copy(Input: ecbCode); |
| 101 | string monthString = code.substr(pos: 0, n: 3); |
| 102 | Month m; |
| 103 | if (monthString=="JAN" ) m = January; |
| 104 | else if (monthString=="FEB" ) m = February; |
| 105 | else if (monthString=="MAR" ) m = March; |
| 106 | else if (monthString=="APR" ) m = April; |
| 107 | else if (monthString=="MAY" ) m = May; |
| 108 | else if (monthString=="JUN" ) m = June; |
| 109 | else if (monthString=="JUL" ) m = July; |
| 110 | else if (monthString=="AUG" ) m = August; |
| 111 | else if (monthString=="SEP" ) m = September; |
| 112 | else if (monthString=="OCT" ) m = October; |
| 113 | else if (monthString=="NOV" ) m = November; |
| 114 | else if (monthString=="DEC" ) m = December; |
| 115 | else QL_FAIL("not an ECB month (and it should have been)" ); |
| 116 | |
| 117 | Year y = std::stoi(str: code.substr(pos: 3, n: 2)); |
| 118 | Date referenceDate = (refDate != Date() ? |
| 119 | refDate : |
| 120 | Date(Settings::instance().evaluationDate())); |
| 121 | Year referenceYear = (referenceDate.year() % 100); |
| 122 | y += referenceDate.year() - referenceYear; |
| 123 | if (y<Date::minDate().year()) |
| 124 | return ECB::nextDate(d: Date::minDate()); |
| 125 | |
| 126 | return ECB::nextDate(d: Date(1, m, y) - 1); |
| 127 | } |
| 128 | |
| 129 | string ECB::code(const Date& ecbDate) { |
| 130 | |
| 131 | QL_REQUIRE(isECBdate(ecbDate), |
| 132 | ecbDate << " is not a valid ECB date" ); |
| 133 | |
| 134 | std::ostringstream ECBcode; |
| 135 | unsigned int y = ecbDate.year() % 100; |
| 136 | string padding; |
| 137 | if (y < 10) |
| 138 | padding = "0" ; |
| 139 | switch(ecbDate.month()) { |
| 140 | case January: |
| 141 | ECBcode << "JAN" << padding << y; |
| 142 | break; |
| 143 | case February: |
| 144 | ECBcode << "FEB" << padding << y; |
| 145 | break; |
| 146 | case March: |
| 147 | ECBcode << "MAR" << padding << y; |
| 148 | break; |
| 149 | case April: |
| 150 | ECBcode << "APR" << padding << y; |
| 151 | break; |
| 152 | case May: |
| 153 | ECBcode << "MAY" << padding << y; |
| 154 | break; |
| 155 | case June: |
| 156 | ECBcode << "JUN" << padding << y; |
| 157 | break; |
| 158 | case July: |
| 159 | ECBcode << "JUL" << padding << y; |
| 160 | break; |
| 161 | case August: |
| 162 | ECBcode << "AUG" << padding << y; |
| 163 | break; |
| 164 | case September: |
| 165 | ECBcode << "SEP" << padding << y; |
| 166 | break; |
| 167 | case October: |
| 168 | ECBcode << "OCT" << padding << y; |
| 169 | break; |
| 170 | case November: |
| 171 | ECBcode << "NOV" << padding << y; |
| 172 | break; |
| 173 | case December: |
| 174 | ECBcode << "DEC" << padding << y; |
| 175 | break; |
| 176 | default: |
| 177 | QL_FAIL("not an ECB month (and it should have been)" ); |
| 178 | } |
| 179 | |
| 180 | #if defined(QL_EXTRA_SAFETY_CHECKS) |
| 181 | QL_ENSURE(isECBcode(ECBcode.str()), |
| 182 | "the result " << ECBcode.str() << |
| 183 | " is an invalid ECB code" ); |
| 184 | #endif |
| 185 | return ECBcode.str(); |
| 186 | } |
| 187 | |
| 188 | |
| 189 | |
| 190 | Date ECB::nextDate(const Date& date) { |
| 191 | Date d = (date == Date() ? |
| 192 | Settings::instance().evaluationDate() : |
| 193 | date); |
| 194 | |
| 195 | auto i = std::upper_bound(first: knownDates().begin(), last: knownDates().end(), val: d); |
| 196 | |
| 197 | QL_REQUIRE(i != knownDates().end(), |
| 198 | "ECB dates after " << *knownDates().rbegin() << " are unknown" ); |
| 199 | return *i; |
| 200 | } |
| 201 | |
| 202 | std::vector<Date> ECB::nextDates(const Date& date) { |
| 203 | Date d = (date == Date() ? |
| 204 | Settings::instance().evaluationDate() : |
| 205 | date); |
| 206 | |
| 207 | auto i = std::upper_bound(first: knownDates().begin(), last: knownDates().end(), val: d); |
| 208 | |
| 209 | QL_REQUIRE(i != knownDates().end(), |
| 210 | "ECB dates after " << *knownDates().rbegin() << " are unknown" ); |
| 211 | return std::vector<Date>(i, knownDates().end()); |
| 212 | } |
| 213 | |
| 214 | |
| 215 | bool ECB::isECBcode(const std::string& ecbCode) { |
| 216 | |
| 217 | if (ecbCode.length() != 5) |
| 218 | return false; |
| 219 | |
| 220 | string code = to_upper_copy(Input: ecbCode); |
| 221 | |
| 222 | string str1("0123456789" ); |
| 223 | string::size_type loc = str1.find(str: code.substr(pos: 3, n: 1), pos: 0); |
| 224 | if (loc == string::npos) |
| 225 | return false; |
| 226 | loc = str1.find(str: code.substr(pos: 4, n: 1), pos: 0); |
| 227 | if (loc == string::npos) |
| 228 | return false; |
| 229 | |
| 230 | string monthString = code.substr(pos: 0, n: 3); |
| 231 | if (monthString=="JAN" ) return true; |
| 232 | else if (monthString=="FEB" ) return true; |
| 233 | else if (monthString=="MAR" ) return true; |
| 234 | else if (monthString=="APR" ) return true; |
| 235 | else if (monthString=="MAY" ) return true; |
| 236 | else if (monthString=="JUN" ) return true; |
| 237 | else if (monthString=="JUL" ) return true; |
| 238 | else if (monthString=="AUG" ) return true; |
| 239 | else if (monthString=="SEP" ) return true; |
| 240 | else if (monthString=="OCT" ) return true; |
| 241 | else if (monthString=="NOV" ) return true; |
| 242 | else if (monthString=="DEC" ) return true; |
| 243 | else return false; |
| 244 | } |
| 245 | |
| 246 | string ECB::nextCode(const std::string& ecbCode) { |
| 247 | QL_REQUIRE(isECBcode(ecbCode), |
| 248 | ecbCode << " is not a valid ECB code" ); |
| 249 | |
| 250 | string code = to_upper_copy(Input: ecbCode); |
| 251 | std::ostringstream result; |
| 252 | |
| 253 | string monthString = code.substr(pos: 0, n: 3); |
| 254 | if (monthString=="JAN" ) result << "FEB" << code.substr(pos: 3, n: 2); |
| 255 | else if (monthString=="FEB" ) result << "MAR" << code.substr(pos: 3, n: 2); |
| 256 | else if (monthString=="MAR" ) result << "APR" << code.substr(pos: 3, n: 2); |
| 257 | else if (monthString=="APR" ) result << "MAY" << code.substr(pos: 3, n: 2); |
| 258 | else if (monthString=="MAY" ) result << "JUN" << code.substr(pos: 3, n: 2); |
| 259 | else if (monthString=="JUN" ) result << "JUL" << code.substr(pos: 3, n: 2); |
| 260 | else if (monthString=="JUL" ) result << "AUG" << code.substr(pos: 3, n: 2); |
| 261 | else if (monthString=="AUG" ) result << "SEP" << code.substr(pos: 3, n: 2); |
| 262 | else if (monthString=="SEP" ) result << "OCT" << code.substr(pos: 3, n: 2); |
| 263 | else if (monthString=="OCT" ) result << "NOV" << code.substr(pos: 3, n: 2); |
| 264 | else if (monthString=="NOV" ) result << "DEC" << code.substr(pos: 3, n: 2); |
| 265 | else if (monthString=="DEC" ) { |
| 266 | unsigned int y = (std::stoi(str: code.substr(pos: 3, n: 2)) + 1) % 100; |
| 267 | string padding; |
| 268 | if (y < 10) |
| 269 | padding = "0" ; |
| 270 | |
| 271 | result << "JAN" << padding << y; |
| 272 | } else QL_FAIL("not an ECB month (and it should have been)" ); |
| 273 | |
| 274 | |
| 275 | #if defined(QL_EXTRA_SAFETY_CHECKS) |
| 276 | QL_ENSURE(isECBcode(result.str()), |
| 277 | "the result " << result.str() << |
| 278 | " is an invalid ECB code" ); |
| 279 | #endif |
| 280 | return result.str(); |
| 281 | } |
| 282 | |
| 283 | } |
| 284 | |