/** * 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.util.Arrays; /** * Utility for combining and truncating ascending arrays of doubles. This is used for CDS pricing. */ public abstract class DoublesScheduleGenerator { private static final double TOL = 1. / 730; /** * Combines the knot points on the yield and credit curves into a single (ordered) list of times * strictly between the specified start and end. The start and end values are added at the beginning * and end of the list. If two times are very close (defined as less than half a day - 1/730 years different) * only the smaller value is kept (with the exception of the end value which takes precedence). * <p> * Since ISDACompliantCurve is piecewise constant in the forward rate, this makes the integrals * that appear in CDS pricing (i.e. * $$\int_0^T P(t) \frac{dQ(t)}{dt} dt$$ on the protection leg and $$\sum_{i=0}^{N-1}\int_{T_i}^{T_{i+1}} (t-T_i) P(t) \frac{dQ(t)}{dt} dt$$ * on the premium leg) analytic between the points in the list. * * @param start the first time in the list * @param end the last time in the list * @param yieldCurve the yield curve * @param creditCurve the credit curve * @return a list of times used to split CDS pricing integrals into analytic pieces. */ public static double[] getIntegrationsPoints( double start, double end, IsdaCompliantYieldCurve yieldCurve, IsdaCompliantCreditCurve creditCurve) { return getIntegrationsPoints(start, end, yieldCurve.getKnotTimes(), creditCurve.getKnotTimes()); } /** * Combines two sets of numbers and return only the values strictly between the specified * start and end. The start and end values are added at the beginning and end of the list. * If two times are very close (defined as less than half a day - 1/730 years different) * only the smaller value is kept (with the exception of the end value which takes precedence). * * @param start the first time in the list * @param end the last time in the list * @param setA the first set * @param setB the second * @return combined list between first and last value */ public static double[] getIntegrationsPoints(double start, double end, double[] setA, double[] setB) { double[] set1 = truncateSetExclusive(start, end, setA); double[] set2 = truncateSetExclusive(start, end, setB); int n1 = set1.length; int n2 = set2.length; int n = n1 + n2; double[] set = new double[n]; System.arraycopy(set1, 0, set, 0, n1); System.arraycopy(set2, 0, set, n1, n2); Arrays.sort(set); double[] temp = new double[n + 2]; temp[0] = start; int pos = 0; for (int i = 0; i < n; i++) { if (different(temp[pos], set[i])) { temp[++pos] = set[i]; } } if (different(temp[pos], end)) { pos++; } temp[pos] = end; // add the end point (this may replace the last entry in temp if that is not significantly different) int resLength = pos + 1; if (resLength == n + 2) { return temp; // everything was unique } double[] res = new double[resLength]; System.arraycopy(temp, 0, res, 0, resLength); return res; } /** * Combines two sets of numbers (times) and return the unique sorted set. * If two times are very close (defined as less than half a day - 1/730 years different) * only the smaller value is kept. * * @param set1 the first set * @param set2 the second set * @return the unique sorted set, set1 U set2 */ public static double[] combineSets(double[] set1, double[] set2) { int n1 = set1.length; int n2 = set2.length; int n = n1 + n2; double[] set = new double[n]; System.arraycopy(set1, 0, set, 0, n1); System.arraycopy(set2, 0, set, n1, n2); Arrays.sort(set); double[] temp = new double[n]; temp[0] = set[0]; int pos = 0; for (int i = 1; i < n; i++) { if (different(temp[pos], set[i])) { temp[++pos] = set[i]; } } int resLength = pos + 1; if (resLength == n) { return temp; // everything was unique } double[] res = new double[resLength]; System.arraycopy(temp, 0, res, 0, resLength); return res; } private static boolean different(double a, double b) { return Math.abs(a - b) > TOL; } /** * Truncates an array of doubles so it contains only the values between lower and upper, plus * the values of lower and higher (as the first and last entry respectively). If no values met * this criteria an array just containing lower and upper is returned. If the first (last) * entry of set is too close to lower (upper) - defined by TOL - the first (last) entry of * set is replaced by lower (upper). * * @param lower the lower value * @param upper the upper value * @param set the numbers must be sorted in ascending order * @return the truncated array */ public static double[] truncateSetInclusive(double lower, double upper, double[] set) { // this is private, so assume inputs are fine double[] temp = truncateSetExclusive(lower, upper, set); int n = temp.length; if (n == 0) { return new double[] {lower, upper}; } boolean addLower = different(lower, temp[0]); boolean addUpper = different(upper, temp[n - 1]); if (!addLower && !addUpper) { // replace first and last entries of set temp[0] = lower; temp[n - 1] = upper; return temp; } int m = n + (addLower ? 1 : 0) + (addUpper ? 1 : 0); double[] res = new double[m]; System.arraycopy(temp, 0, res, (addLower ? 1 : 0), n); res[0] = lower; res[m - 1] = upper; return res; } /** * Truncates an array of doubles so it contains only the values between lower and upper exclusive. * If no values met this criteria an empty array is returned. * * @param lower the lower value * @param upper the upper value * @param set the numbers must be sorted in ascending order * @return the truncated array */ public static double[] truncateSetExclusive(double lower, double upper, double[] set) { // this is private, so assume inputs are fine int n = set.length; if (upper < set[0] || lower > set[n - 1]) { return new double[0]; } int lIndex; if (lower < set[0]) { lIndex = 0; } else { int temp = Arrays.binarySearch(set, lower); lIndex = temp >= 0 ? temp + 1 : -(temp + 1); } int uIndex; if (upper > set[n - 1]) { uIndex = n; } else { int temp = Arrays.binarySearch(set, lIndex, n, upper); uIndex = temp >= 0 ? temp : -(temp + 1); } int m = uIndex - lIndex; if (m == n) { return set; } double[] trunc = new double[m]; System.arraycopy(set, lIndex, trunc, 0, m); return trunc; } public static double[] leftTruncate(double lower, double[] set) { int n = set.length; if (n == 0) { return set; } if (lower < set[0]) { return set; } if (lower >= set[n - 1]) { return new double[0]; } int index = Arrays.binarySearch(set, lower); int chop = index >= 0 ? index + 1 : -(index + 1); double[] res; if (chop == 0) { res = set; } else { res = new double[n - chop]; System.arraycopy(set, chop, res, 0, n - chop); } return res; } }