/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.credit.isdastandardmodel.demo; import static com.opengamma.analytics.financial.credit.isdastandardmodel.IMMDateLogic.getNextIMMDate; import static com.opengamma.analytics.financial.credit.isdastandardmodel.IMMDateLogic.getPrevIMMDate; import static com.opengamma.financial.convention.businessday.BusinessDayDateUtils.addWorkDays; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import java.util.Arrays; import org.testng.annotations.Test; import org.threeten.bp.LocalDate; import org.threeten.bp.Period; import com.opengamma.analytics.financial.credit.isdastandardmodel.AnalyticCDSPricer; import com.opengamma.analytics.financial.credit.isdastandardmodel.AnalyticSpreadSensitivityCalculator; import com.opengamma.analytics.financial.credit.isdastandardmodel.CDSAnalytic; import com.opengamma.analytics.financial.credit.isdastandardmodel.CDSAnalyticFactory; import com.opengamma.analytics.financial.credit.isdastandardmodel.CDSQuoteConvention; import com.opengamma.analytics.financial.credit.isdastandardmodel.FastCreditCurveBuilder; import com.opengamma.analytics.financial.credit.isdastandardmodel.FiniteDifferenceSpreadSensitivityCalculator; import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDABaseTest; import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDACompliantCreditCurve; import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDACompliantCreditCurveBuilder; import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDACompliantYieldCurve; import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDACompliantYieldCurveBuild; import com.opengamma.analytics.financial.credit.isdastandardmodel.MarketQuoteConverter; import com.opengamma.analytics.financial.credit.isdastandardmodel.ParSpread; import com.opengamma.analytics.financial.credit.isdastandardmodel.PointsUpFront; import com.opengamma.analytics.financial.credit.isdastandardmodel.PriceType; import com.opengamma.analytics.financial.credit.isdastandardmodel.QuotedSpread; import com.opengamma.analytics.financial.credit.isdastandardmodel.StubType; import com.opengamma.analytics.financial.credit.isdastandardmodel.fastcalibration.SuperFastCreditCurveBuilder; import com.opengamma.analytics.financial.credit.options.YieldCurveProvider; import com.opengamma.financial.convention.daycount.DayCount; import com.opengamma.util.test.TestGroup; /** * 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(groups = TestGroup.INTEGRATION) 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 = addWorkDays(TRADE_DATE.minusDays(1), 3, DEFAULT_CALENDAR); //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.getDayCountFraction(TRADE_DATE, spotDate)); final int n = TEST_DATES.length; for (int i = 0; i < n; i++) { final double t1 = ACT365F.getDayCountFraction(TRADE_DATE, TEST_DATES[i]); final double t2 = ACT365F.getDayCountFraction(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 = addWorkDays(TRADE_DATE, 3, DEFAULT_CALENDAR); // 3-Jul-2014 final LocalDate accStartDate = FOLLOWING.adjustDate(DEFAULT_CALENDAR, getPrevIMMDate(TRADE_DATE)); // 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 StubType stub = StubType.FRONTSHORT; //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("PUF: %.4f%%, Quoted Spread: %.3fbps\n", puf * ONE_HUNDRED, spread * TEN_THOUSAND); //Above we knew the constant hazard rate, so could compute PUF and quoted spread directly from this. In practise //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 QuotedSpread spreadQuote = converter.convert(cds, pufQuote, YIELD_CURVE); System.out.format("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("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("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("cash Settlement: %.2f\n", cashSettlement); //The market value is the value of the CDS on the trade date final LocalDate cashSettleDate = addWorkDays(TRADE_DATE, 3, DEFAULT_CALENDAR); // 3-Jul-2014 final double df = YIELD_CURVE.getDiscountFactor(ACT365F.getDayCountFraction(TRADE_DATE, cashSettleDate)); final double marketValue = df * cashSettlement; System.out.format("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, PriceType.CLEAN); final double cashSettlement2 = notional * pricer.pv(cds, YIELD_CURVE, fittedConstCreditCurve, coupon, PriceType.DIRTY); final double marketValue2 = notional * pricer.pv(cds, YIELD_CURVE, fittedConstCreditCurve, coupon, PriceType.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("annuity: %.3f, time to maturity: %.3f\n", annuity, cds.getProtectionEnd()); final double protectionLeg = notional * pricer.protectionLeg(cds, YIELD_CURVE, fittedConstCreditCurve); System.out.format("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("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("%.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 ParSpread(80 * ONE_BP); quotes[1] = new PointsUpFront(coupon, -0.3 * ONE_PC); quotes[2] = new PointsUpFront(coupon, 0.5 * ONE_PC); quotes[3] = new QuotedSpread(coupon, 130 * ONE_BP); quotes[4] = new ParSpread(150 * ONE_BP); quotes[5] = new QuotedSpread(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 QuotedSpread(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("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, (QuotedSpread) quotedSpread, YIELD_CURVE); System.out.format("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); } }