/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.impl.credit.isda; import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; import com.opengamma.strata.basics.date.DayCount; import com.opengamma.strata.basics.date.DayCounts; import com.opengamma.strata.collect.ArgChecker; /** * */ public class IsdaCompliantScheduleGenerator { private static final DayCount ACT365 = DayCounts.ACT_365F; /** * This mimics JpmcdsRiskyTimeLine from the ISDA model in c. * * @param startDate start date * @param endDate end date * @param disCurveDates all the points in the discount curve * @param spreadCurveDates all the points in the risky curve * @return An ascending array of dates that is the unique combination of all the input dates * (including startDate and endDate) that are not strictly * before startDate or after EndDate (hence startDate will be the first entry and enddate the last). */ public static LocalDate[] getIntegrationNodesAsDates( LocalDate startDate, LocalDate endDate, LocalDate[] disCurveDates, LocalDate[] spreadCurveDates) { ArgChecker.notNull(startDate, "null startDate"); ArgChecker.notNull(endDate, "null endDate"); ArgChecker.noNulls(disCurveDates, "nulls in disCurveDates"); ArgChecker.noNulls(spreadCurveDates, "nulls in spreadCurveDates"); ArgChecker.isTrue( endDate.isAfter(startDate), "endDate of {} is not after startDate of {}", endDate.toString(), startDate.toString()); int nDisCurvePoints = disCurveDates.length; int nSpreadCurvePoints = spreadCurveDates.length; LinkedHashSet<LocalDate> set = new LinkedHashSet<>(2 + nDisCurvePoints + nSpreadCurvePoints); set.add(startDate); for (LocalDate date : disCurveDates) { set.add(date); } for (LocalDate date : spreadCurveDates) { set.add(date); } set.add(endDate); int n = set.size(); LocalDate[] res = new LocalDate[n]; set.toArray(res); Arrays.sort(res); // remove dates strictly before startDate and strictly after endDate int a = 0; int b = n - 1; while (res[a].isBefore(startDate)) { a++; } while (res[b].isAfter(endDate)) { b--; } int newLength = b - a + 1; if (newLength == n) { return res; // nothing got chopped off } LocalDate[] res2 = new LocalDate[newLength]; System.arraycopy(res, a, res2, 0, newLength); return res2; } /** * This calls getIntegrationNodesAsDates to get an array of dates then calculates the year fraction * from today to those points using ACT/365. * * @param today the date to measure year-fractions from. Must NOT have today after startDate * @param startDate start date * @param endDate end date * @param disCurveDates all the points in the discount curve * @param spreadCurveDates all the points in the risky curve * @return An ascending array of times from today @see getIntegrationNodesAsDates */ public static double[] getIntegrationNodesAsTimes( LocalDate today, LocalDate startDate, LocalDate endDate, LocalDate[] disCurveDates, LocalDate[] spreadCurveDates) { ArgChecker.notNull(today, "null today"); ArgChecker.notNull(startDate, "null startDate"); ArgChecker.isFalse(today.isAfter(startDate), "today is after startDate"); LocalDate[] dates = getIntegrationNodesAsDates(startDate, endDate, disCurveDates, spreadCurveDates); return getYearFractionFromToday(today, dates); } /** * Truncate an sort (ascending) array of dates so the interior values are strictly after startDate * and strictly before endEnd, and startDate and endDate becomes to first and last entries. * * @param startDate This will be the first value in the list * @param endDate This will be the last value in the list * @param dateList Must be sorted * @return dates between startDate and endDate */ public static LocalDate[] truncateList(LocalDate startDate, LocalDate endDate, LocalDate[] dateList) { ArgChecker.notNull(startDate, "null startDate"); ArgChecker.notNull(endDate, "null endDate"); ArgChecker.noNulls(dateList, "nulls in dateList"); ArgChecker.isTrue(endDate.isAfter(startDate), "require enddate after startDate"); int n = dateList.length; if (n == 0) { return new LocalDate[] {startDate, endDate}; } List<LocalDate> temp = new ArrayList<>(n + 2); for (LocalDate d : dateList) { if (d.isAfter(startDate) && d.isBefore(endDate)) { temp.add(d); } } int m = temp.size(); LocalDate[] tArray = new LocalDate[m]; temp.toArray(tArray); LocalDate[] res = new LocalDate[m + 2]; res[0] = startDate; System.arraycopy(tArray, 0, res, 1, m); res[m + 1] = endDate; return res; } /** * Year fractions from a fixed date to a set of dates using ACT/365. * * @param today the date to measure from * @param dates set of dates to measure to * @return set of year fractions (array of double) */ public static double[] getYearFractionFromToday(LocalDate today, LocalDate[] dates) { return getYearFractionFromToday(today, dates, ACT365); } /** * Year fractions from a fixed date to a set of dates using the specified day-count. * * @param today the date to measure from * @param dates the set of dates to measure to * @param dayCount the day-count * @return set of year fractions (array of double) */ public static double[] getYearFractionFromToday(LocalDate today, LocalDate[] dates, DayCount dayCount) { ArgChecker.notNull(today, "null today"); ArgChecker.noNulls(dates, "nulls in dates"); ArgChecker.notNull(dayCount, "null dayCount"); int n = dates.length; double[] res = new double[n]; for (int i = 0; i < n; i++) { res[i] = dayCount.yearFraction(today, dates[i]); } return res; } }