/** * 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 static org.testng.AssertJUnit.assertEquals; import java.time.LocalDate; import java.time.Month; import java.time.Period; import java.util.Arrays; import com.opengamma.strata.basics.schedule.StubConvention; /** * Test. */ public class CreditCurveCalibrationTest extends IsdaBaseTest { private static final CdsAnalytic[][] PILLAR_CDS; private static final IsdaCompliantYieldCurve[] YIELD_CURVES; private static final double[][] SPREADS; private static final double[][] SUR_PROB_ISDA; private static final double[][] SUR_PROB_MARKIT_FIX; private static final double[] OBS_TIMES = new double[] {30 / 365., 90 / 365., 180. / 365., 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; private static final int N_OBS = OBS_TIMES.length; static { final CdsAnalyticFactory factory = new CdsAnalyticFactory(); final String[] yieldCurvePoints = new String[] {"1M", "2M", "3M", "6M", "9M", "1Y", "2Y", "3Y", "4Y", "5Y", "6Y", "7Y", "8Y", "9Y", "10Y", "11Y", "12Y", "15Y", "20Y", "25Y", "30Y" }; final String[] yieldCurveInstruments = new String[] {"M", "M", "M", "M", "M", "M", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S" }; final double[] rates = new double[] {0.00445, 0.009488, 0.012337, 0.017762, 0.01935, 0.020838, 0.01652, 0.02018, 0.023033, 0.02525, 0.02696, 0.02825, 0.02931, 0.03017, 0.03092, 0.0316, 0.03231, 0.03367, 0.03419, 0.03411, 0.03412 }; final int nCases = 5; PILLAR_CDS = new CdsAnalytic[nCases][]; SPREADS = new double[nCases][]; YIELD_CURVES = new IsdaCompliantYieldCurve[nCases]; SUR_PROB_MARKIT_FIX = new double[nCases][]; SUR_PROB_ISDA = new double[nCases][]; //case1 LocalDate tradeDate = LocalDate.of(2011, Month.JUNE, 19); LocalDate spotDate = DEFAULT_CALENDAR.shift(tradeDate.minusDays(1), 3); Period[] tenors = new Period[] {Period.ofMonths(6), Period.ofYears(1), Period.ofYears(3), Period.ofYears(5), Period.ofYears(7), Period.ofYears(10) }; PILLAR_CDS[0] = factory.makeImmCds(tradeDate, tenors); SPREADS[0] = new double[] {0.00886315689995649, 0.00886315689995649, 0.0133044689825873, 0.0171490070952563, 0.0183903639181293, 0.0194721890639724 }; YIELD_CURVES[0] = makeYieldCurve(tradeDate, spotDate, yieldCurvePoints, yieldCurveInstruments, rates, ACT360, D30360, Period.ofYears(1)); SUR_PROB_ISDA[0] = new double[] {0.998772746815168, 0.996322757048216, 0.992659036212158, 0.985174753005029, 0.959541054444166, 0.9345154655283, 0.897874320219939, 0.862605325653124, 0.830790530716993, 0.80016566917562, 0.76968842828467, 0.740364207356242, 0.71215720464425, 0.685024855452902, 0.658926216751093 }; SUR_PROB_MARKIT_FIX[0] = new double[] {0.998773616100865, 0.996325358510497, 0.992664220011069, 0.985181033285486, 0.959551128356433, 0.934529141029508, 0.897893062747179, 0.862628725130658, 0.830817532293803, 0.800195970143901, 0.76972190245315, 0.740400570243092, 0.712196187570045, 0.685066206017066, 0.658969697981512 }; //case2 tradeDate = LocalDate.of(2011, Month.MARCH, 21); LocalDate effDate = LocalDate.of(2011, Month.MARCH, 20); //not this is a Sunday - for a standard CDS this would roll to the Monday. spotDate = DEFAULT_CALENDAR.shift(tradeDate.minusDays(1), 3); tenors = new Period[] {Period.ofMonths(6), Period.ofYears(1), Period.ofYears(3), Period.ofYears(5), Period.ofYears(7), Period.ofYears(10) }; PILLAR_CDS[1] = factory.makeImmCds(tradeDate, effDate, tenors); SPREADS[1] = new double[] {0.027, 0.018, 0.012, 0.009, 0.007, 0.006 }; YIELD_CURVES[1] = makeYieldCurve(tradeDate, spotDate, yieldCurvePoints, yieldCurveInstruments, rates, ACT360, D30360, Period.ofYears(1)); SUR_PROB_ISDA[1] = new double[] {0.996266535762958, 0.988841371514657, 0.977807258018988, 0.96475844963628, 0.953395823781617, 0.940590393592274, 0.933146036536171, 0.927501935763199, 0.924978347338877, 0.923516383873675, 0.919646843289677, 0.914974439245307, 0.91032577405212, 0.905700727101315, 0.901099178396858 }; SUR_PROB_MARKIT_FIX[1] = new double[] {0.996272873932676, 0.988860244428938, 0.977844583012059, 0.964805380714707, 0.953429040991605, 0.940617833909825, 0.933169293548597, 0.927521552929219, 0.924995002753253, 0.923530307620416, 0.91965942070523, 0.914986149602945, 0.910336625838319, 0.905710728738695, 0.901108338244616, }; //case3 tradeDate = LocalDate.of(2011, Month.MAY, 30); spotDate = DEFAULT_CALENDAR.shift(tradeDate.minusDays(1), 3); LocalDate[] maDates = new LocalDate[] {LocalDate.of(2011, Month.JUNE, 20), LocalDate.of(2012, Month.MAY, 30), LocalDate.of(2014, Month.JUNE, 20), LocalDate.of(2016, Month.JUNE, 20), LocalDate.of(2018, Month.JUNE, 20) }; PILLAR_CDS[2] = factory.withRecoveryRate(0.25).withAccrualDCC(D30360).makeCds(tradeDate, tradeDate.plusDays(1), maDates); SPREADS[2] = new double[] {0.05, 0.05, 0.05, 0.05, 0.05 }; YIELD_CURVES[2] = makeYieldCurve(tradeDate, spotDate, yieldCurvePoints, yieldCurveInstruments, rates, ACT360, D30360, Period.ofYears(1)); SUR_PROB_ISDA[2] = new double[] {0.994488823839325, 0.983690222697363, 0.967711777571664, 0.935677618299157, 0.875533583554252, 0.819255475760025, 0.7666904278069, 0.717503794565525, 0.671435362513808, 0.628322474825315, 0.587977867136078, 0.550223788092265, 0.514893899760578, 0.481832544772127, 0.450894060523028 }; SUR_PROB_MARKIT_FIX[2] = new double[] {0.994492541382389, 0.983716053360113, 0.967769880036333, 0.935798775210736, 0.875741081454824, 0.819537657320969, 0.766996460740263, 0.717827034617184, 0.671770999435863, 0.628667500941574, 0.588329694303598, 0.550580121735183, 0.515252711846802, 0.482192049049632, 0.451252689837008 }; //case4 tradeDate = LocalDate.of(2011, Month.MAY, 30); effDate = LocalDate.of(2011, Month.JULY, 31); spotDate = DEFAULT_CALENDAR.shift(tradeDate.minusDays(1), 3); maDates = new LocalDate[] {LocalDate.of(2011, Month.NOVEMBER, 30), LocalDate.of(2012, Month.MAY, 30), LocalDate.of(2014, Month.MAY, 30), LocalDate.of(2016, Month.MAY, 30), LocalDate.of(2018, Month.MAY, 30), LocalDate.of(2021, Month.MAY, 30) }; PILLAR_CDS[3] = factory.withRecoveryRate(0.25).withAccrualDCC(ACT365F).with(Period.ofMonths(6)).with(StubConvention.LONG_INITIAL) .makeCds(tradeDate, effDate, maDates); SPREADS[3] = new double[] {0.07, 0.06, 0.05, 0.055, 0.06, 0.065 }; YIELD_CURVES[3] = makeYieldCurve(tradeDate, spotDate, yieldCurvePoints, yieldCurveInstruments, rates, ACT360, D30360, Period.ofYears(1)); SUR_PROB_ISDA[3] = new double[] {0.99238650617037, 0.977332973057625, 0.955179740225657, 0.92187587198518, 0.868032006457467, 0.817353939709416, 0.751100020583073, 0.690170357851426, 0.622562049244094, 0.561519352597547, 0.500515112466997, 0.44610942528539, 0.397617603088025, 0.354396812361283, 0.315874095202052 }; SUR_PROB_MARKIT_FIX[3] = new double[] {0.992434753056402, 0.977475525071675, 0.955458402114146, 0.923257693140384, 0.86924227242564, 0.818402338488625, 0.752150342806546, 0.691215773405857, 0.623608833084194, 0.562557270491733, 0.50153493334764, 0.447102836461508, 0.3985783104631, 0.355320200669978, 0.316756937570093 }; //case5 This is designed to trip the low rates/low spreads branch tradeDate = LocalDate.of(2014, Month.JANUARY, 14); spotDate = DEFAULT_CALENDAR.shift(tradeDate.minusDays(1), 3); PILLAR_CDS[4] = factory.makeImmCds(tradeDate, tenors); SPREADS[4] = new double[6]; Arrays.fill(SPREADS[4], ONE_BP); final int n = rates.length; final double[] lowRates = new double[n]; for (int i = 0; i < n; i++) { lowRates[i] = rates[i] / 1000; } YIELD_CURVES[4] = makeYieldCurve(tradeDate, spotDate, yieldCurvePoints, yieldCurveInstruments, lowRates, ACT360, D30360, Period.ofYears(1)); SUR_PROB_ISDA[4] = new double[] {0.999986111241871, 0.999958334304303, 0.999916670344636, 0.999831033196934, 0.999662094963152, 0.999493185285761, 0.999324304350342, 0.999155451994703, 0.998986628218491, 0.998817832978659, 0.998649066279251, 0.998480328100177, 0.998311618432194, 0.998142937270482, 0.997974284610226 }; SUR_PROB_MARKIT_FIX[4] = new double[] {0.999986111408132, 0.999958334803071, 0.999916671342131, 0.999831035053453, 0.999662097437689, 0.999493188187127, 0.999324307673036, 0.9991554557374, 0.998986632383511, 0.998817837566403, 0.998649071291, 0.998480333536109, 0.998311624292164, 0.998142943554348, 0.997974291317845 }; } protected void testCalibrationAgainstISDA(final IsdaCompliantCreditCurveBuilder builder, final double tol) { final int n = YIELD_CURVES.length; final String text = builder.getAccOnDefaultFormula().toString(); final AnalyticCdsPricer pricer = new AnalyticCdsPricer(builder.getAccOnDefaultFormula()); for (int i = 0; i < n; i++) { final IsdaCompliantCreditCurve creditCurve = builder.calibrateCreditCurve(PILLAR_CDS[i], SPREADS[i], YIELD_CURVES[i]); final double[] expected = builder.getAccOnDefaultFormula() == MARKIT_FIX ? SUR_PROB_MARKIT_FIX[i] : SUR_PROB_ISDA[i]; for (int k = 0; k < N_OBS; k++) { assertEquals("failed test case " + (i + 1) + " (" + text + "), node " + k, expected[k], creditCurve.getSurvivalProbability(OBS_TIMES[k]), tol); } final CdsAnalytic[] cds = PILLAR_CDS[i]; final int m = cds.length; for (int j = 0; j < m; j++) { final double p = pricer.pv(cds[j], YIELD_CURVES[i], creditCurve, SPREADS[i][j]); assertEquals("failed test case " + (i + 1) + " (" + text + "), cds: " + j + 1, 0.0, p, 5e-16); } } } }