/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.impl.credit.isda.e2e; import static com.opengamma.strata.pricer.impl.credit.isda.ImmDateLogic.getNextIMMDate; import static com.opengamma.strata.pricer.impl.credit.isda.ImmDateLogic.getPrevIMMDate; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import java.time.LocalDate; import java.time.Period; import java.util.Arrays; import java.util.Locale; import org.testng.annotations.Test; import com.opengamma.strata.basics.date.DayCount; import com.opengamma.strata.basics.schedule.StubConvention; import com.opengamma.strata.pricer.impl.credit.isda.AnalyticCdsPricer; import com.opengamma.strata.pricer.impl.credit.isda.AnalyticSpreadSensitivityCalculator; import com.opengamma.strata.pricer.impl.credit.isda.CdsAnalytic; import com.opengamma.strata.pricer.impl.credit.isda.CdsAnalyticFactory; import com.opengamma.strata.pricer.impl.credit.isda.CdsParSpread; import com.opengamma.strata.pricer.impl.credit.isda.CdsPriceType; import com.opengamma.strata.pricer.impl.credit.isda.CdsQuoteConvention; import com.opengamma.strata.pricer.impl.credit.isda.CdsQuotedSpread; import com.opengamma.strata.pricer.impl.credit.isda.FastCreditCurveBuilder; import com.opengamma.strata.pricer.impl.credit.isda.FiniteDifferenceSpreadSensitivityCalculator; import com.opengamma.strata.pricer.impl.credit.isda.IsdaBaseTest; import com.opengamma.strata.pricer.impl.credit.isda.IsdaCompliantCreditCurve; import com.opengamma.strata.pricer.impl.credit.isda.IsdaCompliantCreditCurveBuilder; import com.opengamma.strata.pricer.impl.credit.isda.IsdaCompliantYieldCurve; import com.opengamma.strata.pricer.impl.credit.isda.IsdaCompliantYieldCurveBuild; import com.opengamma.strata.pricer.impl.credit.isda.MarketQuoteConverter; import com.opengamma.strata.pricer.impl.credit.isda.PointsUpFront; import com.opengamma.strata.pricer.impl.credit.isda.SuperFastCreditCurveBuilder; import com.opengamma.strata.pricer.impl.credit.isda.YieldCurveProvider; /** * The purpose of this class is the demonstrate the API for OpenGamma's implementation of the ISDA standard and extensions * we have built. Each method (test) demonstrates a new feature and outputs some results to the console - ideally they should * be read and executed in order. */ @Test(enabled = false) public class CdsAnalyticsDemo extends IsdaBaseTest { private static final LocalDate TRADE_DATE = LocalDate.of(2014, 6, 30); private static final double[] RATES = new double[] { 0.001515, 0.001965, 0.002346, 0.003268, 0.005451, 0.005875, 0.010035, 0.013935, 0.017125, 0.01973, 0.021855, 0.02364, 0.025135, 0.02638, 0.028405, 0.030435, 0.032225, 0.033055, 0.033435}; private static final IsdaCompliantYieldCurve YIELD_CURVE = YieldCurveProvider.makeUSDCurve(TRADE_DATE, RATES); private static final LocalDate[] TEST_DATES = new LocalDate[] {LocalDate.of(2014, 7, 2), LocalDate.of(2014, 7, 10), LocalDate.of(2014, 7, 30), LocalDate.of(2014, 9, 1), LocalDate.of(2014, 10, 1), LocalDate.of(2014, 11, 1), LocalDate.of(2014, 12, 1), LocalDate.of(2015, 1, 1), LocalDate.of(2016, 1, 1), LocalDate.of(2017, 1, 1), LocalDate.of(2018, 1, 1)}; /** * Show the construction of a ISDA model yield curve. {@link IsdaCompliantYieldCurveBuild} is the class that does the actual work, but * {@link YieldCurveProvider} provides tools to set up standard (i.e. USD, EUR, GBP & JPY) yield curves.<p> * The main point of this example is to show what is meant by a <i>shifted</i> yield curve */ public void yieldCurveDemo() { System.out.println("\nyieldCurveDemo"); //The yield curve is snapped on 29-Jun-2014 and the spot date is 2-Jul-2014 (three working days on) final LocalDate spotDate = DEFAULT_CALENDAR.shift(TRADE_DATE.minusDays(1), 3); //USD conventions final String[] periods = new String[] { "1M", "2M", "3M", "6M", "1Y", "2Y", "3Y", "4Y", "5Y", "6Y", "7Y", "8Y", "9Y", "10Y", "12Y", "15Y", "20Y", "25Y", "30Y"}; final String[] types = new String[] { "M", "M", "M", "M", "M", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}; final DayCount mmDCC = ACT360; final DayCount swapDCC = D30360; final Period swapInterval = Period.ofMonths(6); //build shifted yield curve, by specifying all parameters final IsdaCompliantYieldCurve yc_shifted = YieldCurveProvider.makeYieldCurve( TRADE_DATE, spotDate, periods, types, RATES, mmDCC, swapDCC, swapInterval, DEFAULT_CALENDAR); //cannot use equals directly as the curves have different names assertTrue(Arrays.equals(YIELD_CURVE.getKnotTimes(), yc_shifted.getKnotTimes())); assertTrue(Arrays.equals(YIELD_CURVE.getKnotZeroRates(), yc_shifted.getKnotZeroRates())); //build unshifted yield curve final IsdaCompliantYieldCurve yc_unshifted = YieldCurveProvider.makeYieldCurve( spotDate, spotDate, periods, types, RATES, mmDCC, swapDCC, swapInterval, DEFAULT_CALENDAR); //check the shifted and unshifted curves are equivalent - po is the discount factor between the CDS trade date and //the spot date of the yield curve instruments final double p0 = yc_shifted.getDiscountFactor(ACT365F.yearFraction(TRADE_DATE, spotDate)); final int n = TEST_DATES.length; for (int i = 0; i < n; i++) { final double t1 = ACT365F.yearFraction(TRADE_DATE, TEST_DATES[i]); final double t2 = ACT365F.yearFraction(spotDate, TEST_DATES[i]); final double df1 = YIELD_CURVE.getDiscountFactor(t1); final double df2 = p0 * yc_unshifted.getDiscountFactor(t2); assertEquals(df1, df2, 1e-15); } //this should be cut and pasted into Excel to view the curve final int nSamples = 100; System.out.println("Time\tDiscount Factor\tZero Rate\tForward Rate"); for (int i = 0; i < nSamples; i++) { final double t = i / (nSamples - 1.0) * 12.0; //view the curve out to 12 years final double df = YIELD_CURVE.getDiscountFactor(t); final double r = YIELD_CURVE.getZeroRate(t); final double f = YIELD_CURVE.getForwardRate(t); System.out.println(t + "\t" + df + "\t" + r + "\t" + f); } } /** * Construct an object that represents a SNAC 5Y single-name CDS */ public void cdsConstructionDemo() { System.out.println("\nConstructionDemo"); //CDSAnalyticFactory defaults to SNAC setting (with recovery rate 40%). All of these can be change using 'with' methods. //In this case we change the recovery rate to 30% final double recoveryRate = 0.3; final CdsAnalyticFactory factory = new CdsAnalyticFactory().withRecoveryRate(recoveryRate); final Period cdsTerm = Period.ofYears(5); final CdsAnalytic cds = factory.makeImmCds(TRADE_DATE, cdsTerm); assertEquals(recoveryRate, 1 - cds.getLGD(), 1e-15); assertEquals(11, cds.getAccuredDays()); //can change the recovery rate without recomputing all the date logic final CdsAnalytic cds65 = cds.withRecoveryRate(0.65); assertEquals(0.65, 1 - cds65.getLGD(), 1e-15); assertEquals(11, cds65.getAccuredDays()); //the full API is consistent with the ISDA C code (and the xll plugin) //standard CDS settings final LocalDate stepinDate = TRADE_DATE.plusDays(1); // 1-Jul-2014 final LocalDate cashSettleDate = DEFAULT_CALENDAR.shift(TRADE_DATE, 3); // 3-Jul-2014 final LocalDate accStartDate = FOLLOWING.adjust(getPrevIMMDate(TRADE_DATE), DEFAULT_CALENDAR); // 20-Jun-2014 final LocalDate endDate = getNextIMMDate(TRADE_DATE).plus(cdsTerm); // 20-Sep-2019 final boolean payAccOnDefault = true; final Period paymentInterval = Period.ofMonths(3); final StubConvention stub = StubConvention.SHORT_INITIAL; //Irrelevant for SNAC where accrual start is previous IMM date final boolean protectionStart = true; final CdsAnalytic cds2 = new CdsAnalytic( TRADE_DATE, stepinDate, cashSettleDate, accStartDate, endDate, payAccOnDefault, paymentInterval, stub, protectionStart, recoveryRate, FOLLOWING, DEFAULT_CALENDAR, ACT360); assertTrue(cds.equals(cds2)); //Note: Can use exactly the same code to setup 'legacy' CDS, i.e. accStartDate T+1, or some bespoke CDS //Nowhere has the coupon appeared - the coupon is an input to the pricing, or in the case of par instruments, the par spread //is a calculation output } /** * Standard ISDA up-front model for pricing CDS. Conversion between Points-Up-Front (PUF) and Spread quotes */ public void upfrontModelDemo() { System.out.println("\nupfrontModelDemo"); final CdsAnalyticFactory factory = new CdsAnalyticFactory(); final Period cdsTerm = Period.ofYears(3); final CdsAnalytic cds = factory.makeImmCds(TRADE_DATE, cdsTerm); final double coupon = 100 * ONE_BP; final double lambda = 7e-3; //a hazard rate of 0.7% final IsdaCompliantCreditCurve constCreditCurve = new IsdaCompliantCreditCurve(1.0, lambda); final AnalyticCdsPricer pricer = new AnalyticCdsPricer(); final double puf = pricer.pv(cds, YIELD_CURVE, constCreditCurve, coupon); final double spread = pricer.parSpread(cds, YIELD_CURVE, constCreditCurve); //quoted spread or trade level //In this case the credit quality is good (hazard rate of 0.7%), so a coupon of 100bps is too high; the result //is a negative Points-Up-Front of -1.87%, and a corresponding quoted spread of 41.5bps System.out.format(Locale.ENGLISH, "PUF: %.4f%%, Quoted Spread: %.3fbps\n", puf * ONE_HUNDRED, spread * TEN_THOUSAND); //a CDS has a market quoted given as PUF and an equivalent quoted spread is required - this requires solving for the //hazard rate final IsdaCompliantCreditCurveBuilder creditCurveBuilder = new FastCreditCurveBuilder(); final IsdaCompliantCreditCurve fittedConstCreditCurve = creditCurveBuilder .calibrateCreditCurve(cds, coupon, YIELD_CURVE, puf); final double quotedSpread = pricer.parSpread(cds, YIELD_CURVE, fittedConstCreditCurve); assertEquals(spread, quotedSpread, 1e-15); //going the other way also involves solving for a constant hazard rate final IsdaCompliantCreditCurve fittedConstCreditCurve2 = creditCurveBuilder.calibrateCreditCurve(cds, spread, YIELD_CURVE); final double puf2 = pricer.pv(cds, YIELD_CURVE, fittedConstCreditCurve2, coupon); assertEquals(puf, puf2, 1e-15); //we have some tools to hide the calibration step final MarketQuoteConverter converter = new MarketQuoteConverter(); System.out.print("\n"); for (int i = 0; i < 21; i++) { final PointsUpFront pufQuote = new PointsUpFront(coupon, (-2.0 + 4.0 * i / 20.0) * ONE_PC); final CdsQuotedSpread spreadQuote = converter.convert(cds, pufQuote, YIELD_CURVE); System.out .format( Locale.ENGLISH, "PUF: %.4f%%," + "Quoted Spread: %.3fbps\n", pufQuote.getPointsUpFront() * ONE_HUNDRED, spreadQuote.getQuotedSpread() * TEN_THOUSAND); } } /** * We start with a quoted spread (or trade level) and compute the various cash amounts that are shown on BBG CDSW and/or * Markit calculator */ public void cashSettlementDemo() { System.out.println("\ncashSettlementDemo"); final CdsAnalyticFactory factory = new CdsAnalyticFactory(); final Period cdsTerm = Period.ofYears(5); final CdsAnalytic cds = factory.makeImmCds(TRADE_DATE, cdsTerm); final double notional = 1e7; //10MM final double coupon = 100 * ONE_BP; //accured for the buyer of protection is conventionally shown as negative amount (e.g. on BBG CDSW) //Here we show it as an absolute value which is consistent with is ISDA C code and the Markit calculator final double accruedAmount = notional * cds.getAccruedPremium(coupon); System.out.format(Locale.ENGLISH, "Accrued Amt: %.2f\n", accruedAmount); final double tradeLevel = 75 * ONE_BP; final MarketQuoteConverter converter = new MarketQuoteConverter(); final double puf = converter.quotedSpreadToPUF(cds, coupon, YIELD_CURVE, tradeLevel); final double principle = notional * puf; System.out.format(Locale.ENGLISH, "Principle: %.2f\n", principle); //The cash settlement is the amount actually paid (to enter the CDS contract) on the cash settlement date (3-Jul_2014) //here we subtract a positive accrued amount rather than add a negative accrued final double cashSettlement = principle - accruedAmount; System.out.format(Locale.ENGLISH, "cash Settlement: %.2f\n", cashSettlement); //The market value is the value of the CDS on the trade date final LocalDate cashSettleDate = DEFAULT_CALENDAR.shift(TRADE_DATE, 3); // 3-Jul-2014 final double df = YIELD_CURVE.getDiscountFactor(ACT365F.yearFraction(TRADE_DATE, cashSettleDate)); final double marketValue = df * cashSettlement; System.out.format(Locale.ENGLISH, "Market value: %.2f\n", marketValue); //can do all this explicitly with a credit curve and pricer final IsdaCompliantCreditCurveBuilder creditCurveBuilder = new FastCreditCurveBuilder(); final AnalyticCdsPricer pricer = new AnalyticCdsPricer(); final IsdaCompliantCreditCurve fittedConstCreditCurve = creditCurveBuilder.calibrateCreditCurve(cds, tradeLevel, YIELD_CURVE); final double principle2 = notional * pricer.pv(cds, YIELD_CURVE, fittedConstCreditCurve, coupon, CdsPriceType.CLEAN); final double cashSettlement2 = notional * pricer.pv(cds, YIELD_CURVE, fittedConstCreditCurve, coupon, CdsPriceType.DIRTY); final double marketValue2 = notional * pricer.pv(cds, YIELD_CURVE, fittedConstCreditCurve, coupon, CdsPriceType.DIRTY, 0.0); assertEquals(principle, principle2, 1e-15 * notional); assertEquals(cashSettlement, cashSettlement2, 1e-15 * notional); assertEquals(marketValue, marketValue2, 1e-15 * notional); //finally we can look at the individual legs //premium leg per unit of coupon (and unit notional) - often quoted per basis point of coupon //AKA RPV01 or duration (its value should be slightly less than the CDS time to maturity) final double annuity = pricer.annuity(cds, YIELD_CURVE, fittedConstCreditCurve); System.out.format(Locale.ENGLISH, "annuity: %.3f, time to maturity: %.3f\n", annuity, cds.getProtectionEnd()); final double protectionLeg = notional * pricer.protectionLeg(cds, YIELD_CURVE, fittedConstCreditCurve); System.out.format(Locale.ENGLISH, "Protection leg: %.2f\n", protectionLeg); //this can be a useful way of expressing the principle final double principle3 = notional * (tradeLevel - coupon) * annuity; assertEquals(principle, principle3, 1e-15 * notional); } /** * Build a full credit curve (i.e. time dependent hazard rate) from par quoted CDS */ public void creditCurveBuildDemo() { System.out.println("\ncreditCurveBuildDemo"); final Period[] tenors = new Period[] { Period.ofMonths(6), Period.ofYears(1), Period.ofYears(3), Period.ofYears(5), Period.ofYears(7), Period.ofYears(10)}; final double[] parSpreads = new double[] {0.008, 0.0088, 0.013, 0.017, 0.018, 0.019}; final CdsAnalyticFactory factory = new CdsAnalyticFactory(); //Default is 40% recovery //the calibration instruments have accrual starting on last IMM date rather than T+1 (we'll show an example of a 'legacy' CDS later) final CdsAnalytic[] calibrationCDS = factory.makeImmCds(TRADE_DATE, tenors); final IsdaCompliantCreditCurveBuilder creditCurveBuilder = new SuperFastCreditCurveBuilder(); //its faster than FastCreditCurveBuilder final IsdaCompliantCreditCurve creditCurve = creditCurveBuilder.calibrateCreditCurve(calibrationCDS, parSpreads, YIELD_CURVE); //check all the calibration instruments do indeed price back to zero (i.e. internally consistent) final int n = tenors.length; final AnalyticCdsPricer pricer = new AnalyticCdsPricer(); for (int i = 0; i < n; i++) { final double p = pricer.pv(calibrationCDS[i], YIELD_CURVE, creditCurve, parSpreads[i]); assertEquals(0, p, 1e-15); final double s = pricer.parSpread(calibrationCDS[i], YIELD_CURVE, creditCurve); assertEquals(parSpreads[i], s, 1e-15); } //price a 4Y CDS off the credit curve (assume RR is also 40%) final CdsAnalytic cds4Y = factory.makeImmCds(TRADE_DATE, Period.ofYears(4)); //A SNAC will have a coupon of 100bps final double coupon = 100 * ONE_BP; final double annuity = pricer.annuity(cds4Y, YIELD_CURVE, creditCurve); final double protLeg = pricer.protectionLeg(cds4Y, YIELD_CURVE, creditCurve); final double parSpread = protLeg / annuity; final double puf1 = protLeg - coupon * annuity; final double puf2 = (parSpread - coupon) * annuity; assertEquals(puf1, puf2, 1e-15); System.out.format(Locale.ENGLISH, "4Y par spread: %.3f, PUF: %.3f%%\n", parSpread * TEN_THOUSAND, puf1 * ONE_HUNDRED); //this should be cut and pasted into Excel to view the curve final int nSamples = 100; System.out.println("\nTime\tDiscount Factor\tZero Rate\tForward Rate"); for (int i = 0; i < nSamples; i++) { final double t = i / (nSamples - 1.0) * 12.0; //view the curve out to 12 years final double df = creditCurve.getSurvivalProbability(t); //can also use getDiscountFactor - it's the same thing final double r = creditCurve.getHazardRate(t); // ditto getZeroRate(t); final double f = creditCurve.getForwardRate(t); System.out.println(t + "\t" + df + "\t" + r + "\t" + f); } } /** * We can also build a full credit curve from PUF (the standard ISDA model can't do this) */ public void creditCurveBuildFromPUFDemo() { System.out.println("\ncreditCurveBuildFromPUFDemo"); final Period[] tenors = new Period[] { Period.ofMonths(6), Period.ofYears(1), Period.ofYears(3), Period.ofYears(5), Period.ofYears(7), Period.ofYears(10)}; final double[] puf = new double[] {-0.0, -0.003, -0.01, 0.01, 0.015, 0.019}; final int n = puf.length; final double[] coupons = new double[n]; Arrays.fill(coupons, 100 * ONE_BP); final CdsAnalyticFactory factory = new CdsAnalyticFactory(); //Default is 40% recovery final CdsAnalytic[] calibrationCDS = factory.makeImmCds(TRADE_DATE, tenors); final IsdaCompliantCreditCurveBuilder creditCurveBuilder = new SuperFastCreditCurveBuilder(); final IsdaCompliantCreditCurve creditCurve = creditCurveBuilder.calibrateCreditCurve(calibrationCDS, coupons, YIELD_CURVE, puf); //check all the calibration instruments do indeed price back to the market PUF (i.e. internally consistent) final AnalyticCdsPricer pricer = new AnalyticCdsPricer(); for (int i = 0; i < n; i++) { final double p = pricer.pv(calibrationCDS[i], YIELD_CURVE, creditCurve, coupons[i]); assertEquals(puf[i], p, 1e-14); } //this should be cut and pasted into Excel to view the curve final int nSamples = 100; System.out.println("\nTime\tDiscount Factor\tZero Rate\tForward Rate"); for (int i = 0; i < nSamples; i++) { final double t = i / (nSamples - 1.0) * 12.0; //view the curve out to 12 years final double df = creditCurve.getSurvivalProbability(t); //can also use getDiscountFactor - it's the same thing final double r = creditCurve.getHazardRate(t); // ditto getZeroRate(t); final double f = creditCurve.getForwardRate(t); System.out.println(t + "\t" + df + "\t" + r + "\t" + f); } } /** * Understanding the difference between par and quoted spreads. * Typically par spread and quoted spreads will differ by a few basis points */ public void parVsQuotedSpreadDemo() { System.out.println("\nparVsQuotedSpreadDemo"); //build a credit curve from par spreads final Period[] tenors = new Period[] {Period.ofMonths(6), Period.ofYears(1), Period.ofYears(3), Period.ofYears(5), Period.ofYears(7), Period.ofYears(10)}; final double[] parSpreads = new double[] {0.008, 0.0088, 0.013, 0.017, 0.018, 0.019}; final CdsAnalyticFactory factory = new CdsAnalyticFactory(); //Default is 40% recovery final CdsAnalytic[] calibrationCDS = factory.makeImmCds(TRADE_DATE, tenors); final IsdaCompliantCreditCurveBuilder creditCurveBuilder = new SuperFastCreditCurveBuilder(); //its faster than FastCreditCurveBuilder final IsdaCompliantCreditCurve creditCurve = creditCurveBuilder.calibrateCreditCurve(calibrationCDS, parSpreads, YIELD_CURVE); final double coupon = 100 * ONE_BP; //again use standard coupon of 100bps final int n = tenors.length; final double[] puf = new double[n]; // price standard CDS with a coupon of 100bps - these are different from the calibration instruments // which have coupons equal to their par spreads for (int i = 0; i < n; i++) { puf[i] = PRICER.pv(calibrationCDS[i], YIELD_CURVE, creditCurve, coupon); } final MarketQuoteConverter converter = new MarketQuoteConverter(); //this does an instrument-by-instrument conversion using separate constant hazard rates final double[] quotedSpreads = converter.pufToQuotedSpreads(calibrationCDS, coupon, YIELD_CURVE, puf); System.out.println("Par Spread\tPUF\tQuoted Spread"); for (int i = 0; i < n; i++) { System.out.format( Locale.ENGLISH, "%.0fbps\t%.3f%%\t%.3fbps\n", parSpreads[i] * TEN_THOUSAND, puf[i] * ONE_HUNDRED, quotedSpreads[i] * TEN_THOUSAND); } } /** * To build a credit curve from quoted spreads, one should first convert the quoted spread to PUF. Systems tend to not do * this, but just treat quoted spreads as par spreads. We can build curves from mixed quote types */ public void mixedQuoteCurveBuildDemo() { System.out.println("\nmixedQuoteCurveBuildDemo"); final Period[] tenors = new Period[] { Period.ofMonths(6), Period.ofYears(1), Period.ofYears(3), Period.ofYears(5), Period.ofYears(7), Period.ofYears(10)}; final int n = tenors.length; final double coupon = 100 * ONE_BP; final CdsQuoteConvention[] quotes = new CdsQuoteConvention[n]; quotes[0] = new CdsParSpread(80 * ONE_BP); quotes[1] = new PointsUpFront(coupon, -0.3 * ONE_PC); quotes[2] = new PointsUpFront(coupon, 0.5 * ONE_PC); quotes[3] = new CdsQuotedSpread(coupon, 130 * ONE_BP); quotes[4] = new CdsParSpread(150 * ONE_BP); quotes[5] = new CdsQuotedSpread(coupon, 225 * ONE_BP); final CdsAnalyticFactory factory = new CdsAnalyticFactory(); final CdsAnalytic[] calibrationCDS = factory.makeImmCds(TRADE_DATE, tenors); //make the par spread quotes T+1 accrual calibrationCDS[0] = factory.makeCds(TRADE_DATE, TRADE_DATE.plusDays(1), LocalDate.of(2015, 1, 20)); calibrationCDS[4] = factory.makeCds(TRADE_DATE, TRADE_DATE.plusDays(1), LocalDate.of(2021, 9, 20)); assertEquals(0, calibrationCDS[0].getAccuredDays()); //check no accrual //can have a recovery rate term structure for (int i = 0; i < n; i++) { final double rr = 0.7 - 0.5 * i / (n - 1.0); calibrationCDS[i] = calibrationCDS[i].withRecoveryRate(rr); } //get out a horrible looking curve (because I have just made up some data) final IsdaCompliantCreditCurve creditCurve = CREDIT_CURVE_BUILDER.calibrateCreditCurve(calibrationCDS, quotes, YIELD_CURVE); //this should be cut and pasted into Excel to view the curve final int nSamples = 100; System.out.println("\nTime\tDiscount Factor\tZero Rate\tForward Rate"); for (int i = 0; i < nSamples; i++) { final double t = i / (nSamples - 1.0) * 12.0; //view the curve out to 12 years final double df = creditCurve.getSurvivalProbability(t); //can also use getDiscountFactor - it's the same thing final double r = creditCurve.getHazardRate(t); // ditto getZeroRate(t); final double f = creditCurve.getForwardRate(t); System.out.println(t + "\t" + df + "\t" + r + "\t" + f); } } //********************************************************************************** // Greeks //********************************************************************************** /** * Credit spread sensitivity (CS01 or Credit DV01) */ public void cs01Demo() { System.out.println("\ncs01Demo"); final FiniteDifferenceSpreadSensitivityCalculator sensCal = new FiniteDifferenceSpreadSensitivityCalculator(); final AnalyticSpreadSensitivityCalculator anSensCal = new AnalyticSpreadSensitivityCalculator(); final CdsAnalyticFactory factory = new CdsAnalyticFactory().withRecoveryRate(0.25); final CdsAnalytic cds5Y = factory.makeImmCds(TRADE_DATE, Period.ofYears(5)); final double coupon = 500 * ONE_BP; final double tradeLevel = 367 * ONE_BP; final CdsQuoteConvention quotedSpread = new CdsQuotedSpread(coupon, tradeLevel); final double notional = 1e7; //10MM //CS01 is defined as a change in the principle for a one basis point increase in the quoted spread. //We can also calculate analytic sensitivity of the principle to the quoted spread, however it is the first //number (a forward finite difference) which is quoted on CDSW and Markit calculator final double cs01 = notional * ONE_BP * sensCal.parallelCS01(cds5Y, quotedSpread, YIELD_CURVE, ONE_BP); final double anCS01 = notional * ONE_BP * anSensCal.parallelCS01(cds5Y, quotedSpread, YIELD_CURVE); System.out.format(Locale.ENGLISH, "CS01: %.2f, analytic sense: %.2f \n", cs01, anCS01); //we can also compute a CS01 directly from PUF final MarketQuoteConverter converter = new MarketQuoteConverter(); final PointsUpFront puf = converter.convert(cds5Y, (CdsQuotedSpread) quotedSpread, YIELD_CURVE); System.out.format(Locale.ENGLISH, "PUF: %.2f%%\n", puf.getPointsUpFront() * ONE_HUNDRED); //this converted the PUF to a quoted spread behind the final double cs01FromPUF = notional * ONE_BP * sensCal.parallelCS01(cds5Y, puf, YIELD_CURVE, ONE_BP); assertEquals(cs01, cs01FromPUF, 1e-15 * notional); } }