Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support adjusted start date for StandardRollConventions (e.g. IMM). #1580

Merged
merged 6 commits into from Oct 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -830,48 +830,81 @@ 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)
int rollDom = rollConvention != null ? rollConvention.getDayOfMonth() : 0;
if (rollDom > 0 &&
startDate.getDayOfMonth() != rollDom &&
refData != null &&
BusinessDayAdjustment.NONE.equals(startDateBusinessDayAdjustment)) {
// and either
// numeric roll convention and day-of-month actually differs
// or
// StandardDayConvention is used and the day is not a valid roll date

return calculatedUnadjustedDateFromAdjusted(startDate, rollDom, businessDayAdjustment, refData);
if (refData != null &&
rollConvention != null &&
BusinessDayAdjustment.NONE.equals(startDateBusinessDayAdjustment)) {
return calculatedUnadjustedDateFromAdjusted(startDate, rollConvention, businessDayAdjustment, refData);
}
return startDate;
}

// 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 && rollConvention != 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
// 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,
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.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

//Find the valid (unadjusted) roll date for the given month or week
LocalDate rollImpliedDate = rollConvention.adjust(baseDate);

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).

//Calculate the expected adjusted roll date, based on the valid unadjusted roll date
LocalDate adjDate = businessDayAdjustment.adjust(rollImpliedDate, 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 (adjDate.equals(baseDate)) {
return rollImpliedDate;
}
}

}
return baseDate;
}
Expand All @@ -894,9 +927,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 && rollConvention != null) {
return calculatedUnadjustedDateFromAdjusted(firstRegularStartDate, rollConvention, businessDayAdjustment, refData);
}
return firstRegularStartDate;
}
Expand All @@ -918,9 +950,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 && rollConvention != null) {
return calculatedUnadjustedDateFromAdjusted(lastRegularEndDate, rollConvention, businessDayAdjustment, refData);
}
return lastRegularEndDate;
}
Expand Down