From 8cdaf2343b0400433beb6369f59a69d42a2d3c69 Mon Sep 17 00:00:00 2001 From: Brian Date: Thu, 19 Oct 2017 18:22:55 +0100 Subject: [PATCH 1/6] Support adjusted start and end dates for roll conventions which always return a day in the month based on a defined rule, such as IMM. --- .../basics/schedule/PeriodicSchedule.java | 77 ++++++++++++------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java b/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java index 763a67fed8..943afce488 100644 --- a/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java +++ b/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java @@ -835,13 +835,9 @@ private LocalDate calculatedUnadjustedStartDate(ReferenceData refData) { // and day-of-month actually differs // and reference data is available // and explicit start adjustment must be NONE (not ideal, but meets backwards compatibility) - int rollDom = rollConvention != null ? rollConvention.getDayOfMonth() : 0; - if (rollDom > 0 && - startDate.getDayOfMonth() != rollDom && - refData != null && - BusinessDayAdjustment.NONE.equals(startDateBusinessDayAdjustment)) { - return calculatedUnadjustedDateFromAdjusted(startDate, rollDom, businessDayAdjustment, refData); + if (refData != null && BusinessDayAdjustment.NONE.equals(startDateBusinessDayAdjustment)) { + return calculatedUnadjustedDateFromAdjusted(startDate, rollConvention, businessDayAdjustment, refData); } return startDate; } @@ -849,29 +845,60 @@ private LocalDate calculatedUnadjustedStartDate(ReferenceData refData) { // calculates the applicable end date // adjust when numeric roll convention present private LocalDate calculatedUnadjustedEndDate(ReferenceData refData) { - int rollDom = rollConvention != null ? rollConvention.getDayOfMonth() : 0; - if (rollDom > 0 && endDate.getDayOfMonth() != rollDom && refData != null) { - return calculatedUnadjustedDateFromAdjusted(endDate, rollDom, calculatedEndDateBusinessDayAdjustment(), refData); + if (refData != null) { + return calculatedUnadjustedDateFromAdjusted(endDate, rollConvention, calculatedEndDateBusinessDayAdjustment(), refData); } return endDate; } - // calculates the applicable date based on the roll day-of-month + // calculates an unadjusted date + // for EOM and day of month roll conventions the unadjusted date is based on the roll day-of-month + // for other conventions, the nearest unadjusted roll date is calculated, adjusted and compared to the base date + // fhis is known not to work for day of week conventions if the passed adjusted date has been rolled forwards private static LocalDate calculatedUnadjustedDateFromAdjusted( LocalDate baseDate, - int rollDom, + RollConvention rollConvention, BusinessDayAdjustment businessDayAdjustment, ReferenceData refData) { - - int lengthOfMonth = baseDate.lengthOfMonth(); - int actualDom = Math.min(rollDom, lengthOfMonth); - // startDate is already the expected day, then nothing to do - if (baseDate.getDayOfMonth() != actualDom) { - LocalDate rollImpliedDate = baseDate.withDayOfMonth(actualDom); - LocalDate adjDate = businessDayAdjustment.adjust(rollImpliedDate, refData); - if (adjDate.equals(baseDate)) { - return rollImpliedDate; + + int rollDom = rollConvention != null ? rollConvention.getDayOfMonth() : 0; + + if (rollDom > 0 && baseDate.getDayOfMonth() != rollDom) { + int lengthOfMonth = baseDate.lengthOfMonth(); + int actualDom = Math.min(rollDom, lengthOfMonth); + // startDate is already the expected day, then nothing to do + if (baseDate.getDayOfMonth() != actualDom) { + LocalDate rollImpliedDate = baseDate.withDayOfMonth(actualDom); + LocalDate adjDate = businessDayAdjustment.adjust(rollImpliedDate, refData); + if (adjDate.equals(baseDate)) { + return rollImpliedDate; + } } + } else if (rollDom == 0) { + //0 roll day implies that the roll date is calculated relative to the month or week + + //Find the valid (unadjusted) roll date for the given month or week + LocalDate unadjustedValidRollDate = rollConvention.adjust(baseDate); + + if(!unadjustedValidRollDate.equals(baseDate)) { + + //If roll date is relative to the month the assumption is that the adjusted date is not in a different month to + //the original unadjusted date. This is safe as the roll day produced by monthly roll conventions are typically + //not close to the end of the month and hence any reasonable adjustment will not move into the next month. + //adjust() method for "day of week" roll conventions will roll forward from the passed date; hence this logic + //will not work for "day of week" conventions if the passed baseDate has been adjusted to be after the original + //unadjusted date (i.e. has been rolled forward). + + //Calculate the expected adjusted roll date, based on the valid unadjusted roll date + LocalDate adjustedRollDate = businessDayAdjustment.adjust(unadjustedValidRollDate, refData); + + //If the adjusted roll date equals the original base date then that the base date is in fact an adjusted date + //and hence return the unadjusted date for building the schedule. + if (adjustedRollDate.equals(baseDate)) { + return unadjustedValidRollDate; + } + } + } return baseDate; } @@ -894,9 +921,8 @@ private LocalDate calculatedFirstRegularStartDate(LocalDate unadjStart, Referenc if (firstRegularStartDate == null) { return unadjStart; } - int rollDom = rollConvention != null ? rollConvention.getDayOfMonth() : 0; - if (rollDom > 0 && firstRegularStartDate.getDayOfMonth() != rollDom && refData != null) { - return calculatedUnadjustedDateFromAdjusted(firstRegularStartDate, rollDom, businessDayAdjustment, refData); + if (refData != null) { + return calculatedUnadjustedDateFromAdjusted(firstRegularStartDate, rollConvention, businessDayAdjustment, refData); } return firstRegularStartDate; } @@ -918,9 +944,8 @@ private LocalDate calculatedLastRegularEndDate(LocalDate unadjEnd, ReferenceData if (lastRegularEndDate == null) { return unadjEnd; } - int rollDom = rollConvention != null ? rollConvention.getDayOfMonth() : 0; - if (rollDom > 0 && lastRegularEndDate.getDayOfMonth() != rollDom && refData != null) { - return calculatedUnadjustedDateFromAdjusted(lastRegularEndDate, rollDom, businessDayAdjustment, refData); + if (refData != null) { + return calculatedUnadjustedDateFromAdjusted(lastRegularEndDate, rollConvention, businessDayAdjustment, refData); } return lastRegularEndDate; } From 36a6402adc91bc113b2380fc2e7905f2ce361b63 Mon Sep 17 00:00:00 2001 From: Brian Date: Thu, 19 Oct 2017 19:56:39 +0100 Subject: [PATCH 2/6] Checkstyle --- .../com/opengamma/strata/basics/schedule/PeriodicSchedule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java b/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java index 943afce488..d934f7e965 100644 --- a/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java +++ b/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java @@ -880,7 +880,7 @@ private static LocalDate calculatedUnadjustedDateFromAdjusted( //Find the valid (unadjusted) roll date for the given month or week LocalDate unadjustedValidRollDate = rollConvention.adjust(baseDate); - if(!unadjustedValidRollDate.equals(baseDate)) { + if (!unadjustedValidRollDate.equals(baseDate)) { //If roll date is relative to the month the assumption is that the adjusted date is not in a different month to //the original unadjusted date. This is safe as the roll day produced by monthly roll conventions are typically From 17cff63574e6a572a09ab8932feb5148cd64d35a Mon Sep 17 00:00:00 2001 From: Brian Date: Fri, 20 Oct 2017 12:47:58 +0100 Subject: [PATCH 3/6] Test fixes --- .../basics/schedule/PeriodicSchedule.java | 69 ++++++++++--------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java b/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java index d934f7e965..80bbb67d89 100644 --- a/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java +++ b/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java @@ -854,51 +854,54 @@ private LocalDate calculatedUnadjustedEndDate(ReferenceData refData) { // calculates an unadjusted date // for EOM and day of month roll conventions the unadjusted date is based on the roll day-of-month // for other conventions, the nearest unadjusted roll date is calculated, adjusted and compared to the base date - // fhis is known not to work for day of week conventions if the passed adjusted date has been rolled forwards + // this is known not to work for day of week conventions if the passed adjusted date has been rolled forwards private static LocalDate calculatedUnadjustedDateFromAdjusted( LocalDate baseDate, RollConvention rollConvention, BusinessDayAdjustment businessDayAdjustment, ReferenceData refData) { - int rollDom = rollConvention != null ? rollConvention.getDayOfMonth() : 0; + if (rollConvention != null) { - if (rollDom > 0 && baseDate.getDayOfMonth() != rollDom) { - int lengthOfMonth = baseDate.lengthOfMonth(); - int actualDom = Math.min(rollDom, lengthOfMonth); - // startDate is already the expected day, then nothing to do - if (baseDate.getDayOfMonth() != actualDom) { - LocalDate rollImpliedDate = baseDate.withDayOfMonth(actualDom); - LocalDate adjDate = businessDayAdjustment.adjust(rollImpliedDate, refData); - if (adjDate.equals(baseDate)) { - return rollImpliedDate; + int rollDom = rollConvention.getDayOfMonth(); + + if (rollDom > 0 && baseDate.getDayOfMonth() != rollDom) { + int lengthOfMonth = baseDate.lengthOfMonth(); + int actualDom = Math.min(rollDom, lengthOfMonth); + // startDate is already the expected day, then nothing to do + if (baseDate.getDayOfMonth() != actualDom) { + LocalDate rollImpliedDate = baseDate.withDayOfMonth(actualDom); + LocalDate adjDate = businessDayAdjustment.adjust(rollImpliedDate, refData); + if (adjDate.equals(baseDate)) { + return rollImpliedDate; + } } - } - } else if (rollDom == 0) { - //0 roll day implies that the roll date is calculated relative to the month or week + } else if (rollDom == 0) { + //0 roll day implies that the roll date is calculated relative to the month or week - //Find the valid (unadjusted) roll date for the given month or week - LocalDate unadjustedValidRollDate = rollConvention.adjust(baseDate); + //Find the valid (unadjusted) roll date for the given month or week + LocalDate unadjustedValidRollDate = rollConvention.adjust(baseDate); - if (!unadjustedValidRollDate.equals(baseDate)) { - - //If roll date is relative to the month the assumption is that the adjusted date is not in a different month to - //the original unadjusted date. This is safe as the roll day produced by monthly roll conventions are typically - //not close to the end of the month and hence any reasonable adjustment will not move into the next month. - //adjust() method for "day of week" roll conventions will roll forward from the passed date; hence this logic - //will not work for "day of week" conventions if the passed baseDate has been adjusted to be after the original - //unadjusted date (i.e. has been rolled forward). - - //Calculate the expected adjusted roll date, based on the valid unadjusted roll date - LocalDate adjustedRollDate = businessDayAdjustment.adjust(unadjustedValidRollDate, refData); - - //If the adjusted roll date equals the original base date then that the base date is in fact an adjusted date - //and hence return the unadjusted date for building the schedule. - if (adjustedRollDate.equals(baseDate)) { - return unadjustedValidRollDate; + if (!unadjustedValidRollDate.equals(baseDate)) { + + //If roll date is relative to the month the assumption is that the adjusted date is not in a different month to + //the original unadjusted date. This is safe as the roll day produced by monthly roll conventions are typically + //not close to the end of the month and hence any reasonable adjustment will not move into the next month. + //adjust() method for "day of week" roll conventions will roll forward from the passed date; hence this logic + //will not work for "day of week" conventions if the passed baseDate has been adjusted to be after the original + //unadjusted date (i.e. has been rolled forward). + + //Calculate the expected adjusted roll date, based on the valid unadjusted roll date + LocalDate adjustedRollDate = businessDayAdjustment.adjust(unadjustedValidRollDate, refData); + + //If the adjusted roll date equals the original base date then that the base date is in fact an adjusted date + //and hence return the unadjusted date for building the schedule. + if (adjustedRollDate.equals(baseDate)) { + return unadjustedValidRollDate; + } } } - + } return baseDate; } From 757213fa5828c3223e4a0a6ab09e6e6c5d6afcb1 Mon Sep 17 00:00:00 2001 From: Brian Date: Fri, 20 Oct 2017 19:11:26 +0100 Subject: [PATCH 4/6] Test for IMM period with adjusted start date --- .../basics/schedule/PeriodicScheduleTest.java | 284 +++++++++++------- 1 file changed, 183 insertions(+), 101 deletions(-) diff --git a/modules/basics/src/test/java/com/opengamma/strata/basics/schedule/PeriodicScheduleTest.java b/modules/basics/src/test/java/com/opengamma/strata/basics/schedule/PeriodicScheduleTest.java index 0379e719fa..9ed3afb05a 100644 --- a/modules/basics/src/test/java/com/opengamma/strata/basics/schedule/PeriodicScheduleTest.java +++ b/modules/basics/src/test/java/com/opengamma/strata/basics/schedule/PeriodicScheduleTest.java @@ -9,12 +9,14 @@ import static com.opengamma.strata.basics.date.BusinessDayConventions.MODIFIED_FOLLOWING; import static com.opengamma.strata.basics.date.BusinessDayConventions.MODIFIED_PRECEDING; import static com.opengamma.strata.basics.date.BusinessDayConventions.PRECEDING; +import static com.opengamma.strata.basics.date.HolidayCalendarIds.JPTO; import static com.opengamma.strata.basics.date.HolidayCalendarIds.NO_HOLIDAYS; import static com.opengamma.strata.basics.date.HolidayCalendarIds.SAT_SUN; import static com.opengamma.strata.basics.schedule.Frequency.P12M; import static com.opengamma.strata.basics.schedule.Frequency.P1M; import static com.opengamma.strata.basics.schedule.Frequency.P2M; import static com.opengamma.strata.basics.schedule.Frequency.P3M; +import static com.opengamma.strata.basics.schedule.Frequency.P6M; import static com.opengamma.strata.basics.schedule.Frequency.TERM; import static com.opengamma.strata.basics.schedule.RollConventions.DAY_11; import static com.opengamma.strata.basics.schedule.RollConventions.DAY_17; @@ -71,6 +73,8 @@ public class PeriodicScheduleTest { private static final StubConvention STUB_NONE = StubConvention.NONE; private static final StubConvention STUB_BOTH = StubConvention.BOTH; private static final BusinessDayAdjustment BDA = BusinessDayAdjustment.of(MODIFIED_FOLLOWING, SAT_SUN); + private static final BusinessDayAdjustment BDA_JPY_MF = BusinessDayAdjustment.of(MODIFIED_FOLLOWING, JPTO); + private static final BusinessDayAdjustment BDA_JPY_P = BusinessDayAdjustment.of(PRECEDING, JPTO); private static final BusinessDayAdjustment BDA_NONE = BusinessDayAdjustment.NONE; private static final LocalDate NOV_29_2013 = date(2013, NOVEMBER, 29); // Fri private static final LocalDate NOV_30_2013 = date(2013, NOVEMBER, 30); // Sat @@ -217,238 +221,246 @@ private PeriodicSchedule createDates(LocalDate start, LocalDate end, LocalDate f Object[][] data_generation() { return new Object[][] { // stub null - {JUN_17, SEP_17, P1M, null, null, null, null, null, + {JUN_17, SEP_17, P1M, null, null, BDA, null, null, null, ImmutableList.of(JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, // stub NONE - {JUN_17, SEP_17, P1M, STUB_NONE, null, null, null, null, + {JUN_17, SEP_17, P1M, STUB_NONE, null, BDA, null, null, null, ImmutableList.of(JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, - {JUN_17, JUL_17, P1M, STUB_NONE, null, null, null, null, + {JUN_17, JUL_17, P1M, STUB_NONE, null, BDA, null, null, null, ImmutableList.of(JUN_17, JUL_17), ImmutableList.of(JUN_17, JUL_17), DAY_17}, // stub SHORT_INITIAL - {JUN_04, SEP_17, P1M, SHORT_INITIAL, null, null, null, null, + {JUN_04, SEP_17, P1M, SHORT_INITIAL, null, BDA, null, null, null, ImmutableList.of(JUN_04, JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_04, JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, - {JUN_17, SEP_17, P1M, SHORT_INITIAL, null, null, null, null, + {JUN_17, SEP_17, P1M, SHORT_INITIAL, null, BDA, null, null, null, ImmutableList.of(JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, - {JUN_17, JUL_04, P1M, SHORT_INITIAL, null, null, null, null, + {JUN_17, JUL_04, P1M, SHORT_INITIAL, null, BDA, null, null, null, ImmutableList.of(JUN_17, JUL_04), ImmutableList.of(JUN_17, JUL_04), DAY_4}, - {date(2011, 6, 28), date(2011, 6, 30), P1M, SHORT_INITIAL, EOM, null, null, null, + {date(2011, 6, 28), date(2011, 6, 30), P1M, SHORT_INITIAL, EOM, BDA, null, null, null, ImmutableList.of(date(2011, 6, 28), date(2011, 6, 30)), ImmutableList.of(date(2011, 6, 28), date(2011, 6, 30)), EOM}, - {date(2014, 12, 12), date(2015, 8, 24), P3M, SHORT_INITIAL, null, null, null, null, + {date(2014, 12, 12), date(2015, 8, 24), P3M, SHORT_INITIAL, null, BDA, null, null, null, ImmutableList.of(date(2014, 12, 12), date(2015, 2, 24), date(2015, 5, 24), date(2015, 8, 24)), ImmutableList.of(date(2014, 12, 12), date(2015, 2, 24), date(2015, 5, 25), date(2015, 8, 24)), DAY_24}, - {date(2014, 12, 12), date(2015, 8, 24), P3M, SHORT_INITIAL, RollConventions.NONE, null, null, null, + {date(2014, 12, 12), date(2015, 8, 24), P3M, SHORT_INITIAL, RollConventions.NONE, BDA, null, null, null, ImmutableList.of(date(2014, 12, 12), date(2015, 2, 24), date(2015, 5, 24), date(2015, 8, 24)), ImmutableList.of(date(2014, 12, 12), date(2015, 2, 24), date(2015, 5, 25), date(2015, 8, 24)), DAY_24}, - {date(2014, 11, 24), date(2015, 8, 24), P3M, null, RollConventions.NONE, null, null, null, + {date(2014, 11, 24), date(2015, 8, 24), P3M, null, RollConventions.NONE, BDA, null, null, null, ImmutableList.of(date(2014, 11, 24), date(2015, 2, 24), date(2015, 5, 24), date(2015, 8, 24)), ImmutableList.of(date(2014, 11, 24), date(2015, 2, 24), date(2015, 5, 25), date(2015, 8, 24)), DAY_24}, // stub LONG_INITIAL - {JUN_04, SEP_17, P1M, LONG_INITIAL, null, null, null, null, + {JUN_04, SEP_17, P1M, LONG_INITIAL, null, BDA, null, null, null, ImmutableList.of(JUN_04, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_04, JUL_17, AUG_18, SEP_17), DAY_17}, - {JUN_17, SEP_17, P1M, LONG_INITIAL, null, null, null, null, + {JUN_17, SEP_17, P1M, LONG_INITIAL, null, BDA, null, null, null, ImmutableList.of(JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, - {JUN_17, JUL_04, P1M, LONG_INITIAL, null, null, null, null, + {JUN_17, JUL_04, P1M, LONG_INITIAL, null, BDA, null, null, null, ImmutableList.of(JUN_17, JUL_04), ImmutableList.of(JUN_17, JUL_04), DAY_4}, - {JUN_17, AUG_04, P1M, LONG_INITIAL, null, null, null, null, + {JUN_17, AUG_04, P1M, LONG_INITIAL, null, BDA, null, null, null, ImmutableList.of(JUN_17, AUG_04), ImmutableList.of(JUN_17, AUG_04), DAY_4}, // stub SHORT_FINAL - {JUN_04, SEP_17, P1M, SHORT_FINAL, null, null, null, null, + {JUN_04, SEP_17, P1M, SHORT_FINAL, null, BDA, null, null, null, ImmutableList.of(JUN_04, JUL_04, AUG_04, SEP_04, SEP_17), ImmutableList.of(JUN_04, JUL_04, AUG_04, SEP_04, SEP_17), DAY_4}, - {JUN_17, SEP_17, P1M, SHORT_FINAL, null, null, null, null, + {JUN_17, SEP_17, P1M, SHORT_FINAL, null, BDA, null, null, null, ImmutableList.of(JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, - {JUN_17, JUL_04, P1M, SHORT_FINAL, null, null, null, null, + {JUN_17, JUL_04, P1M, SHORT_FINAL, null, BDA, null, null, null, ImmutableList.of(JUN_17, JUL_04), ImmutableList.of(JUN_17, JUL_04), DAY_17}, - {date(2011, 6, 28), date(2011, 6, 30), P1M, SHORT_FINAL, EOM, null, null, null, + {date(2011, 6, 28), date(2011, 6, 30), P1M, SHORT_FINAL, EOM, BDA, null, null, null, ImmutableList.of(date(2011, 6, 28), date(2011, 6, 30)), ImmutableList.of(date(2011, 6, 28), date(2011, 6, 30)), DAY_28}, - {date(2014, 11, 29), date(2015, 9, 2), P3M, SHORT_FINAL, null, null, null, null, + {date(2014, 11, 29), date(2015, 9, 2), P3M, SHORT_FINAL, null, BDA, null, null, null, ImmutableList.of(date(2014, 11, 29), date(2015, 2, 28), date(2015, 5, 29), date(2015, 8, 29), date(2015, 9, 2)), ImmutableList.of(date(2014, 11, 28), date(2015, 2, 27), date(2015, 5, 29), date(2015, 8, 31), date(2015, 9, 2)), DAY_29}, - {date(2014, 11, 29), date(2015, 9, 2), P3M, SHORT_FINAL, RollConventions.NONE, null, null, null, + {date(2014, 11, 29), date(2015, 9, 2), P3M, SHORT_FINAL, RollConventions.NONE, BDA, null, null, null, ImmutableList.of(date(2014, 11, 29), date(2015, 2, 28), date(2015, 5, 29), date(2015, 8, 29), date(2015, 9, 2)), ImmutableList.of(date(2014, 11, 28), date(2015, 2, 27), date(2015, 5, 29), date(2015, 8, 31), date(2015, 9, 2)), DAY_29}, // stub LONG_FINAL - {JUN_04, SEP_17, P1M, LONG_FINAL, null, null, null, null, + {JUN_04, SEP_17, P1M, LONG_FINAL, null, BDA, null, null, null, ImmutableList.of(JUN_04, JUL_04, AUG_04, SEP_17), ImmutableList.of(JUN_04, JUL_04, AUG_04, SEP_17), DAY_4}, - {JUN_17, SEP_17, P1M, LONG_FINAL, null, null, null, null, + {JUN_17, SEP_17, P1M, LONG_FINAL, null, BDA, null, null, null, ImmutableList.of(JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, - {JUN_17, JUL_04, P1M, LONG_FINAL, null, null, null, null, + {JUN_17, JUL_04, P1M, LONG_FINAL, null, BDA, null, null, null, ImmutableList.of(JUN_17, JUL_04), ImmutableList.of(JUN_17, JUL_04), DAY_17}, - {JUN_17, AUG_04, P1M, LONG_FINAL, null, null, null, null, + {JUN_17, AUG_04, P1M, LONG_FINAL, null, BDA, null, null, null, ImmutableList.of(JUN_17, AUG_04), ImmutableList.of(JUN_17, AUG_04), DAY_17}, // explicit initial stub - {JUN_04, SEP_17, P1M, null, null, JUN_17, null, null, + {JUN_04, SEP_17, P1M, null, null, BDA, JUN_17, null, null, ImmutableList.of(JUN_04, JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_04, JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, - {JUN_04, SEP_17, P1M, SHORT_INITIAL, null, JUN_17, null, null, + {JUN_04, SEP_17, P1M, SHORT_INITIAL, null, BDA, JUN_17, null, null, ImmutableList.of(JUN_04, JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_04, JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, - {JUN_17, SEP_17, P1M, null, null, JUN_17, null, null, + {JUN_17, SEP_17, P1M, null, null, BDA, JUN_17, null, null, ImmutableList.of(JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, // explicit final stub - {JUN_04, SEP_17, P1M, null, null, null, AUG_04, null, + {JUN_04, SEP_17, P1M, null, null, BDA, null, AUG_04, null, ImmutableList.of(JUN_04, JUL_04, AUG_04, SEP_17), ImmutableList.of(JUN_04, JUL_04, AUG_04, SEP_17), DAY_4}, - {JUN_04, SEP_17, P1M, SHORT_FINAL, null, null, AUG_04, null, + {JUN_04, SEP_17, P1M, SHORT_FINAL, null, BDA, null, AUG_04, null, ImmutableList.of(JUN_04, JUL_04, AUG_04, SEP_17), ImmutableList.of(JUN_04, JUL_04, AUG_04, SEP_17), DAY_4}, - {JUN_17, SEP_17, P1M, null, null, null, AUG_17, null, + {JUN_17, SEP_17, P1M, null, null, BDA, null, AUG_17, null, ImmutableList.of(JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, // explicit double stub - {JUN_04, SEP_17, P1M, null, null, JUL_11, AUG_11, null, + {JUN_04, SEP_17, P1M, null, null, BDA, JUL_11, AUG_11, null, ImmutableList.of(JUN_04, JUL_11, AUG_11, SEP_17), ImmutableList.of(JUN_04, JUL_11, AUG_11, SEP_17), DAY_11}, - {JUN_04, OCT_17, P1M, STUB_BOTH, null, JUL_11, SEP_11, null, + {JUN_04, OCT_17, P1M, STUB_BOTH, null, BDA, JUL_11, SEP_11, null, ImmutableList.of(JUN_04, JUL_11, AUG_11, SEP_11, OCT_17), ImmutableList.of(JUN_04, JUL_11, AUG_11, SEP_11, OCT_17), DAY_11}, - {JUN_17, SEP_17, P1M, null, null, JUN_17, SEP_17, null, + {JUN_17, SEP_17, P1M, null, null, BDA, JUN_17, SEP_17, null, ImmutableList.of(JUN_17, JUL_17, AUG_17, SEP_17), ImmutableList.of(JUN_17, JUL_17, AUG_18, SEP_17), DAY_17}, // near end of month // EOM flag false, thus roll on 30th - {NOV_30_2013, NOV_30, P3M, STUB_NONE, null, null, null, null, + {NOV_30_2013, NOV_30, P3M, STUB_NONE, null, BDA, null, null, null, ImmutableList.of(NOV_30_2013, FEB_28, MAY_30, AUG_30, NOV_30), ImmutableList.of(NOV_29_2013, FEB_28, MAY_30, date(2014, AUGUST, 29), date(2014, NOVEMBER, 28)), DAY_30}, // EOM flag true and is EOM, thus roll at EOM - {NOV_30_2013, NOV_30, P3M, STUB_NONE, EOM, null, null, null, + {NOV_30_2013, NOV_30, P3M, STUB_NONE, EOM, BDA, null, null, null, ImmutableList.of(NOV_30_2013, FEB_28, MAY_31, AUG_31, NOV_30), ImmutableList.of(NOV_29_2013, FEB_28, MAY_30, date(2014, AUGUST, 29), date(2014, NOVEMBER, 28)), EOM}, // EOM flag true, but not EOM, thus roll on 30th (stub convention defined) - {MAY_30, NOV_30, P3M, STUB_NONE, EOM, null, null, null, + {MAY_30, NOV_30, P3M, STUB_NONE, EOM, BDA, null, null, null, ImmutableList.of(MAY_30, AUG_30, NOV_30), ImmutableList.of(MAY_30, date(2014, AUGUST, 29), date(2014, NOVEMBER, 28)), DAY_30}, // EOM flag true, but not EOM, thus roll on 30th (no stub convention defined) - {MAY_30, NOV_30, P3M, null, EOM, null, null, null, + {MAY_30, NOV_30, P3M, null, EOM, BDA, null, null, null, ImmutableList.of(MAY_30, AUG_30, NOV_30), ImmutableList.of(MAY_30, date(2014, AUGUST, 29), date(2014, NOVEMBER, 28)), DAY_30}, // EOM flag true and is EOM, double stub, thus roll at EOM - {date(2014, 1, 3), SEP_17, P3M, STUB_BOTH, EOM, FEB_28, AUG_31, null, + {date(2014, 1, 3), SEP_17, P3M, STUB_BOTH, EOM, BDA, FEB_28, AUG_31, null, ImmutableList.of(date(2014, 1, 3), FEB_28, MAY_31, AUG_31, SEP_17), ImmutableList.of(date(2014, 1, 3), FEB_28, MAY_30, date(2014, AUGUST, 29), SEP_17), EOM}, // EOM flag true plus start date as last business day of month with start date adjust of NONE - {NOV_29_2013, NOV_30, P3M, STUB_NONE, EOM, null, null, BDA_NONE, + {NOV_29_2013, NOV_30, P3M, STUB_NONE, EOM, BDA, null, null, BDA_NONE, ImmutableList.of(NOV_30_2013, FEB_28, MAY_31, AUG_31, NOV_30), ImmutableList.of(NOV_29_2013, FEB_28, MAY_30, date(2014, AUGUST, 29), date(2014, NOVEMBER, 28)), EOM}, // EOM flag true plus start date as last business day of month with start date adjust of NONE - {NOV_29_2013, NOV_30, P3M, null, EOM, null, null, BDA_NONE, + {NOV_29_2013, NOV_30, P3M, null, EOM, BDA, null, null, BDA_NONE, ImmutableList.of(NOV_30_2013, FEB_28, MAY_31, AUG_31, NOV_30), ImmutableList.of(NOV_29_2013, FEB_28, MAY_30, date(2014, AUGUST, 29), date(2014, NOVEMBER, 28)), EOM}, // EOM flag false, short initial, implies EOM true - {date(2011, 6, 2), date(2011, 8, 31), P1M, SHORT_INITIAL, null, null, null, null, + {date(2011, 6, 2), date(2011, 8, 31), P1M, SHORT_INITIAL, null, BDA, null, null, null, ImmutableList.of(date(2011, 6, 2), date(2011, 6, 30), date(2011, 7, 31), date(2011, 8, 31)), ImmutableList.of(date(2011, 6, 2), date(2011, 6, 30), date(2011, 7, 29), date(2011, 8, 31)), EOM}, // EOM flag false, explicit stub, implies EOM true - {date(2011, 6, 2), date(2011, 8, 31), P1M, null, null, date(2011, 6, 30), null, null, + {date(2011, 6, 2), date(2011, 8, 31), P1M, null, null, BDA, date(2011, 6, 30), null, null, ImmutableList.of(date(2011, 6, 2), date(2011, 6, 30), date(2011, 7, 31), date(2011, 8, 31)), ImmutableList.of(date(2011, 6, 2), date(2011, 6, 30), date(2011, 7, 29), date(2011, 8, 31)), EOM}, // EOM flag false, explicit stub, implies EOM true - {date(2011, 7, 31), date(2011, 10, 10), P1M, null, null, null, date(2011, 9, 30), null, + {date(2011, 7, 31), date(2011, 10, 10), P1M, null, null, BDA, null, date(2011, 9, 30), null, ImmutableList.of(date(2011, 7, 31), date(2011, 8, 31), date(2011, 9, 30), date(2011, 10, 10)), ImmutableList.of(date(2011, 7, 29), date(2011, 8, 31), date(2011, 9, 30), date(2011, 10, 10)), EOM}, // EOM flag false, explicit stub, implies EOM true - {date(2011, 2, 2), date(2011, 5, 30), P1M, null, null, date(2011, 2, 28), null, null, + {date(2011, 2, 2), date(2011, 5, 30), P1M, null, null, BDA, date(2011, 2, 28), null, null, ImmutableList.of(date(2011, 2, 2), date(2011, 2, 28), date(2011, 3, 30), date(2011, 4, 30), date(2011, 5, 30)), ImmutableList.of(date(2011, 2, 2), date(2011, 2, 28), date(2011, 3, 30), date(2011, 4, 29), date(2011, 5, 30)), DAY_30}, // pre-adjusted start date, no change needed - {JUL_17, OCT_17, P1M, null, DAY_17, null, null, BDA_NONE, + {JUL_17, OCT_17, P1M, null, DAY_17, BDA, null, null, BDA_NONE, ImmutableList.of(JUL_17, AUG_17, SEP_17, OCT_17), ImmutableList.of(JUL_17, AUG_18, SEP_17, OCT_17), DAY_17}, // pre-adjusted start date, change needed - {AUG_18, OCT_17, P1M, null, DAY_17, null, null, BDA_NONE, + {AUG_18, OCT_17, P1M, null, DAY_17, BDA, null, null, BDA_NONE, ImmutableList.of(AUG_17, SEP_17, OCT_17), ImmutableList.of(AUG_18, SEP_17, OCT_17), DAY_17}, // pre-adjusted first regular, change needed - {JUL_11, OCT_17, P1M, null, DAY_17, AUG_18, null, BDA_NONE, + {JUL_11, OCT_17, P1M, null, DAY_17, BDA, AUG_18, null, BDA_NONE, ImmutableList.of(JUL_11, AUG_17, SEP_17, OCT_17), ImmutableList.of(JUL_11, AUG_18, SEP_17, OCT_17), DAY_17}, // pre-adjusted last regular, change needed - {JUL_17, OCT_17, P1M, null, DAY_17, null, AUG_18, BDA_NONE, + {JUL_17, OCT_17, P1M, null, DAY_17, BDA, null, AUG_18, BDA_NONE, ImmutableList.of(JUL_17, AUG_17, OCT_17), ImmutableList.of(JUL_17, AUG_18, OCT_17), DAY_17}, // pre-adjusted first+last regular, change needed - {APR_01, OCT_17, P1M, null, DAY_17, MAY_19, AUG_18, BDA_NONE, + {APR_01, OCT_17, P1M, null, DAY_17, BDA, MAY_19, AUG_18, BDA_NONE, ImmutableList.of(APR_01, MAY_17, JUN_17, JUL_17, AUG_17, OCT_17), ImmutableList.of(APR_01, MAY_19, JUN_17, JUL_17, AUG_18, OCT_17), DAY_17}, // pre-adjusted end date, change needed - {JUL_17, AUG_18, P1M, null, DAY_17, null, null, BDA_NONE, + {JUL_17, AUG_18, P1M, null, DAY_17, BDA, null, null, BDA_NONE, ImmutableList.of(JUL_17, AUG_17), ImmutableList.of(JUL_17, AUG_18), DAY_17}, // pre-adjusted end date, change needed, with adjustment - {JUL_17, AUG_18, P1M, null, DAY_17, null, null, BDA, + {JUL_17, AUG_18, P1M, null, DAY_17, BDA, null, null, BDA, ImmutableList.of(JUL_17, AUG_17), ImmutableList.of(JUL_17, AUG_18), DAY_17}, // TERM period - {JUN_04, SEP_17, TERM, STUB_NONE, null, null, null, null, + {JUN_04, SEP_17, TERM, STUB_NONE, null, BDA, null, null, null, ImmutableList.of(JUN_04, SEP_17), ImmutableList.of(JUN_04, SEP_17), ROLL_NONE}, // TERM period defined as a stub and no regular periods - {JUN_04, SEP_17, P12M, SHORT_INITIAL, null, SEP_17, null, null, + {JUN_04, SEP_17, P12M, SHORT_INITIAL, null, BDA, SEP_17, null, null, ImmutableList.of(JUN_04, SEP_17), ImmutableList.of(JUN_04, SEP_17), DAY_17}, - {JUN_04, SEP_17, P12M, SHORT_INITIAL, null, null, JUN_04, null, + {JUN_04, SEP_17, P12M, SHORT_INITIAL, null, BDA, null, JUN_04, null, ImmutableList.of(JUN_04, SEP_17), ImmutableList.of(JUN_04, SEP_17), DAY_4}, - {date(2014, 9, 24), date(2016, 11, 24), Frequency.ofYears(2), SHORT_INITIAL, null, null, null, null, + {date(2014, 9, 24), date(2016, 11, 24), Frequency.ofYears(2), SHORT_INITIAL, null, BDA, null, null, null, ImmutableList.of(date(2014, 9, 24), date(2014, 11, 24), date(2016, 11, 24)), ImmutableList.of(date(2014, 9, 24), date(2014, 11, 24), date(2016, 11, 24)), DAY_24}, // IMM - {date(2014, 9, 17), date(2014, 10, 15), P1M, STUB_NONE, IMM, null, null, null, + {date(2014, 9, 17), date(2014, 10, 15), P1M, STUB_NONE, IMM, BDA, null, null, null, ImmutableList.of(date(2014, 9, 17), date(2014, 10, 15)), ImmutableList.of(date(2014, 9, 17), date(2014, 10, 15)), IMM}, - {date(2014, 9, 17), date(2014, 10, 15), TERM, STUB_NONE, IMM, null, null, null, + {date(2014, 9, 17), date(2014, 10, 15), TERM, STUB_NONE, IMM, BDA, null, null, null, ImmutableList.of(date(2014, 9, 17), date(2014, 10, 15)), ImmutableList.of(date(2014, 9, 17), date(2014, 10, 15)), IMM}, // IMM with stupid short period still works - {date(2014, 9, 17), date(2014, 10, 15), Frequency.ofDays(2), STUB_NONE, IMM, null, null, null, + {date(2014, 9, 17), date(2014, 10, 15), Frequency.ofDays(2), STUB_NONE, IMM, BDA, null, null, null, ImmutableList.of(date(2014, 9, 17), date(2014, 10, 15)), ImmutableList.of(date(2014, 9, 17), date(2014, 10, 15)), IMM}, - {date(2014, 9, 17), date(2014, 10, 1), Frequency.ofDays(2), STUB_NONE, IMM, null, null, null, + {date(2014, 9, 17), date(2014, 10, 1), Frequency.ofDays(2), STUB_NONE, IMM, BDA, null, null, null, ImmutableList.of(date(2014, 9, 17), date(2014, 10, 1)), ImmutableList.of(date(2014, 9, 17), date(2014, 10, 1)), IMM}, + //IMM with adjusted start dates and various conventions + {date(2018, 3, 22), date(2020, 03, 18), P6M, STUB_NONE, IMM, BDA_JPY_MF, null, null, BDA_NONE, + ImmutableList.of(date(2018, 3, 21), date(2018, 9, 19), date(2019, 3, 20), date(2019, 9, 18), date(2020, 3, 18)), + ImmutableList.of(date(2018, 3, 22), date(2018, 9, 19), date(2019, 3, 20), date(2019, 9, 18), date(2020, 3, 18)), IMM}, + {date(2018, 3, 20), date(2019, 03, 20), P6M, STUB_NONE, IMM, BDA_JPY_P, null, null, BDA_NONE, + ImmutableList.of(date(2018, 3, 21), date(2018, 9, 19), date(2019, 3, 20)), + ImmutableList.of(date(2018, 3, 20), date(2018, 9, 19), date(2019, 3, 20)), IMM}, + // Day30 rolling with February - {date(2015, 1, 30), date(2015, 4, 30), P1M, STUB_NONE, DAY_30, null, null, null, + {date(2015, 1, 30), date(2015, 4, 30), P1M, STUB_NONE, DAY_30, BDA, null, null, null, ImmutableList.of(date(2015, 1, 30), date(2015, 2, 28), date(2015, 3, 30), date(2015, 4, 30)), ImmutableList.of(date(2015, 1, 30), date(2015, 2, 27), date(2015, 3, 30), date(2015, 4, 30)), DAY_30}, - {date(2015, 2, 28), date(2015, 4, 30), P1M, STUB_NONE, DAY_30, null, null, null, + {date(2015, 2, 28), date(2015, 4, 30), P1M, STUB_NONE, DAY_30, BDA, null, null, null, ImmutableList.of(date(2015, 2, 28), date(2015, 3, 30), date(2015, 4, 30)), ImmutableList.of(date(2015, 2, 27), date(2015, 3, 30), date(2015, 4, 30)), DAY_30}, - {date(2015, 2, 28), date(2015, 4, 30), P1M, SHORT_INITIAL, DAY_30, null, null, null, + {date(2015, 2, 28), date(2015, 4, 30), P1M, SHORT_INITIAL, DAY_30, BDA, null, null, null, ImmutableList.of(date(2015, 2, 28), date(2015, 3, 30), date(2015, 4, 30)), ImmutableList.of(date(2015, 2, 27), date(2015, 3, 30), date(2015, 4, 30)), DAY_30}, }; @@ -456,15 +468,25 @@ Object[][] data_generation() { @Test(dataProvider = "generation") public void test_monthly_schedule( - LocalDate start, LocalDate end, Frequency freq, StubConvention stubConv, RollConvention rollConv, - LocalDate firstReg, LocalDate lastReg, BusinessDayAdjustment startBusDayAdjustment, - List unadjusted, List adjusted, RollConvention expRoll) { + LocalDate start, + LocalDate end, + Frequency freq, + StubConvention stubConv, + RollConvention rollConv, + BusinessDayAdjustment businessDayAdjustment, + LocalDate firstReg, + LocalDate lastReg, + BusinessDayAdjustment startBusDayAdjustment, + List unadjusted, + List adjusted, + RollConvention expRoll) { + PeriodicSchedule defn = PeriodicSchedule.builder() .startDate(start) .endDate(end) .frequency(freq) .startDateBusinessDayAdjustment(startBusDayAdjustment) - .businessDayAdjustment(BDA) + .businessDayAdjustment(businessDayAdjustment) .stubConvention(stubConv) .rollConvention(rollConv) .firstRegularStartDate(firstReg) @@ -485,15 +507,25 @@ public void test_monthly_schedule( @Test(dataProvider = "generation") public void test_monthly_schedule_withOverride( - LocalDate start, LocalDate end, Frequency freq, StubConvention stubConv, RollConvention rollConv, - LocalDate firstReg, LocalDate lastReg, BusinessDayAdjustment startBusDayAdjustment, - List unadjusted, List adjusted, RollConvention expRoll) { + LocalDate start, + LocalDate end, + Frequency freq, + StubConvention stubConv, + RollConvention rollConv, + BusinessDayAdjustment businessDayAdjustment, + LocalDate firstReg, + LocalDate lastReg, + BusinessDayAdjustment startBusDayAdjustment, + List unadjusted, + List adjusted, + RollConvention expRoll) { + PeriodicSchedule defn = PeriodicSchedule.builder() .startDate(start) .endDate(end) .frequency(freq) .startDateBusinessDayAdjustment(startBusDayAdjustment) - .businessDayAdjustment(BDA) + .businessDayAdjustment(businessDayAdjustment) .stubConvention(stubConv) .rollConvention(rollConv) .firstRegularStartDate(firstReg) @@ -520,15 +552,25 @@ public void test_monthly_schedule_withOverride( @Test(dataProvider = "generation") public void test_monthly_unadjusted( - LocalDate start, LocalDate end, Frequency freq, StubConvention stubConv, RollConvention rollConv, - LocalDate firstReg, LocalDate lastReg, BusinessDayAdjustment startBusDayAdjustment, - List unadjusted, List adjusted, RollConvention expRoll) { + LocalDate start, + LocalDate end, + Frequency freq, + StubConvention stubConv, + RollConvention rollConv, + BusinessDayAdjustment businessDayAdjustment, + LocalDate firstReg, + LocalDate lastReg, + BusinessDayAdjustment startBusDayAdjustment, + List unadjusted, + List adjusted, + RollConvention expRoll) { + PeriodicSchedule defn = PeriodicSchedule.builder() .startDate(start) .endDate(end) .frequency(freq) .startDateBusinessDayAdjustment(startBusDayAdjustment) - .businessDayAdjustment(BDA) + .businessDayAdjustment(businessDayAdjustment) .stubConvention(stubConv) .rollConvention(rollConv) .firstRegularStartDate(firstReg) @@ -545,15 +587,25 @@ public void test_monthly_unadjusted( @Test(dataProvider = "generation") public void test_monthly_unadjusted_withOverride( - LocalDate start, LocalDate end, Frequency freq, StubConvention stubConv, RollConvention rollConv, - LocalDate firstReg, LocalDate lastReg, BusinessDayAdjustment startBusDayAdjustment, - List unadjusted, List adjusted, RollConvention expRoll) { + LocalDate start, + LocalDate end, + Frequency freq, + StubConvention stubConv, + RollConvention rollConv, + BusinessDayAdjustment businessDayAdjustment, + LocalDate firstReg, + LocalDate lastReg, + BusinessDayAdjustment startBusDayAdjustment, + List unadjusted, + List adjusted, + RollConvention expRoll) { + PeriodicSchedule defn = PeriodicSchedule.builder() .startDate(start) .endDate(end) .frequency(freq) .startDateBusinessDayAdjustment(startBusDayAdjustment) - .businessDayAdjustment(BDA) + .businessDayAdjustment(businessDayAdjustment) .stubConvention(stubConv) .rollConvention(rollConv) .firstRegularStartDate(firstReg) @@ -573,15 +625,25 @@ public void test_monthly_unadjusted_withOverride( @Test(dataProvider = "generation") public void test_monthly_adjusted( - LocalDate start, LocalDate end, Frequency freq, StubConvention stubConv, RollConvention rollConv, - LocalDate firstReg, LocalDate lastReg, BusinessDayAdjustment startBusDayAdjustment, - List unadjusted, List adjusted, RollConvention expRoll) { + LocalDate start, + LocalDate end, + Frequency freq, + StubConvention stubConv, + RollConvention rollConv, + BusinessDayAdjustment businessDayAdjustment, + LocalDate firstReg, + LocalDate lastReg, + BusinessDayAdjustment startBusDayAdjustment, + List unadjusted, + List adjusted, + RollConvention expRoll) { + PeriodicSchedule defn = PeriodicSchedule.builder() .startDate(start) .endDate(end) .frequency(freq) .startDateBusinessDayAdjustment(startBusDayAdjustment) - .businessDayAdjustment(BDA) + .businessDayAdjustment(businessDayAdjustment) .stubConvention(stubConv) .rollConvention(rollConv) .firstRegularStartDate(firstReg) @@ -593,15 +655,25 @@ public void test_monthly_adjusted( @Test(dataProvider = "generation") public void test_monthly_adjusted_withOverride( - LocalDate start, LocalDate end, Frequency freq, StubConvention stubConv, RollConvention rollConv, - LocalDate firstReg, LocalDate lastReg, BusinessDayAdjustment startBusDayAdjustment, - List unadjusted, List adjusted, RollConvention expRoll) { + LocalDate start, + LocalDate end, + Frequency freq, + StubConvention stubConv, + RollConvention rollConv, + BusinessDayAdjustment businessDayAdjustment, + LocalDate firstReg, + LocalDate lastReg, + BusinessDayAdjustment startBusDayAdjustment, + List unadjusted, + List adjusted, + RollConvention expRoll) { + PeriodicSchedule defn = PeriodicSchedule.builder() .startDate(start) .endDate(end) .frequency(freq) .startDateBusinessDayAdjustment(startBusDayAdjustment) - .businessDayAdjustment(BDA) + .businessDayAdjustment(businessDayAdjustment) .stubConvention(stubConv) .rollConvention(rollConv) .firstRegularStartDate(firstReg) @@ -961,26 +1033,36 @@ public LocalDate next(LocalDate date, Frequency frequency) { //------------------------------------------------------------------------- @Test(dataProvider = "generation") public void coverage_equals( - LocalDate start, LocalDate end, Frequency freq, StubConvention stubConv, RollConvention rollConv, - LocalDate firstReg, LocalDate lastReg, BusinessDayAdjustment startBusDayAdjustment, - List unadjusted, List adjusted, RollConvention expRoll) { - PeriodicSchedule a1 = of(start, end, freq, BDA, stubConv, rollConv, firstReg, lastReg, null, null, null); - PeriodicSchedule a2 = of(start, end, freq, BDA, stubConv, rollConv, firstReg, lastReg, null, null, null); - PeriodicSchedule b = of(LocalDate.MIN, end, freq, BDA, stubConv, rollConv, firstReg, lastReg, null, null, null); - PeriodicSchedule c = of(start, LocalDate.MAX, freq, BDA, stubConv, rollConv, firstReg, lastReg, null, null, null); + LocalDate start, + LocalDate end, + Frequency freq, + StubConvention stubConv, + RollConvention rollConv, + BusinessDayAdjustment busDayAdjustment, + LocalDate firstReg, + LocalDate lastReg, + BusinessDayAdjustment startBusDayAdjustment, + List unadjusted, + List adjusted, + RollConvention expRoll) { + + PeriodicSchedule a1 = of(start, end, freq, busDayAdjustment, stubConv, rollConv, firstReg, lastReg, null, null, null); + PeriodicSchedule a2 = of(start, end, freq, busDayAdjustment, stubConv, rollConv, firstReg, lastReg, null, null, null); + PeriodicSchedule b = of(LocalDate.MIN, end, freq, busDayAdjustment, stubConv, rollConv, firstReg, lastReg, null, null, null); + PeriodicSchedule c = of(start, LocalDate.MAX, freq, busDayAdjustment, stubConv, rollConv, firstReg, lastReg, null, null, null); PeriodicSchedule d = of( - start, end, freq == P1M ? P3M : P1M, BDA, stubConv, rollConv, firstReg, lastReg, null, null, null); + start, end, freq == P1M ? P3M : P1M, busDayAdjustment, stubConv, rollConv, firstReg, lastReg, null, null, null); PeriodicSchedule e = of( start, end, freq, BDA_NONE, stubConv, rollConv, firstReg, lastReg, null, null, null); PeriodicSchedule f = of( - start, end, freq, BDA, stubConv == STUB_NONE ? SHORT_FINAL : STUB_NONE, rollConv, firstReg, lastReg, null, null, null); - PeriodicSchedule g = of(start, end, freq, BDA, stubConv, SFE, firstReg, lastReg, null, null, null); - PeriodicSchedule h = of(start, end, freq, BDA, stubConv, rollConv, start.plusDays(1), null, null, null, null); - PeriodicSchedule i = of(start, end, freq, BDA, stubConv, rollConv, null, end.minusDays(1), null, null, null); - PeriodicSchedule j = of(start, end, freq, BDA, stubConv, rollConv, firstReg, lastReg, BDA, null, null); - PeriodicSchedule k = of(start, end, freq, BDA, stubConv, rollConv, firstReg, lastReg, null, BDA, null); + start, end, freq, busDayAdjustment, stubConv == STUB_NONE ? SHORT_FINAL : STUB_NONE, rollConv, firstReg, lastReg, null, null, null); + PeriodicSchedule g = of(start, end, freq, busDayAdjustment, stubConv, SFE, firstReg, lastReg, null, null, null); + PeriodicSchedule h = of(start, end, freq, busDayAdjustment, stubConv, rollConv, start.plusDays(1), null, null, null, null); + PeriodicSchedule i = of(start, end, freq, busDayAdjustment, stubConv, rollConv, null, end.minusDays(1), null, null, null); + PeriodicSchedule j = of(start, end, freq, busDayAdjustment, stubConv, rollConv, firstReg, lastReg, BDA, null, null); + PeriodicSchedule k = of(start, end, freq, busDayAdjustment, stubConv, rollConv, firstReg, lastReg, null, BDA, null); PeriodicSchedule m = of( - start, end, freq, BDA, stubConv, rollConv, firstReg, lastReg, null, null, AdjustableDate.of(start.minusDays(1))); + start, end, freq, busDayAdjustment, stubConv, rollConv, firstReg, lastReg, null, null, AdjustableDate.of(start.minusDays(1))); assertEquals(a1.equals(a1), true); assertEquals(a1.equals(a2), true); assertEquals(a1.equals(b), false); From ed32718990f6277b28f985f769b0c2ede49cb225 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 23 Oct 2017 10:50:27 +0100 Subject: [PATCH 5/6] Improve comments --- .../strata/basics/schedule/PeriodicSchedule.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java b/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java index 80bbb67d89..f30332b868 100644 --- a/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java +++ b/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java @@ -830,11 +830,16 @@ private RollConvention calculatedRollConvention(LocalDate calculatedFirstRegStar // applies de facto rule where EOM means last business day for startDate // and similar rule for numeric roll conventions // http://www.fpml.org/forums/topic/can-a-roll-convention-imply-a-stub/#post-7659 + // For 'StandardRollConventions', such as IMM, adjusted date is identified by finding the closest valid roll date + // and applying the the trade level business day adjustment private LocalDate calculatedUnadjustedStartDate(ReferenceData refData) { - // change date if numeric roll convention - // and day-of-month actually differs - // and reference data is available + // change date if + // reference data is available // and explicit start adjustment must be NONE (not ideal, but meets backwards compatibility) + // and either + // numeric roll convention and day-of-month actually differs + // or + // StandardDayConvention is used and the day is not a valid roll date if (refData != null && BusinessDayAdjustment.NONE.equals(startDateBusinessDayAdjustment)) { return calculatedUnadjustedDateFromAdjusted(startDate, rollConvention, businessDayAdjustment, refData); @@ -843,7 +848,6 @@ private LocalDate calculatedUnadjustedStartDate(ReferenceData refData) { } // calculates the applicable end date - // adjust when numeric roll convention present private LocalDate calculatedUnadjustedEndDate(ReferenceData refData) { if (refData != null) { return calculatedUnadjustedDateFromAdjusted(endDate, rollConvention, calculatedEndDateBusinessDayAdjustment(), refData); @@ -854,7 +858,7 @@ private LocalDate calculatedUnadjustedEndDate(ReferenceData refData) { // calculates an unadjusted date // for EOM and day of month roll conventions the unadjusted date is based on the roll day-of-month // for other conventions, the nearest unadjusted roll date is calculated, adjusted and compared to the base date - // this is known not to work for day of week conventions if the passed adjusted date has been rolled forwards + // this is known not to work for day of week conventions if the passed date has been adjusted forwards private static LocalDate calculatedUnadjustedDateFromAdjusted( LocalDate baseDate, RollConvention rollConvention, From 1a70dc94c3b35f0f707a245e26c0673bb4d7d31b Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 23 Oct 2017 17:34:51 +0100 Subject: [PATCH 6/6] PR feedback --- .../basics/schedule/PeriodicSchedule.java | 75 +++++++++---------- .../basics/schedule/PeriodicScheduleTest.java | 18 +++++ 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java b/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java index f30332b868..1c4d9118f1 100644 --- a/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java +++ b/modules/basics/src/main/java/com/opengamma/strata/basics/schedule/PeriodicSchedule.java @@ -841,7 +841,9 @@ private LocalDate calculatedUnadjustedStartDate(ReferenceData refData) { // or // StandardDayConvention is used and the day is not a valid roll date - if (refData != null && BusinessDayAdjustment.NONE.equals(startDateBusinessDayAdjustment)) { + if (refData != null && + rollConvention != null && + BusinessDayAdjustment.NONE.equals(startDateBusinessDayAdjustment)) { return calculatedUnadjustedDateFromAdjusted(startDate, rollConvention, businessDayAdjustment, refData); } return startDate; @@ -849,7 +851,7 @@ private LocalDate calculatedUnadjustedStartDate(ReferenceData refData) { // calculates the applicable end date private LocalDate calculatedUnadjustedEndDate(ReferenceData refData) { - if (refData != null) { + if (refData != null && rollConvention != null) { return calculatedUnadjustedDateFromAdjusted(endDate, rollConvention, calculatedEndDateBusinessDayAdjustment(), refData); } return endDate; @@ -865,44 +867,41 @@ private static LocalDate calculatedUnadjustedDateFromAdjusted( BusinessDayAdjustment businessDayAdjustment, ReferenceData refData) { - if (rollConvention != null) { + int rollDom = rollConvention.getDayOfMonth(); + + if (rollDom > 0 && baseDate.getDayOfMonth() != rollDom) { + int lengthOfMonth = baseDate.lengthOfMonth(); + int actualDom = Math.min(rollDom, lengthOfMonth); + // startDate is already the expected day, then nothing to do + if (baseDate.getDayOfMonth() != actualDom) { + LocalDate rollImpliedDate = baseDate.withDayOfMonth(actualDom); + LocalDate adjDate = businessDayAdjustment.adjust(rollImpliedDate, refData); + if (adjDate.equals(baseDate)) { + return rollImpliedDate; + } + } + } else if (rollDom == 0) { + //0 roll day implies that the roll date is calculated relative to the month or week - int rollDom = rollConvention.getDayOfMonth(); + //Find the valid (unadjusted) roll date for the given month or week + LocalDate rollImpliedDate = rollConvention.adjust(baseDate); - if (rollDom > 0 && baseDate.getDayOfMonth() != rollDom) { - int lengthOfMonth = baseDate.lengthOfMonth(); - int actualDom = Math.min(rollDom, lengthOfMonth); - // startDate is already the expected day, then nothing to do - if (baseDate.getDayOfMonth() != actualDom) { - LocalDate rollImpliedDate = baseDate.withDayOfMonth(actualDom); - LocalDate adjDate = businessDayAdjustment.adjust(rollImpliedDate, refData); - if (adjDate.equals(baseDate)) { - return rollImpliedDate; - } - } - } else if (rollDom == 0) { - //0 roll day implies that the roll date is calculated relative to the month or week + if (!rollImpliedDate.equals(baseDate)) { + + //If roll date is relative to the month the assumption is that the adjusted date is not in a different month to + //the original unadjusted date. This is safe as the roll day produced by monthly roll conventions are typically + //not close to the end of the month and hence any reasonable adjustment will not move into the next month. + //adjust() method for "day of week" roll conventions will roll forward from the passed date; hence this logic + //will not work for "day of week" conventions if the passed baseDate has been adjusted to be after the original + //unadjusted date (i.e. has been rolled forward). - //Find the valid (unadjusted) roll date for the given month or week - LocalDate unadjustedValidRollDate = rollConvention.adjust(baseDate); + //Calculate the expected adjusted roll date, based on the valid unadjusted roll date + LocalDate adjDate = businessDayAdjustment.adjust(rollImpliedDate, refData); - if (!unadjustedValidRollDate.equals(baseDate)) { - - //If roll date is relative to the month the assumption is that the adjusted date is not in a different month to - //the original unadjusted date. This is safe as the roll day produced by monthly roll conventions are typically - //not close to the end of the month and hence any reasonable adjustment will not move into the next month. - //adjust() method for "day of week" roll conventions will roll forward from the passed date; hence this logic - //will not work for "day of week" conventions if the passed baseDate has been adjusted to be after the original - //unadjusted date (i.e. has been rolled forward). - - //Calculate the expected adjusted roll date, based on the valid unadjusted roll date - LocalDate adjustedRollDate = businessDayAdjustment.adjust(unadjustedValidRollDate, refData); - - //If the adjusted roll date equals the original base date then that the base date is in fact an adjusted date - //and hence return the unadjusted date for building the schedule. - if (adjustedRollDate.equals(baseDate)) { - return unadjustedValidRollDate; - } + //If the adjusted roll date equals the original base date then that the base date is in fact an adjusted date + //and hence return the unadjusted date for building the schedule. + if (adjDate.equals(baseDate)) { + return rollImpliedDate; } } @@ -928,7 +927,7 @@ private LocalDate calculatedFirstRegularStartDate(LocalDate unadjStart, Referenc if (firstRegularStartDate == null) { return unadjStart; } - if (refData != null) { + if (refData != null && rollConvention != null) { return calculatedUnadjustedDateFromAdjusted(firstRegularStartDate, rollConvention, businessDayAdjustment, refData); } return firstRegularStartDate; @@ -951,7 +950,7 @@ private LocalDate calculatedLastRegularEndDate(LocalDate unadjEnd, ReferenceData if (lastRegularEndDate == null) { return unadjEnd; } - if (refData != null) { + if (refData != null && rollConvention != null) { return calculatedUnadjustedDateFromAdjusted(lastRegularEndDate, rollConvention, businessDayAdjustment, refData); } return lastRegularEndDate; diff --git a/modules/basics/src/test/java/com/opengamma/strata/basics/schedule/PeriodicScheduleTest.java b/modules/basics/src/test/java/com/opengamma/strata/basics/schedule/PeriodicScheduleTest.java index 9ed3afb05a..960338429d 100644 --- a/modules/basics/src/test/java/com/opengamma/strata/basics/schedule/PeriodicScheduleTest.java +++ b/modules/basics/src/test/java/com/opengamma/strata/basics/schedule/PeriodicScheduleTest.java @@ -446,12 +446,30 @@ Object[][] data_generation() { ImmutableList.of(date(2014, 9, 17), date(2014, 10, 1)), IMM}, //IMM with adjusted start dates and various conventions + //MF, no stub {date(2018, 3, 22), date(2020, 03, 18), P6M, STUB_NONE, IMM, BDA_JPY_MF, null, null, BDA_NONE, ImmutableList.of(date(2018, 3, 21), date(2018, 9, 19), date(2019, 3, 20), date(2019, 9, 18), date(2020, 3, 18)), ImmutableList.of(date(2018, 3, 22), date(2018, 9, 19), date(2019, 3, 20), date(2019, 9, 18), date(2020, 3, 18)), IMM}, + //Preceding, no stub {date(2018, 3, 20), date(2019, 03, 20), P6M, STUB_NONE, IMM, BDA_JPY_P, null, null, BDA_NONE, ImmutableList.of(date(2018, 3, 21), date(2018, 9, 19), date(2019, 3, 20)), ImmutableList.of(date(2018, 3, 20), date(2018, 9, 19), date(2019, 3, 20)), IMM}, + //MF, null stub + {date(2018, 3, 22), date(2019, 03, 20), P6M, null, IMM, BDA_JPY_MF, null, null, BDA_NONE, + ImmutableList.of(date(2018, 3, 21), date(2018, 9, 19), date(2019, 3, 20)), + ImmutableList.of(date(2018, 3, 22), date(2018, 9, 19), date(2019, 3, 20)), IMM}, + //Explicit long front stub with (adjusted) first regular start date + {date(2017, 9, 2), date(2018, 9, 19), P6M, LONG_INITIAL, IMM, BDA_JPY_MF, date(2018, 3, 22), null, BDA_NONE, + ImmutableList.of(date(2017, 9, 2), date(2018, 3, 21), date(2018, 9, 19)), + ImmutableList.of(date(2017, 9, 2), date(2018, 3, 22), date(2018, 9, 19)), IMM}, + //Implicit short front stub with (adjusted) first regular start date + {date(2018, 1, 2), date(2018, 9, 19), P6M, null, IMM, BDA_JPY_MF, date(2018, 3, 22), null, BDA_NONE, + ImmutableList.of(date(2018, 1, 2), date(2018, 3, 21), date(2018, 9, 19)), + ImmutableList.of(date(2018, 1, 2), date(2018, 3, 22), date(2018, 9, 19)), IMM}, + //Implicit back stub with (adjusted) last regular start date + {date(2017, 3, 15), date(2018, 5, 19), P6M, null, IMM, BDA_JPY_MF, null, date(2018, 3, 22), BDA_NONE, + ImmutableList.of(date(2017, 3, 15), date(2017, 9, 20), date(2018, 3, 21), date(2018, 5, 19)), + ImmutableList.of(date(2017, 3, 15), date(2017, 9, 20), date(2018, 3, 22), date(2018, 5, 21)), IMM}, // Day30 rolling with February {date(2015, 1, 30), date(2015, 4, 30), P1M, STUB_NONE, DAY_30, BDA, null, null, null,