/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.product.credit; import java.time.LocalDate; import java.time.Period; import com.opengamma.strata.collect.ArgChecker; /** * Utility for producing sets of CDS dates. * These are always quarterly on Mar, Jun, Sep, Dec on the 20th of each month */ public final class CdsDatesLogic { private static final int ROLL_DAY = 20; private static final int[] QUARTER_MONTHS = new int[] {3, 6, 9, 12}; private static final int[] INDEX_ROLL_MONTHS = new int[] {3, 9}; /** * Restricted constructor. */ private CdsDatesLogic() { } //------------------------------------------------------------------------- /** * Checks if the specified date is a CDS date. * <p> * CDS dates are the 20th of March, June, September and December. * * @param date the date * @return true is date is an CDS date */ public static boolean isCdsDate(LocalDate date) { return date.getDayOfMonth() == ROLL_DAY && (date.getMonthValue() % 3) == 0; } /** * Checks if the specified date is an index roll date. * <p> * Index roll dates are the 20th of March and September. * * @param date the date * @return true is date is an CDS date */ public static boolean isIndexRollDate(LocalDate date) { if (date.getDayOfMonth() != ROLL_DAY) { return false; } int month = date.getMonthValue(); return month == INDEX_ROLL_MONTHS[0] || month == INDEX_ROLL_MONTHS[1]; } //------------------------------------------------------------------------- /** * Gets a set of CDS dates fixed periods from an initial CDS date. * <p> * The specified date must be a CDS date. * * @param baseCdsDate the base CDS date, where all dates are some interval on from this * @param tenors the periods, typically this would look like 6M, 1Y, 2Y, 3Y, 5Y, 10Y * @return the list of CDS dates */ public static LocalDate[] getCdsDateSet(LocalDate baseCdsDate, Period[] tenors) { // TODO: use lists not arrays? ArgChecker.notNull(baseCdsDate, "baseCdsDate"); ArgChecker.isTrue(isCdsDate(baseCdsDate), "Start date must be a CDS date"); ArgChecker.noNulls(tenors, "tenors"); int size = tenors.length; LocalDate[] result = new LocalDate[size]; for (int i = 0; i < size; i++) { result[i] = baseCdsDate.plus(tenors[i]); } return result; } /** * Gets a complete set of CDS dates from some starting CDS date. * <p> * The specified date will be the first date in the array. * * @param startCdsDate the starting CDS date * @param size the number of dates * @return the list of CDS dates, including the specified date */ public static LocalDate[] getCdsDateSet(LocalDate startCdsDate, int size) { // TODO: use lists not arrays? ArgChecker.notNull(startCdsDate, "startCdsDate"); ArgChecker.isTrue(isCdsDate(startCdsDate), "Start date must be a CDS date"); LocalDate[] result = new LocalDate[size]; result[0] = startCdsDate; for (int i = 1; i < size; i++) { result[i] = result[i - 1].plusMonths(3); } return result; } //------------------------------------------------------------------------- /** * Finds the next CDS date after the specified date. * <p> * This returns the next CDS date from the given date. * If the date is already a CDS date then the next CDS date is returned, 3 months later. * <p> * CDS dates are the 20th of March, June, September and December. * * @param date the date to start from * @return the next CDS date */ public static LocalDate getNextCdsDate(LocalDate date) { int year = date.getYear(); int month = date.getMonthValue(); int day = date.getDayOfMonth(); if (month % 3 == 0) { //in a CDS month if (day < ROLL_DAY) { return LocalDate.of(year, month, ROLL_DAY); } else { return date.withDayOfMonth(ROLL_DAY).plusMonths(3); } } else { return LocalDate.of(year, QUARTER_MONTHS[month / 3], ROLL_DAY); } } /** * Finds the previous CDS date after the specified date. * <p> * This returns the previous CDS date from the given date. * If the date is already a CDS date then the previous CDS date is returned, 3 months earlier. * <p> * CDS dates are the 20th of March, June, September and December. * * @param date the date to start from * @return the previous CDS date */ public static LocalDate getPreviousCdsDate(LocalDate date) { int year = date.getYear(); int month = date.getMonthValue(); int day = date.getDayOfMonth(); if (month % 3 == 0) { //in a CDS month if (day > ROLL_DAY) { return LocalDate.of(year, month, ROLL_DAY); } else { return date.withDayOfMonth(ROLL_DAY).minusMonths(3); } } else { return LocalDate.of(year, QUARTER_MONTHS[month / 3], ROLL_DAY).minusMonths(3); } } //------------------------------------------------------------------------- /** * Finds the next CDS index roll date after the specified date. * <p> * This returns the next CDS index roll date from the given date. * If the date is already a CDS index roll date then the next date is returned, 6 months later. * <p> * CDS index roll dates are the 20th of March and September. * * @param date the date to start from * @return the next CDS index roll date */ public static LocalDate getNextIndexRollDate(LocalDate date) { int year = date.getYear(); int month = date.getMonthValue(); int day = date.getDayOfMonth(); if (isIndexRollDate(date)) { // on an index roll return date.plusMonths(6); } else { if (month < INDEX_ROLL_MONTHS[0]) { return LocalDate.of(year, INDEX_ROLL_MONTHS[0], ROLL_DAY); } else if (month == INDEX_ROLL_MONTHS[0]) { if (day < ROLL_DAY) { return LocalDate.of(year, month, ROLL_DAY); } else { return LocalDate.of(year, INDEX_ROLL_MONTHS[1], ROLL_DAY); } } else if (month < INDEX_ROLL_MONTHS[1]) { return LocalDate.of(year, INDEX_ROLL_MONTHS[1], ROLL_DAY); } else if (month == INDEX_ROLL_MONTHS[1]) { if (day < ROLL_DAY) { return LocalDate.of(year, month, ROLL_DAY); } else { return LocalDate.of(year + 1, INDEX_ROLL_MONTHS[0], ROLL_DAY); } } else { return LocalDate.of(year + 1, INDEX_ROLL_MONTHS[0], ROLL_DAY); } } } }