@@ -92,6 +92,7 @@ void ScheduleRules::fromXML(XMLNode* node) {
9292 }
9393 rule_ = XMLUtils::getChildValue (node, " Rule" );
9494 endOfMonth_ = XMLUtils::getChildValue (node, " EndOfMonth" );
95+ endOfMonthConvention_ = XMLUtils::getChildValue (node, " EndOfMonthConvention" );
9596 firstDate_ = XMLUtils::getChildValue (node, " FirstDate" );
9697 lastDate_ = XMLUtils::getChildValue (node, " LastDate" );
9798 removeFirstDate_ = XMLUtils::getChildValueAsBool (node, " RemoveFirstDate" , false , false );
@@ -109,6 +110,8 @@ XMLNode* ScheduleRules::toXML(XMLDocument& doc) const {
109110 XMLUtils::addChild (doc, rules, " TermConvention" , termConvention_);
110111 XMLUtils::addChild (doc, rules, " Rule" , rule_);
111112 XMLUtils::addChild (doc, rules, " EndOfMonth" , endOfMonth_);
113+ if (!endOfMonthConvention_.empty ())
114+ XMLUtils::addChild (doc, rules, " EndOfMonthConvention" , endOfMonthConvention_);
112115 XMLUtils::addChild (doc, rules, " FirstDate" , firstDate_);
113116 XMLUtils::addChild (doc, rules, " LastDate" , lastDate_);
114117 if (removeFirstDate_)
@@ -131,10 +134,10 @@ void ScheduleDates::fromXML(XMLNode* node) {
131134XMLNode* ScheduleDates::toXML (XMLDocument& doc) const {
132135 XMLNode* node = doc.allocNode (" Dates" );
133136 XMLUtils::addChild (doc, node, " Calendar" , calendar_);
134- if (convention_ != " " )
137+ if (!convention_. empty () )
135138 XMLUtils::addChild (doc, node, " Convention" , convention_);
136139 XMLUtils::addChild (doc, node, " Tenor" , was1T_ ? " 1T" : tenor_);
137- if (endOfMonth_ != " " )
140+ if (!endOfMonth_. empty () )
138141 XMLUtils::addChild (doc, node, " EndOfMonth" , endOfMonth_);
139142 XMLUtils::addChildren (doc, node, " Dates" , " Date" , dates_);
140143 return node;
@@ -261,25 +264,28 @@ Schedule makeSchedule(const ScheduleDates& data) {
261264 QL_REQUIRE (data.dates ().size () > 0 , " Must provide at least 1 date for Schedule" );
262265 Calendar calendar = parseCalendar (data.calendar ());
263266 BusinessDayConvention convention = ModifiedFollowing;
264- if (data.convention () != " " )
267+ if (! data.convention (). empty () )
265268 convention = parseBusinessDayConvention (data.convention ());
266269 // Avoid compiler warning on gcc
267270 // https://www.boost.org/doc/libs/1_74_0/libs/optional/doc/html/boost_optional/tutorial/
268271 // gotchas/false_positive_with__wmaybe_uninitialized.html
269272 auto tenor = boost::make_optional (false , Period ());
270- if (data.tenor () != " " )
273+ if (! data.tenor (). empty () )
271274 tenor = parsePeriod (data.tenor ());
272275 bool endOfMonth = false ;
273- if (data.endOfMonth () != " " )
276+ if (! data.endOfMonth (). empty () )
274277 endOfMonth = parseBool (data.endOfMonth ());
278+ ext::optional<BusinessDayConvention> endOfMonthConvention = boost::none;
279+ if (!data.endOfMonthConvention ().empty ())
280+ endOfMonthConvention = parseBusinessDayConvention (data.endOfMonthConvention ());
275281
276282 // Ensure that Schedule ctor is passed a vector of unique ordered dates.
277283 std::set<Date> uniqueDates;
278284 for (const string& d : data.dates ())
279285 uniqueDates.insert (calendar.adjust (parseDate (d), convention));
280286
281- return Schedule (vector<Date>(uniqueDates.begin (), uniqueDates.end ()), calendar, convention, boost::none, tenor ,
282- boost::none, endOfMonth);
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 );
283289}
284290
285291Schedule makeSchedule (const ScheduleDerived& data, const Schedule& baseSchedule) {
@@ -314,9 +320,14 @@ Schedule makeSchedule(const ScheduleDerived& data, const Schedule& baseSchedule)
314320 derivedDate = calendar.advance (d, shift, convention);
315321 derivedDates.push_back (derivedDate);
316322 }
317- return Schedule (vector<Date>(derivedDates.begin (), derivedDates.end ()), calendar, convention, boost::none,
318- baseSchedule.tenor (), boost::none, baseSchedule.endOfMonth (), std::vector<bool >(0 ),
319- data.removeFirstDate (), data.removeLastDate ());
323+ ext::optional<BusinessDayConvention> endOfMonthConvention = boost::none;
324+ if (baseSchedule.hasEndOfMonthBusinessDayConvention ())
325+ endOfMonthConvention = baseSchedule.endOfMonthBusinessDayConvention ();
326+
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);
320331}
321332
322333Schedule makeSchedule (const ScheduleRules& data, const Date& openEndDateReplacement) {
@@ -349,6 +360,7 @@ Schedule makeSchedule(const ScheduleRules& data, const Date& openEndDateReplacem
349360 BusinessDayConvention bdcEnd = ModifiedFollowing;
350361 DateGeneration::Rule rule = DateGeneration::Forward;
351362 bool endOfMonth = false ;
363+ ext::optional<BusinessDayConvention> endOfMonthConvention = boost::none;
352364
353365 // now check the strings, if they are empty we take defaults
354366 if (!data.convention ().empty ())
@@ -360,6 +372,8 @@ Schedule makeSchedule(const ScheduleRules& data, const Date& openEndDateReplacem
360372
361373 if (!data.endOfMonth ().empty ())
362374 endOfMonth = parseBool (data.endOfMonth ());
375+ if (!data.endOfMonthConvention ().empty ())
376+ endOfMonthConvention = parseBusinessDayConvention (data.endOfMonthConvention ());
363377
364378 if (!data.rule ().empty ()) {
365379
@@ -369,12 +383,12 @@ Schedule makeSchedule(const ScheduleRules& data, const Date& openEndDateReplacem
369383 auto dates = everyWeekDayDates (startDate, endDate, firstDate, QuantLib::Thursday);
370384 for (auto & d : dates)
371385 d = calendar.adjust (d, bdc);
372- return Schedule (dates, calendar, bdc, bdcEnd, tenor, rule, endOfMonth);
386+ return QuantLib:: Schedule (dates, calendar, bdc, bdcEnd, tenor, rule, endOfMonth, std::vector< bool >( 0 ), false , false , endOfMonthConvention );
373387 } else if (data.rule () == " BusinessWeek" || data.rule () == " CalendarWeek" ) {
374388 auto dates = weeklyDates (startDate, endDate, firstDate, data.rule () == " CalendarWeek" );
375389 for (auto & d : dates)
376390 d = calendar.adjust (d, bdc);
377- return Schedule (dates, calendar, bdc, bdcEnd, tenor, rule, endOfMonth, std::vector<bool >(0 ), data.removeFirstDate (), data.removeLastDate ());
391+ return QuantLib:: Schedule (dates, calendar, bdc, bdcEnd, tenor, rule, endOfMonth, std::vector<bool >(0 ), data.removeFirstDate (), data.removeLastDate (), endOfMonthConvention );
378392 }
379393
380394 // parse rule for further processing below
@@ -391,21 +405,23 @@ Schedule makeSchedule(const ScheduleRules& data, const Date& openEndDateReplacem
391405 // first (last) date of the schedule built in QL with a given first (last) date
392406 // The schedule builder in QL itself is not capable of doing this, it just throws an exception
393407 // if a first (last) date is given in combination with a CDS / CDS2015 date generation rule.
394- std::vector<Date> dates = Schedule (startDate, endDate, tenor, calendar, bdc, bdcEnd, rule, endOfMonth).dates ();
408+ std::vector<Date> dates = QuantLib::Schedule (startDate, endDate, tenor, calendar, bdc, bdcEnd, rule, endOfMonth,
409+ Date (), Date (), false , false , endOfMonthConvention)
410+ .dates ();
395411 QL_REQUIRE (!dates.empty (),
396412 " got empty CDS or CDS2015 schedule, startDate = " << startDate << " , endDate = " << endDate);
397413 if (firstDate != Date ())
398414 dates.front () = firstDate;
399415 if (lastDate != Date ())
400416 dates.back () = lastDate;
401- return Schedule (dates, calendar, bdc, bdcEnd, tenor, rule, endOfMonth, std::vector<bool >(0 ),
402- data.removeFirstDate (), data.removeLastDate ());
417+ return QuantLib:: Schedule (dates, calendar, bdc, bdcEnd, tenor, rule, endOfMonth, std::vector<bool >(0 ),
418+ data.removeFirstDate (), data.removeLastDate (), endOfMonthConvention );
403419 }
404420
405421 // default handling (QuantLib scheduler)
406422
407- return Schedule (startDate, endDate, tenor, calendar, bdc, bdcEnd, rule, endOfMonth, firstDate, lastDate,
408- data.removeFirstDate (), data.removeLastDate ());
423+ return QuantLib:: Schedule (startDate, endDate, tenor, calendar, bdc, bdcEnd, rule, endOfMonth, firstDate, lastDate,
424+ data.removeFirstDate (), data.removeLastDate (), endOfMonthConvention );
409425}
410426
411427namespace {
@@ -465,9 +481,11 @@ Schedule makeSchedule(const ScheduleData& data, const Date& openEndDateReplaceme
465481 Period tenor;
466482 DateGeneration::Rule rule = DateGeneration::Zero; // initialization prevents gcc warning
467483 bool endOfMonth = false ; // initialization prevents gcc warning
484+ BusinessDayConvention endOfMonthConvention = Null<BusinessDayConvention>();
468485 bool hasCalendar = false , hasConvention = false , hasTermConvention = false , hasTenor = false , hasRule = false ,
469- hasEndOfMonth = false , hasConsistentCalendar = true , hasConsistentConvention = true ,
470- hasConsistentTenor = true , hasConsistentRule = true , hasConsistentEndOfMonth = true ;
486+ hasEndOfMonth = false , hasEndOfMonthConvention = false , hasConsistentCalendar = true ,
487+ hasConsistentConvention = true , hasConsistentTenor = true , hasConsistentRule = true ,
488+ hasConsistentEndOfMonth = true , hasConsistentEndOfMonthConvention = true ;
471489 for (auto & d : data.dates ()) {
472490 updateData<Calendar>(d.calendar (), calendar, hasCalendar, hasConsistentCalendar, parseCalendarTemp);
473491 updateData<BusinessDayConvention>(d.convention (), convention, hasConvention, hasConsistentConvention,
@@ -480,6 +498,8 @@ Schedule makeSchedule(const ScheduleData& data, const Date& openEndDateReplaceme
480498 parseBusinessDayConvention);
481499 updateData<Period>(d.tenor (), tenor, hasTenor, hasConsistentTenor, parsePeriod);
482500 updateData<bool >(d.endOfMonth (), endOfMonth, hasEndOfMonth, hasConsistentEndOfMonth, parseBool);
501+ updateData<BusinessDayConvention>(d.endOfMonthConvention (), endOfMonthConvention, hasEndOfMonthConvention,
502+ hasConsistentEndOfMonthConvention, parseBusinessDayConvention);
483503 updateData<DateGeneration::Rule>(d.rule (), rule, hasRule, hasConsistentRule, parseDateGenerationRule);
484504 if (d.termConvention () != " " ) {
485505 hasTermConvention = true ;
@@ -518,13 +538,17 @@ Schedule makeSchedule(const ScheduleData& data, const Date& openEndDateReplaceme
518538 }
519539
520540 // 4) Build schedule
521- return Schedule (dates, hasCalendar && hasConsistentCalendar ? calendar : NullCalendar (),
522- hasConvention && hasConsistentConvention ? convention : Unadjusted,
523- hasTermConvention ? boost::optional<BusinessDayConvention>(termConvention) : boost::none,
524- hasTenor && hasConsistentTenor ? boost::optional<Period>(tenor) : boost::none,
525- hasRule && hasConsistentRule ? boost::optional<DateGeneration::Rule>(rule) : boost::none,
526- hasEndOfMonth && hasConsistentEndOfMonth ? boost::optional<bool >(endOfMonth) : boost::none,
527- isRegular);
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,
548+ false , false ,
549+ hasEndOfMonthConvention && hasConsistentEndOfMonthConvention
550+ ? ext::optional<BusinessDayConvention>(endOfMonthConvention)
551+ : boost::none);
528552 }
529553}
530554} // namespace data
0 commit comments