00001
00010 #include "eleve/config.hpp"
00011
00012 #include <map>
00013 #include <string>
00014 #include <set>
00015 #include <iostream>
00016 #include <fstream>
00017 #include <stdexcept>
00018
00019 #include "scal0.hpp"
00020
00021 #include "weekdays0.hpp"
00022 #include "mismatch.cpp"
00023
00024 #include <boost/filesystem.hpp>
00025
00026 #include <boost/foreach.hpp>
00027 #include <boost/date_time/gregorian/gregorian.hpp>
00028 #include <boost/algorithm/string/replace.hpp>
00029
00030 #include <ql/utilities/tracing.hpp>
00031 #include <ql/time/calendars/jointcalendar.hpp>
00032 #include <ql/time/calendars/bespokecalendar.hpp>
00033
00038
00039 namespace QuantLib {
00040
00041 struct NCalendar : public Calendar {
00042 NCalendar(Calendar & cal) : Calendar(cal) {}
00043 NCalendar(const Calendar & cal) : Calendar(cal) {}
00044
00045 const std::set<Date> & added() const {
00046 return impl_->addedHolidays;
00047 }
00048
00049 const Impl * impl() const {
00050 return impl_.get();
00051 }
00052 };
00053 }
00054
00055 namespace eepgwde { namespace detail {
00056
00057 using eepgwde::daemon::Q;
00058
00059 namespace dt = boost::gregorian;
00060 namespace ba = boost::algorithm;
00061 namespace fs = boost::filesystem;
00062
00063 typedef std::pair<fs::path, fs::path> atomic_t;
00064 typedef std::vector<std::string> compound_t;
00065 typedef std::map<const std::string, QuantLib::Calendar> cache_t;
00066
00067 struct nonatom : std::exception {};
00068 struct notpresent : std::exception {};
00069 struct uncached : std::exception {};
00070
00071 class Calendars::Impl {
00072 friend class Calendars;
00073
00074 const std::string id_;
00075 fs::path path_;
00076 fs::path comp0;
00077 fs::path comp1;
00078 fs::path wk;
00079 boost::regex re_;
00080 Calendars * owner_;
00081
00082 QuantLib::Calendar l_;
00083 QuantLib::Calendar r_;
00084 std::set<QuantLib::Date> dates_;
00085
00086 cache_t cache;
00087
00088 std::set<QuantLib::Weekday> alldays;
00089
00090 public:
00091
00092 Impl() throw(std::exception) :
00093 id_("Calendars::Impl"), path_(".") {
00094 try {
00095 update(path_);
00096 } catch (...) {
00097 }
00098
00099 for (eepgwde::daemon::weekday_map_t::iterator
00100 it = eepgwde::daemon::weekdays.begin();
00101 it != eepgwde::daemon::weekdays.end();
00102 ++it) {
00103 alldays.insert((*it).second);
00104 }
00105 }
00106
00107 ~Impl() throw(std::exception) {}
00108
00109 const std::string & id() {
00110 return id_;
00111 }
00112
00113 void calendars(const QuantLib::Calendar & a,
00114 const QuantLib::Calendar & b,
00115 const std::set<QuantLib::Date> & dates) {
00116 l_ = a;
00117 r_ = b;
00118 dates_ = dates;
00119 }
00120
00121 QuantLib::Date make(QuantLib::Date *dummy, const std::string & ds)
00122 throw (std::exception) {
00123 dt::date d(dt::from_simple_string(ds));
00124
00125 QuantLib::Month m =
00126 static_cast<QuantLib::Month>(static_cast<int>(d.month()));
00127
00128 return QuantLib::Date(d.day(),
00129 m,
00130 d.year());
00131 }
00132
00133 fs::path is_there(const fs::path & p) const throw (std::exception) {
00134 fs::file_status fs0 = fs::status(p);
00135
00136 if (! fs::exists(fs0))
00137 throw std::runtime_error(p.leaf() + " does not exist");
00138
00139 if (! fs::is_directory(fs0))
00140 throw std::runtime_error(p.leaf() + " is not a directory");
00141
00142 return p;
00143 }
00144
00145 void update(fs::path path0) throw (std::exception) {
00146 path_ = is_there(path0);
00147 comp0 = is_there(path_ / eepgwde::daemon::detail::COMP0);
00148 comp1 = is_there(path_ / eepgwde::daemon::detail::COMP1);
00149 wk = is_there(path_ / eepgwde::daemon::detail::WK);
00150 }
00151
00152 void update1(const std::string & s) {
00153 boost::regex re(s + "\\." + eepgwde::daemon::detail::suffix + "$");
00154 re_ = re;
00155 }
00156
00157 operator atomic_t() const throw (nonatom) {
00158 std::set<fs::path> set_;
00159 std::insert_iterator< std::set<fs::path> > iset_(set_,set_.begin());
00160
00161 std::set<fs::path> set1_;
00162 std::insert_iterator< std::set<fs::path> > iset1_(set1_,set1_.begin());
00163
00164 if (!Q::instance().files(iset_, comp0, re_) ||
00165 !Q::instance().files(iset1_, wk, re_) ) {
00166 throw nonatom();
00167 }
00168
00169 return atomic_t(*set_.begin(), *set1_.begin());
00170 }
00171
00172 operator compound_t() const throw (notpresent) {
00173 std::set<fs::path> set_;
00174 std::insert_iterator< std::set<fs::path> > iset_(set_,set_.begin());
00175
00176 if (!Q::instance().files(iset_, comp1, re_)) {
00177 throw notpresent();
00178 }
00179
00180 return Q::instance().readlines(*set_.begin());
00181 }
00182
00183 QuantLib::Calendar & cache_copy(const std::string & name_,
00184 QuantLib::Calendar & cal) {
00185 cache[name_] = cal;
00186 return cache[name_];
00187 }
00188
00189 QuantLib::Calendar & cache_copy(const std::string & name_)
00190 throw (uncached) {
00191 cache_t::iterator it;
00192 if (!cache.empty() &&
00193 (it = cache.find(name_)) != cache.end()) {
00194 #ifndef NDEBUG
00195 QL_TRACE("cache_copy: found: " + name_);
00196 #endif
00197 return (*it).second;
00198 }
00199
00200 throw uncached();
00201 }
00202
00205 QuantLib::Calendar make(QuantLib::Calendar const * dummy,
00206 const atomic_t & atom,
00207 const std::string & n = "")
00208 throw (std::exception) {
00209
00210 update(path_);
00211
00212 compound_t dates_ = Q::instance().readlines(atom.first);
00213 compound_t weekdays_ = Q::instance().readlines(atom.second);
00214
00215 #ifndef NDEBUG
00216 QL_TRACE("Building calendar" +
00217 atom.first.string() + ", " +
00218 atom.second.string());
00219
00220 copy(weekdays_.begin(), weekdays_.end(),
00221 std::ostream_iterator<std::string>(std::cerr, ","));
00222 std::cerr << std::endl;
00223 #endif
00224
00225 std::set<QuantLib::Weekday> weekdays1;
00226 BOOST_FOREACH(std::string s, weekdays_) {
00227 eepgwde::daemon::weekday_map_t::iterator it =
00228 eepgwde::daemon::weekdays.find(s);
00229 weekdays1.insert((*it).second);
00230 }
00231
00232 std::set<QuantLib::Weekday> weekenddays;
00233 std::insert_iterator<std::set<QuantLib::Weekday> >
00234 ii(weekenddays, weekenddays.begin());
00235
00236 std::set_difference(alldays.begin(), alldays.end(),
00237 weekdays1.begin(), weekdays1.end(),
00238 ii);
00239
00240 #ifndef NDEBUG
00241 BOOST_FOREACH(QuantLib::Weekday w, weekenddays) {
00242 std::cerr << QuantLib::io::long_weekday(w) << std::endl;
00243 }
00244 #endif
00245
00246 QuantLib::BespokeCalendar result(n);
00247 BOOST_FOREACH(QuantLib::Weekday w, weekenddays) {
00248 result.addWeekend(w);
00249 }
00250
00251 BOOST_FOREACH(std::string s, dates_) {
00252 QuantLib::Date hdt;
00253 hdt = make(&hdt, s);
00254 result.addHoliday(hdt);
00255 }
00256
00257 return result;
00258 }
00259
00262 QuantLib::Calendar make(QuantLib::Calendar const * dummy,
00263 const compound_t & compound)
00264 throw (std::exception) {
00265
00266 update(path_);
00267
00268 std::vector<QuantLib::Calendar> calendars;
00269
00270 BOOST_FOREACH(std::string s, compound) {
00271 calendars.push_back(Q::instance().make(dummy, s));
00272 }
00273
00274 QuantLib::Calendar result;
00275 BOOST_FOREACH(QuantLib::Calendar s, calendars) {
00276 if (result.empty()) result = s;
00277 else {
00278 QuantLib::JointCalendar join(result, s);
00279 result = join;
00280 }
00281 }
00282
00283 return result;
00284 }
00285
00286 Impl(Impl const &);
00287 Impl & operator=(Impl const &);
00288
00289 };
00290
00291 Calendars::Calendars() throw(std::exception) : impl(new Impl) {
00292 impl->owner_ = this;
00293 }
00294
00295 Calendars::~Calendars() throw(std::exception) {}
00296
00297 QuantLib::Calendar
00298 Calendars::make(QuantLib::Calendar const * dummy,
00299 const fs::path & name_)
00300 throw (std::exception) {
00301 std::string n = name_.leaf();
00302 ba::replace_last(n, std::string(".") + eepgwde::daemon::detail::suffix, "");
00303 return make(dummy, n);
00304 }
00305
00316 QuantLib::Calendar
00317 Calendars::make(QuantLib::Calendar const * dummy,
00318 const std::string & name_)
00319 throw (std::exception) {
00320 impl->update(impl->path_);
00321
00322 try {
00323 return impl->cache_copy(name_);
00324 } catch (uncached) {}
00325
00326 impl->update1(name_);
00327
00328 atomic_t atom;
00329 try {
00330 atom = *(impl.get());
00331 QuantLib::Calendar c = impl->make(dummy, atom, name_);
00332 impl->cache_copy(name_, c);
00333 return Q::instance().make(dummy, name_);
00334 } catch (nonatom e) {
00335 ;
00336 }
00337
00338
00339
00340 compound_t compound;
00341 try {
00342 compound = *(impl.get());
00343 QuantLib::Calendar c = impl->make(dummy, compound);
00344 impl->cache_copy(name_, c);
00345 return Q::instance().make(dummy, name_);
00346 } catch (notpresent e) {
00347 std::string m("unknown calendar " +
00348 name_ +
00349 " within " +
00350 path().string());
00351 QL_TRACE(m);
00352 throw std::runtime_error(m);
00353 }
00354
00355 return *dummy;
00356 }
00357
00358 std::pair<QuantLib::Date, QuantLib::Date>
00359 Calendars::added(QuantLib::Calendar const & c)
00360 throw(std::exception) {
00361 QuantLib::Calendar ncal0 = static_cast<QuantLib::Calendar>(c);
00362 QuantLib::NCalendar ncal(ncal0);
00363
00364 const std::set<QuantLib::Date> added = ncal.added();
00365 if (!added.size()) {
00366 throw runtime_error(c.name() + ": has no added holidays");
00367 }
00368
00369 std::pair<QuantLib::Date, QuantLib::Date> result =
00370 std::make_pair(*added.begin(), *added.rbegin());
00371
00372 return result;
00373 }
00374
00375 const std::string & Calendars::id() const {
00376 return impl->id();
00377 }
00378
00380 fs::path Calendars::path() const {
00381 return impl->path_;
00382 }
00383
00385 fs::path Calendars::path(const fs::path & path_)
00386 throw(std::exception) {
00387 impl->update(path_);
00388 return path();
00389 }
00390
00391 std::set<QuantLib::Date> Calendars::dates() {
00392 return impl->dates_;
00393 }
00394
00395 std::set<QuantLib::Date>
00396 Calendars::set_difference(const QuantLib::Calendar & a,
00397 const QuantLib::Calendar & b,
00398 const QuantLib::Date & from_,
00399 const QuantLib::Date & to_)
00400 throw(std::exception) {
00401
00402 std::vector<QuantLib::Date> holidays0, holidays1;
00403 holidays0 = QuantLib::Calendar::holidayList(a, from_, to_, 1);
00404 holidays1 = QuantLib::Calendar::holidayList(b, from_, to_, 1);
00405
00406 std::set<QuantLib::Date> diffs;
00407 std::insert_iterator<std::set<QuantLib::Date> >
00408 idiffs(diffs, diffs.begin());
00409
00410 std::set_difference(holidays0.begin(), holidays0.end(),
00411 holidays1.begin(), holidays1.end(),
00412 idiffs);
00413
00414 impl->calendars(a, b, diffs);
00415
00416 return diffs;
00417 }
00418
00419 const std::map<const std::string, QuantLib::Calendar> &
00420 Calendars::mismatches() const {
00421 return eepgwde::daemon::mismatches;
00422 }
00423
00424 const std::map<const std::string, QuantLib::Calendar> &
00425 Calendars::mismatches(const std::map<const std::string, QuantLib::Calendar> &m) {
00426 eepgwde::daemon::mismatches = m;
00427 return mismatches();
00428 }
00429
00430 const std::map<const std::string, QuantLib::Calendar> & Calendars::fix()
00431 throw (std::exception) {
00432 return fix(path());
00433 }
00434
00435 const std::map<const std::string, QuantLib::Calendar> &
00436 Calendars::fix(const fs::path & path_) throw(std::exception) {
00437 path(path_);
00438 std::map<const std::string, QuantLib::Calendar> mismatches0 =
00439 this->mismatches() ;
00440
00441 for (std::map<const std::string, QuantLib::Calendar>::iterator
00442 it = mismatches0.begin(); it != mismatches0.end(); ++it) {
00443 QuantLib::Calendar cal = make(&cal, it->first);
00444 QuantLib::Calendar & c = it->second;
00445 if (eepgwde::operator==(cal, c)) continue;
00446
00447 std::set<QuantLib::Date> dates0 = this->dates();
00448 BOOST_FOREACH(QuantLib::Date d0, dates0) {
00449 if (cal.isHoliday(d0) && !c.isHoliday(d0))
00450 c.addHoliday(d0);
00451 else if (!cal.isHoliday(d0) && c.isHoliday(d0))
00452 c.removeHoliday(d0);
00453 }
00454 }
00455
00456 return this->mismatches();
00457 }
00458 }}
00459
00460 namespace eepgwde {
00461
00467 bool operator!= (QuantLib::Calendar const & a,
00468 QuantLib::Calendar const & b)
00469 throw(std::invalid_argument) {
00470 return !eepgwde::operator==(a, b);
00471 }
00472
00477 bool operator== (QuantLib::Calendar const & a,
00478 QuantLib::Calendar const & b)
00479 throw(std::invalid_argument) {
00480 if (QuantLib::operator==(a, b) ) return true;
00481 if (&a == &b) return true;
00482
00483 QuantLib::NCalendar an(a);
00484 QuantLib::NCalendar bn(b);
00485
00486 if (an.impl() == bn.impl()) {
00487 QL_TRACE("eepgwde::operator==(Calendar, Calendar) implementation match");
00488 return true;
00489 }
00490
00491 if (a.empty() && !b.empty()) return false;
00492 if (b.empty() && !a.empty()) return false;
00493
00494
00495
00496
00497
00498
00499 const QuantLib::Calendar * src = &a;
00500 const QuantLib::Calendar * target = &b;
00501 std::pair<QuantLib::Date, QuantLib::Date> src_range;
00502
00503 #ifndef NDEBUG
00504 {
00505 try {
00506 src_range = eepgwde::daemon::Q::instance().added(*src);
00507 QuantLib::Date & from_ = src_range.first;
00508 QuantLib::Date & to_ = src_range.second;
00509 QL_TRACE("QuantLib::Calendar::" <<
00510 "(" << src->name() << ") "
00511 << QuantLib::io::iso_date(from_) << " "
00512 << QuantLib::io::iso_date(to_) << " ");
00513 } catch (std::exception & e) {
00514 QL_TRACE("src failed: " + src->name());
00515 }
00516 }
00517
00518 {
00519 try {
00520 src_range = eepgwde::daemon::Q::instance().added(*target);
00521 QuantLib::Date & from_ = src_range.first;
00522 QuantLib::Date & to_ = src_range.second;
00523 QL_TRACE("QuantLib::Calendar::" <<
00524 "(" << target->name() << ") "
00525 << QuantLib::io::iso_date(from_) << " "
00526 << QuantLib::io::iso_date(to_) << " ");
00527 } catch (std::exception & e) {
00528 QL_TRACE("target failed: " + target->name());
00529 }
00530 }
00531 #endif
00532
00533 try {
00534 src = &a;
00535 target = &b;
00536 src_range = eepgwde::daemon::Q::instance().added(*src);
00537 } catch (std::exception & e) {
00538 try {
00539 src = &b;
00540 target = &a;
00541 src_range = eepgwde::daemon::Q::instance().added(*src);
00542 } catch (std::exception & e) {
00543
00544
00545
00546
00547
00548 QL_TRACE("eepgwde::operator==(Calendar, Calendar) mismatch");
00549 return false;
00550 }
00551 }
00552
00553 QuantLib::Date & from_ = src_range.first;
00554 QuantLib::Date & to_ = src_range.second;
00555
00556 if (! (to_ > from_) ) return false;
00557
00558 std::set<QuantLib::Date> diffs;
00559 diffs = eepgwde::daemon::Q::instance().set_difference(*src, *target, from_, to_);
00560
00561 #ifndef NDEBUG
00562 QL_TRACE("QuantLib::Calendar::" <<
00563 "(" << src->name() << ", " << target->name() << ") "
00564 << QuantLib::io::iso_date(from_) << " "
00565 << QuantLib::io::iso_date(to_) << " "
00566 << "differences: " << diffs.size() );
00567
00568 QL_TRACE("eepgwde::operator==(Calendar, Calendar) added() diffs: " << src->name() << " " << target->name() << " " << diffs.size() );
00569 #endif
00570
00571 return diffs.size() == 0;
00572 }
00573
00574 }