31std::vector<Date> everyWeekDayDates(
const Date& startDate,
const Date& endDate,
const Date& firstDate,
const QuantLib::Weekday weekday) {
32 std::vector<Date> result;
33 if (firstDate != Date())
34 result.push_back(firstDate);
36 while (d <= endDate && (d.weekday() != weekday || d < firstDate)) {
39 if (d.weekday() == weekday && (result.empty() || result.back() != d))
41 while (d + 7 <= endDate) {
48std::vector<Date> weeklyDates(
const Date& startDate,
const Date& endDate,
const Date& firstDate,
49 bool includeWeekend =
false) {
50 QuantLib::Weekday weekday = includeWeekend ? QuantLib::Sunday : QuantLib::Friday;
55 Date effectiveFirstDate = firstDate == Date() ? startDate : firstDate;
56 auto dates = everyWeekDayDates(startDate, endDate, effectiveFirstDate, weekday);
62 if (effectiveFirstDate.weekday() == weekday) {
63 dates.insert(dates.begin(), effectiveFirstDate);
66 if (dates.back() < endDate) {
67 dates.push_back(endDate);
82 while(!Date::isEndOfMonth(ed))ed--;
180 QL_REQUIRE(node,
"ScheduleData::fromXML(): no node given");
211 schedules_.insert(pair<
string, pair<ScheduleData, Schedule&>>({
name, {
data, schedule}}));
215 map<string, Schedule> builtSchedules;
216 map<string, ScheduleData> derivedSchedules;
220 string schName = s.first;
222 Schedule& sch = s.second.second;
226 builtSchedules[schName] = sch;
228 derivedSchedules[schName] = schData;
234 while (derivedSchedules.size() > 0) {
236 for (
auto& ds : derivedSchedules) {
237 string dsName = ds.first;
240 for (
string& bn : baseNames) {
241 QL_REQUIRE(builtSchedules.find(bn) != builtSchedules.end(),
"Could not find base schedule \" " << bn <<
"\" for derived schedule \" " << dsName <<
"\"");
244 schedule =
makeSchedule(dsSchedData, openEndDateReplacement, builtSchedules);
245 schedules_.find(dsName)->second.second = schedule;
246 builtSchedules[dsName] = schedule;
247 derivedSchedules.erase(dsName);
255 for (
auto& ds : derivedSchedules)
256 ALOG(
"makeSchedules(): could not find base schedule \"" << ds.first <<
"\"");
257 QL_FAIL(
"makeSchedules(): failed to build at least one derived schedule");
264 QL_REQUIRE(
data.dates().size() > 0,
"Must provide at least 1 date for Schedule");
266 BusinessDayConvention convention = ModifiedFollowing;
267 if (!
data.convention().empty())
272 auto tenor = boost::make_optional(
false, Period());
273 if (!
data.tenor().empty())
275 bool endOfMonth =
false;
276 if (!
data.endOfMonth().empty())
278 ext::optional<BusinessDayConvention> endOfMonthConvention = boost::none;
279 if (!
data.endOfMonthConvention().empty())
283 std::set<Date> uniqueDates;
284 for (
const string& d :
data.dates())
287 return QuantLib::Schedule(vector<Date>(uniqueDates.begin(), uniqueDates.end()),
calendar, convention, boost::none,
288 tenor, boost::none, endOfMonth, vector<bool>(0),
false,
false, endOfMonthConvention);
293 string strCalendar =
data.calendar();
295 if (strCalendar.empty()) {
297 WLOG(
"No calendar provided in Schedule, attempting to use a null calendar.");
302 BusinessDayConvention convention;
303 string strConvention =
data.convention();
304 if (strConvention.empty())
305 convention = BusinessDayConvention::Unadjusted;
309 string strShift =
data.shift();
311 if (strShift.empty())
316 const std::vector<QuantLib::Date>& baseDates = baseSchedule.dates();
317 std::vector<QuantLib::Date> derivedDates;
318 QuantLib::Date derivedDate;
319 for (
const Date& d : baseDates) {
320 derivedDate =
calendar.advance(d, shift, convention);
321 derivedDates.push_back(derivedDate);
323 ext::optional<BusinessDayConvention> endOfMonthConvention = boost::none;
324 if (baseSchedule.hasEndOfMonthBusinessDayConvention())
325 endOfMonthConvention = baseSchedule.endOfMonthBusinessDayConvention();
327 return QuantLib::Schedule(vector<Date>(derivedDates.begin(), derivedDates.end()),
calendar, convention, boost::none,
328 baseSchedule.tenor(), boost::none, baseSchedule.endOfMonth(), std::vector<bool>(0),
329 data.removeFirstDate(),
data.removeLastDate(),
330 endOfMonthConvention);
334 QL_REQUIRE(!
data.endDate().empty() || openEndDateReplacement != Null<Date>(),
335 "makeSchedule(): Schedule does not have an end date, this is not supported in this context / for this "
336 "trade type. Please provide an end date.");
337 QL_REQUIRE(!
data.endDate().empty() ||
data.lastDate().empty(),
338 "makeSchedule(): If no end date is given, a last date is not allowed either. Please remove the last "
339 "date from the schedule.");
342 WLOG(
"No calendar provided in Schedule, attempting to use a null calendar.");
344 Date endDate =
data.endDate().empty() ? openEndDateReplacement :
parseDate(
data.endDate());
346 if (startDate == endDate)
347 return Schedule(vector<Date>(1, startDate),
calendar);
349 QL_REQUIRE(startDate < endDate,
"StartDate " << startDate <<
" is ahead of EndDate " << endDate);
353 if (firstDate != Date() && lastDate != Date())
354 QL_REQUIRE(firstDate <= lastDate,
"Schedule::makeSchedule firstDate must be before lastDate");
359 BusinessDayConvention bdc = ModifiedFollowing;
360 BusinessDayConvention bdcEnd = ModifiedFollowing;
361 DateGeneration::Rule rule = DateGeneration::Forward;
362 bool endOfMonth =
false;
363 ext::optional<BusinessDayConvention> endOfMonthConvention = boost::none;
366 if (!
data.convention().empty())
368 if (!
data.termConvention().empty())
373 if (!
data.endOfMonth().empty())
375 if (!
data.endOfMonthConvention().empty())
378 if (!
data.rule().empty()) {
382 if (
data.rule() ==
"EveryThursday") {
383 auto dates = everyWeekDayDates(startDate, endDate, firstDate, QuantLib::Thursday);
384 for (
auto& d : dates)
386 return QuantLib::Schedule(dates,
calendar, bdc, bdcEnd, tenor, rule, endOfMonth, std::vector<bool>(0),
false,
false, endOfMonthConvention);
387 }
else if (
data.rule() ==
"BusinessWeek" ||
data.rule() ==
"CalendarWeek") {
388 auto dates = weeklyDates(startDate, endDate, firstDate,
data.rule() ==
"CalendarWeek");
389 for (
auto& d : dates)
391 return QuantLib::Schedule(dates,
calendar, bdc, bdcEnd, tenor, rule, endOfMonth, std::vector<bool>(0),
data.removeFirstDate(),
data.removeLastDate(), endOfMonthConvention);
401 if ((rule == DateGeneration::CDS || rule == DateGeneration::CDS2015) &&
402 (firstDate != Null<Date>() || lastDate != Null<Date>())) {
408 std::vector<Date> dates = QuantLib::Schedule(startDate, endDate, tenor,
calendar, bdc, bdcEnd, rule, endOfMonth,
409 Date(), Date(),
false,
false, endOfMonthConvention)
411 QL_REQUIRE(!dates.empty(),
412 "got empty CDS or CDS2015 schedule, startDate = " << startDate <<
", endDate = " << endDate);
413 if (firstDate != Date())
414 dates.front() = firstDate;
415 if (lastDate != Date())
416 dates.back() = lastDate;
417 return QuantLib::Schedule(dates,
calendar, bdc, bdcEnd, tenor, rule, endOfMonth, std::vector<bool>(0),
418 data.removeFirstDate(),
data.removeLastDate(), endOfMonthConvention);
423 return QuantLib::Schedule(startDate, endDate, tenor,
calendar, bdc, bdcEnd, rule, endOfMonth, firstDate, lastDate,
424 data.removeFirstDate(),
data.removeLastDate(), endOfMonthConvention);
430void updateData(
const std::string& s, T& t,
bool& hasT,
bool& hasConsistentT,
const std::function<T(
string)>& parser) {
434 hasConsistentT = hasConsistentT && (tmp == t);
442Calendar parseCalendarTemp(
const string& s) {
return parseCalendar(s); }
445Schedule
makeSchedule(
const ScheduleData& data,
const Date& openEndDateReplacement,
const map<string, QuantLib::Schedule>& baseSchedules) {
449 for (Size i = 1; i <
data.rules().
size(); ++i) {
450 QL_REQUIRE(!
data.rules()[i - 1].endDate().empty(),
451 "makeSchedule(): only last schedule is allowed to have an open end date");
454 vector<Schedule> schedules;
455 for (
auto& d :
data.dates())
457 for (
auto& r :
data.rules())
458 schedules.push_back(
makeSchedule(r, openEndDateReplacement));
459 if (!baseSchedules.empty())
460 for (
auto& dv :
data.derived()) {
461 auto baseSchedule = baseSchedules.find(dv.baseSchedule());
462 QL_REQUIRE(baseSchedule != baseSchedules.end(),
"makeSchedule(): could not find base schedule \"" << dv.baseSchedule() <<
"\"");
463 schedules.push_back(
makeSchedule(dv, baseSchedule->second));
465 QL_REQUIRE(!schedules.empty(),
"No dates or rules to build Schedule from");
466 if (schedules.size() == 1)
468 return schedules.front();
473 std::sort(schedules.begin(), schedules.end(),
474 [](
const Schedule& lhs,
const Schedule& rhs) ->
bool { return lhs.startDate() < rhs.startDate(); });
478 BusinessDayConvention convention = Null<BusinessDayConvention>(),
479 termConvention = Unadjusted;
482 DateGeneration::Rule rule = DateGeneration::Zero;
483 bool endOfMonth =
false;
484 BusinessDayConvention endOfMonthConvention = Null<BusinessDayConvention>();
485 bool hasCalendar =
false, hasConvention =
false, hasTermConvention =
false, hasTenor =
false, hasRule =
false,
486 hasEndOfMonth =
false, hasEndOfMonthConvention =
false, hasConsistentCalendar =
true,
487 hasConsistentConvention =
true, hasConsistentTenor =
true, hasConsistentRule =
true,
488 hasConsistentEndOfMonth =
true, hasConsistentEndOfMonthConvention =
true;
489 for (
auto& d :
data.dates()) {
490 updateData<Calendar>(d.calendar(),
calendar, hasCalendar, hasConsistentCalendar, parseCalendarTemp);
491 updateData<BusinessDayConvention>(d.convention(), convention, hasConvention, hasConsistentConvention,
493 updateData<Period>(d.tenor(), tenor, hasTenor, hasConsistentTenor,
parsePeriod);
495 for (
auto& d :
data.rules()) {
496 updateData<Calendar>(d.calendar(),
calendar, hasCalendar, hasConsistentCalendar, parseCalendarTemp);
497 updateData<BusinessDayConvention>(d.convention(), convention, hasConvention, hasConsistentConvention,
499 updateData<Period>(d.tenor(), tenor, hasTenor, hasConsistentTenor,
parsePeriod);
500 updateData<bool>(d.endOfMonth(), endOfMonth, hasEndOfMonth, hasConsistentEndOfMonth,
parseBool);
501 updateData<BusinessDayConvention>(d.endOfMonthConvention(), endOfMonthConvention, hasEndOfMonthConvention,
504 if (d.termConvention() !=
"") {
505 hasTermConvention =
true;
511 const Schedule& s0 = schedules.front();
512 vector<Date> dates = s0.dates();
513 std::vector<bool> isRegular(s0.dates().size() - 1,
false);
514 if (s0.hasIsRegular())
515 isRegular = s0.isRegular();
517 isRegular.push_back(
false);
518 for (Size i = 1; i < schedules.size(); ++i) {
519 const Schedule& s = schedules[i];
520 QL_REQUIRE(dates.back() <= s.dates().front(),
"Dates mismatch");
522 Size offset = dates.back() == s.dates().front() ? 1 : 0;
523 isRegular.erase(isRegular.end() - offset,
526 if (s.hasIsRegular()) {
527 isRegular.insert(isRegular.end(), s.isRegular().begin(), s.isRegular().end());
529 for (Size ii = 0; ii < s.dates().
size() - 1; ++ii)
530 isRegular.push_back(
false);
532 if (i < schedules.size() - 1) {
534 isRegular.push_back(
false);
537 dates.insert(dates.end(), s.dates().begin() + offset, s.dates().end());
541 return QuantLib::Schedule(
542 dates, hasCalendar && hasConsistentCalendar ?
calendar : NullCalendar(),
543 hasConvention && hasConsistentConvention ? convention : Unadjusted,
544 hasTermConvention ? ext::optional<BusinessDayConvention>(termConvention) : boost::none,
545 hasTenor && hasConsistentTenor ? ext::optional<Period>(tenor) : boost::none,
546 hasRule && hasConsistentRule ? ext::optional<DateGeneration::Rule>(rule) : boost::none,
547 hasEndOfMonth && hasConsistentEndOfMonth ? ext::optional<bool>(endOfMonth) : boost::none, isRegular,
549 hasEndOfMonthConvention && hasConsistentEndOfMonthConvention
550 ? ext::optional<BusinessDayConvention>(endOfMonthConvention)
void makeSchedules(const QuantLib::Date &openEndDateReplacement=QuantLib::Null< QuantLib::Date >())
map< string, pair< ScheduleData, QuantLib::Schedule & > > schedules_
void add(QuantLib::Schedule &schedule, const ScheduleData &scheduleData)
Serializable schedule data.
vector< ScheduleDates > dates_
vector< ScheduleDerived > derived_
vector< string > baseScheduleNames()
virtual void fromXML(XMLNode *node) override
vector< ScheduleRules > rules_
virtual XMLNode * toXML(XMLDocument &doc) const override
const bool & hasDerived() const
Serializable object holding schedule Dates data.
virtual void fromXML(XMLNode *node) override
virtual XMLNode * toXML(XMLDocument &doc) const override
Serializable object holding Derived schedule data.
virtual void fromXML(XMLNode *node) override
virtual XMLNode * toXML(XMLDocument &doc) const override
Serializable object holding schedule Rules data.
bool adjustEndDateToPreviousMonthEnd_
virtual void fromXML(XMLNode *node) override
virtual XMLNode * toXML(XMLDocument &doc) const override
string endOfMonthConvention_
Small XML Document wrapper class.
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
static void addChildren(XMLDocument &doc, XMLNode *n, const string &names, const string &name, const vector< T > &values)
static void checkNode(XMLNode *n, const string &expectedName)
static vector< XMLNode * > getChildrenNodes(XMLNode *node, const string &name)
Returns all the children with a given name.
static string getNodeName(XMLNode *n)
Get and set a node's name.
static string getChildValue(XMLNode *node, const string &name, bool mandatory=false, const string &defaultValue=string())
static bool getChildValueAsBool(XMLNode *node, const string &name, bool mandatory=false, bool defaultValue=true)
static vector< string > getChildrenValues(XMLNode *node, const string &names, const string &name, bool mandatory=false)
static XMLNode * addChild(XMLDocument &doc, XMLNode *n, const string &name)
static void appendNode(XMLNode *parent, XMLNode *child)
DateGeneration::Rule parseDateGenerationRule(const string &s)
Convert text to QuantLib::DateGeneration::Rule.
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
BusinessDayConvention parseBusinessDayConvention(const string &s)
Convert text to QuantLib::BusinessDayConvention.
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
bool parseBool(const string &s)
Convert text to bool.
Classes and functions for log message handling.
#define ALOG(text)
Logging Macro (Level = Alert)
#define WLOG(text)
Logging Macro (Level = Warning)
Size size(const ValueType &v)
std::string to_string(const LocationInfo &l)
Schedule makeSchedule(const ScheduleDates &data)
Serializable Credit Default Swap.
Map text representations to QuantLib/QuantExt types.
trade schedule data model and serialization
string conversion utilities