/** * 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 static org.testng.AssertJUnit.assertTrue; import java.time.LocalDate; import java.time.Month; import java.time.Period; import org.testng.annotations.Test; import com.opengamma.strata.basics.date.BusinessDayConvention; import com.opengamma.strata.basics.date.BusinessDayConventions; import com.opengamma.strata.basics.date.DayCount; import com.opengamma.strata.basics.date.DayCounts; import com.opengamma.strata.basics.date.HolidayCalendar; import com.opengamma.strata.basics.date.HolidayCalendars; import com.opengamma.strata.basics.schedule.StubConvention; import com.opengamma.strata.collect.ArgChecker; /** * Test. */ @Test public class MarketQuoteConverterTest { private static final MarketQuoteConverter PUF = new MarketQuoteConverter(); private static final HolidayCalendar DEFAULT_CALENDAR = HolidayCalendars.SAT_SUN; private static final DayCount ACT365 = DayCounts.ACT_365F; private static final DayCount ACT360 = DayCounts.ACT_360; private static final BusinessDayConvention FOLLOWING = BusinessDayConventions.FOLLOWING; private static final LocalDate TODAY = LocalDate.of(2008, Month.SEPTEMBER, 19); private static final LocalDate STEPIN_DATE = TODAY.plusDays(1); private static final LocalDate CASH_SETTLE_DATE = DEFAULT_CALENDAR.shift(TODAY, 3); // AKA valuation date private static final LocalDate START_DATE = LocalDate.of(2007, Month.MARCH, 20); private static final LocalDate END_DATE = LocalDate.of(2015, Month.DECEMBER, 20); private static final LocalDate[] MATURITIES = new LocalDate[] {LocalDate.of(2008, Month.DECEMBER, 20), LocalDate.of(2009, Month.JUNE, 20), LocalDate.of(2010, Month.JUNE, 20), LocalDate.of(2011, Month.JUNE, 20), LocalDate.of(2012, Month.JUNE, 20), LocalDate.of(2014, Month.JUNE, 20), LocalDate.of(2017, Month.JUNE, 20) }; // yield curve private static final LocalDate SPOT_DATE = DEFAULT_CALENDAR.shift(TODAY, 2); private static final IsdaCompliantYieldCurve YIELD_CURVE; static { final int nMoneyMarket = 6; final int nSwaps = 15; final int nInstruments = nMoneyMarket + nSwaps; final IsdaInstrumentTypes[] types = new IsdaInstrumentTypes[nInstruments]; final Period[] tenors = new Period[nInstruments]; final int[] mmMonths = new int[] {1, 2, 3, 6, 9, 12 }; final int[] swapYears = new int[] {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 20, 25, 30 }; // check ArgChecker.isTrue(mmMonths.length == nMoneyMarket, "mmMonths"); ArgChecker.isTrue(swapYears.length == nSwaps, "swapYears"); for (int i = 0; i < nMoneyMarket; i++) { types[i] = IsdaInstrumentTypes.MONEY_MARKET; tenors[i] = Period.ofMonths(mmMonths[i]); } for (int i = nMoneyMarket; i < nInstruments; i++) { types[i] = IsdaInstrumentTypes.SWAP; tenors[i] = Period.ofYears(swapYears[i - nMoneyMarket]); } 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 DayCount moneyMarketDCC = ACT360; final DayCount swapDCC = ACT360; final DayCount curveDCC = ACT365; final Period swapInterval = Period.ofMonths(6); YIELD_CURVE = IsdaCompliantYieldCurveBuild.build(TODAY, SPOT_DATE, types, tenors, rates, moneyMarketDCC, swapDCC, swapInterval, curveDCC, FOLLOWING); } public void SingleCDSTest() { final Period tenor = Period.ofMonths(3); final boolean payAccOnDefault = true; final StubConvention stubType = StubConvention.SHORT_INITIAL; final boolean protectionStart = true; final double pointsUpFront = 0.007; final double permium = 100. / 10000; final double recovery = 0.4; final CdsAnalytic cds = new CdsAnalytic(TODAY, STEPIN_DATE, CASH_SETTLE_DATE, START_DATE, END_DATE, payAccOnDefault, tenor, stubType, protectionStart, recovery); final double parSpread = PUF.pufToQuotedSpread(cds, permium, YIELD_CURVE, pointsUpFront); final double expectedParSpread = 0.011112592882846; // taken from Excel-ISDA 1.8.2 assertEquals("Par Spread", expectedParSpread, parSpread, 1e-14); final double derivedPUF = PUF.quotedSpreadToPUF(cds, permium, YIELD_CURVE, parSpread); assertEquals("PUF", pointsUpFront, derivedPUF, 1e-15); } public void SingleCDSTest2() { final Period tenor = Period.ofMonths(6); final boolean payAccOnDefault = true; final StubConvention stubType = StubConvention.LONG_INITIAL; final boolean protectionStart = true; final double parSpread = 143.4 / 10000; final double permium = 500. / 10000; final double recovery = 0.4; final CdsAnalytic cds = new CdsAnalytic(TODAY, STEPIN_DATE, CASH_SETTLE_DATE, START_DATE, END_DATE, payAccOnDefault, tenor, stubType, protectionStart, recovery); final double puf = PUF.quotedSpreadToPUF(cds, permium, YIELD_CURVE, parSpread); final double expectedPUF = -0.2195134271137960; // taken from Excel-ISDA 1.8.2 assertEquals("PUF", expectedPUF, puf, 5e-13); final double derivedParSpread = PUF.pufToQuotedSpread(cds, permium, YIELD_CURVE, puf); assertEquals("Par Spread", parSpread, derivedParSpread, 1e-15); } public void MultiCDSTest() { final Period tenor = Period.ofMonths(3); final boolean payAccOnDefault = true; final StubConvention stubType = StubConvention.SHORT_INITIAL; final boolean protectionStart = true; final double permium = 100. / 10000; final double recovery = 0.4; final int n = 7; final double[] pointsUpFront = {0.007, 0.008, 0.001, 0.0011, 0.0012, 0.0015, 0.0014 }; // make CDS final CdsAnalytic[] cds = new CdsAnalytic[n]; for (int i = 0; i < n; i++) { cds[i] = new CdsAnalytic(TODAY, STEPIN_DATE, CASH_SETTLE_DATE, START_DATE, MATURITIES[i], payAccOnDefault, tenor, stubType, protectionStart, recovery); } final double[] quotedSpreads = PUF.pufToQuotedSpreads(cds, permium, YIELD_CURVE, pointsUpFront); final double[] parSpreads = PUF.pufToParSpreads(cds, permium, YIELD_CURVE, pointsUpFront); final double[] derivedPUF = PUF.quotedSpreadsToPUF(cds, permium, YIELD_CURVE, quotedSpreads); final double[] derivedPUF2 = PUF.parSpreadsToPUF(cds, permium, YIELD_CURVE, parSpreads); for (int i = 0; i < n; i++) { assertEquals("PUF1", pointsUpFront[i], derivedPUF[i], 1e-15); assertEquals("PUF1", pointsUpFront[i], derivedPUF2[i], 1e-15); } } /** * */ public void consistencyTest() { final double tol = 1.e-13; final AccrualOnDefaultFormulae form = AccrualOnDefaultFormulae.MARKIT_FIX; final MarketQuoteConverter localConv = new MarketQuoteConverter(form); final IsdaCompliantCreditCurveBuilder builder = new FastCreditCurveBuilder(form); final AnalyticCdsPricer pricer = new AnalyticCdsPricer(form); final LocalDate today = LocalDate.of(2011, 4, 21); final LocalDate stepIn = today.plusDays(1); final LocalDate val = DEFAULT_CALENDAR.shift(today, 3); final LocalDate start = ImmDateLogic.getPrevIMMDate(today); final LocalDate end = ImmDateLogic.getPrevIMMDate(today.plusYears(5)); final LocalDate end1 = ImmDateLogic.getPrevIMMDate(today.plusYears(10)); final Period tenor = Period.ofMonths(3); final boolean payAccOnDefault = true; final StubConvention stubType = StubConvention.LONG_INITIAL; final boolean protectionStart = true; final double recovery = 0.4; final double recovery1 = 0.25; final CdsAnalytic cds = new CdsAnalytic(today, stepIn, val, start, end, payAccOnDefault, tenor, stubType, protectionStart, recovery); final CdsAnalytic cds1 = new CdsAnalytic(today, stepIn, val, start, end1, payAccOnDefault, tenor, stubType, protectionStart, recovery1); final double spread = 212. * 1.e-4; final double coupon = 250. * 1.e-4; final double spread1 = 102. * 1.e-4; final double coupon1 = 125. * 1.e-4; final CdsQuotedSpread sp = new CdsQuotedSpread(coupon, spread); final CdsQuotedSpread sp1 = new CdsQuotedSpread(coupon1, spread1); final IsdaCompliantCreditCurve cCurve = builder.calibrateCreditCurve(cds, spread, YIELD_CURVE); /* * cleanPrice and principal */ final double clean = localConv.cleanPrice(cds, YIELD_CURVE, cCurve, coupon); final double cleanExp = localConv.cleanPrice(pricer.pv(cds, YIELD_CURVE, cCurve, coupon, CdsPriceType.CLEAN)); assertEquals(cleanExp, clean, tol); final double notional = 25000.; final double principal = localConv.principal(notional, cds, YIELD_CURVE, cCurve, coupon); final double principalExp = notional * pricer.pv(cds, YIELD_CURVE, cCurve, coupon, CdsPriceType.CLEAN); assertEquals(principalExp, principal, tol); /* * A quoted spread -> A points up-front */ final PointsUpFront puf1 = localConv.convert(cds, sp, YIELD_CURVE); final double puf2 = localConv.pointsUpFront(cds, coupon, YIELD_CURVE, cCurve); assertEquals(puf2, puf1.getPointsUpFront(), tol); assertEquals(coupon, puf1.getCoupon()); final CdsQuotedSpread spRe = localConv.convert(cds, puf1, YIELD_CURVE); assertEquals(spread, spRe.getQuotedSpread(), tol); assertEquals(coupon, spRe.getCoupon()); /* * par spreads -> points up-fronts */ final IsdaCompliantCreditCurve cCurveCol = builder.calibrateCreditCurve(new CdsAnalytic[] {cds, cds1 }, new double[] {spread, spread1 }, YIELD_CURVE); final double[] pufs1 = localConv.pointsUpFront(new CdsAnalytic[] {cds, cds1 }, new double[] {coupon, coupon1 }, YIELD_CURVE, cCurveCol); final double[] pufs1Col = localConv.parSpreadsToPUF(new CdsAnalytic[] {cds, cds1 }, new double[] {coupon, coupon1 }, YIELD_CURVE, new double[] {spread, spread1 }); assertEquals(pufs1[0], pufs1Col[0], tol); assertEquals(pufs1[1], pufs1Col[1], tol); /* * quoted spreads <-> points up-fronts */ final IsdaCompliantCreditCurve cCurve1 = builder.calibrateCreditCurve(cds1, spread1, YIELD_CURVE); final double pufCol = localConv.pointsUpFront(cds, coupon, YIELD_CURVE, cCurve); final double pufCol1 = localConv.pointsUpFront(cds1, coupon1, YIELD_CURVE, cCurve1); final double[] pufs2 = localConv.quotedSpreadsToPUF(new CdsAnalytic[] {cds, cds1 }, new double[] {coupon, coupon1 }, YIELD_CURVE, new double[] {spread, spread1 }); final PointsUpFront[] pufs3 = localConv.convert(new CdsAnalytic[] {cds, cds1 }, new CdsQuotedSpread[] {sp, sp1 }, YIELD_CURVE); assertEquals(pufCol, pufs2[0], tol); assertEquals(pufCol1, pufs2[1], tol); assertEquals(pufs2[0], pufs3[0].getPointsUpFront(), tol); assertEquals(pufs2[1], pufs3[1].getPointsUpFront(), tol); final double[] spsRe = localConv.pufToQuotedSpreads(new CdsAnalytic[] {cds, cds1 }, new double[] {coupon, coupon1 }, YIELD_CURVE, pufs2); assertEquals(spread, spsRe[0], tol); assertEquals(spread1, spsRe[1], tol); final CdsQuotedSpread[] qSpInd = localConv.convert(new CdsAnalytic[] {cds, cds1 }, pufs3, YIELD_CURVE); assertEquals(sp.getQuotedSpread(), qSpInd[0].getQuotedSpread(), tol); assertEquals(sp1.getQuotedSpread(), qSpInd[1].getQuotedSpread(), tol); /* * par spreads <-> quoted spreads, via points up-fronts */ final double[] sps = new double[] {spread, spread * 0.9 }; final double[] qSpFromPSp1 = localConv.parSpreadsToQuotedSpreads(new CdsAnalytic[] {cds, cds1 }, coupon, YIELD_CURVE, sps); final IsdaCompliantCreditCurve cCurveCol1 = builder.calibrateCreditCurve(new CdsAnalytic[] {cds, cds1 }, sps, YIELD_CURVE); final double[] tmpPufs = new double[] {pricer.pv(cds, YIELD_CURVE, cCurveCol1, coupon, CdsPriceType.CLEAN), pricer.pv(cds1, YIELD_CURVE, cCurveCol1, coupon, CdsPriceType.CLEAN) }; final double[] qSpFromPSp1Exp = new double[] {pricer.parSpread(cds, YIELD_CURVE, builder.calibrateCreditCurve(cds, coupon, YIELD_CURVE, tmpPufs[0])), pricer.parSpread(cds1, YIELD_CURVE, builder.calibrateCreditCurve(cds1, coupon, YIELD_CURVE, tmpPufs[1])) }; assertEquals(qSpFromPSp1Exp[0], qSpFromPSp1[0], tol); assertEquals(qSpFromPSp1Exp[1], qSpFromPSp1[1], tol); final double[] qSpFromPSp2 = localConv.parSpreadsToQuotedSpreads(new CdsAnalytic[] {cds, cds1 }, new double[] {coupon, coupon1 }, YIELD_CURVE, new double[] {spread, spread1 }); final double[] qSpFromPSp2Exp = new double[] {pricer.parSpread(cds, YIELD_CURVE, builder.calibrateCreditCurve(cds, coupon, YIELD_CURVE, pufs1[0])), pricer.parSpread(cds1, YIELD_CURVE, builder.calibrateCreditCurve(cds1, coupon1, YIELD_CURVE, pufs1[1])) }; assertEquals(qSpFromPSp2Exp[0], qSpFromPSp2[0], tol); assertEquals(qSpFromPSp2Exp[1], qSpFromPSp2[1], tol); final double[] pspsBack1 = localConv.quotedSpreadToParSpreads(new CdsAnalytic[] {cds, cds1 }, coupon, YIELD_CURVE, qSpFromPSp1); assertEquals(sps[0], pspsBack1[0], tol); assertEquals(sps[1], pspsBack1[1], tol); final double[] pspsBack2 = localConv.quotedSpreadToParSpreads(new CdsAnalytic[] {cds, cds1 }, new double[] {coupon, coupon1 }, YIELD_CURVE, qSpFromPSp2); assertEquals(spread, pspsBack2[0], tol); assertEquals(spread1, pspsBack2[1], tol); /* * Error test */ try { localConv.convert(new CdsAnalytic[] {cds }, qSpInd, YIELD_CURVE); } catch (Exception e) { assertTrue(e instanceof IllegalArgumentException); } try { localConv.quotedSpreadsToPUF(new CdsAnalytic[] {cds }, coupon, YIELD_CURVE, sps); } catch (Exception e) { assertTrue(e instanceof IllegalArgumentException); } try { localConv.quotedSpreadsToPUF(new CdsAnalytic[] {cds, cds1 }, new double[] {coupon }, YIELD_CURVE, sps); } catch (Exception e) { assertTrue(e instanceof IllegalArgumentException); } try { localConv.quotedSpreadsToPUF(new CdsAnalytic[] {cds, cds1 }, new double[] {coupon, coupon1 }, YIELD_CURVE, new double[] {spread }); } catch (Exception e) { assertTrue(e instanceof IllegalArgumentException); } try { localConv.convert(new CdsAnalytic[] {cds }, pufs3, YIELD_CURVE); } catch (Exception e) { assertTrue(e instanceof IllegalArgumentException); } } }