/** * 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 com.opengamma.strata.collect.ArgChecker; /** * */ public class InterestRateSensitivityCalculator { private static final MarketQuoteConverter CONVERTER = new MarketQuoteConverter(); private static final double ONE_BPS = 1e-4; private final AnalyticCdsPricer _pricer; public InterestRateSensitivityCalculator() { _pricer = new AnalyticCdsPricer(); } public InterestRateSensitivityCalculator(AccrualOnDefaultFormulae formula) { _pricer = new AnalyticCdsPricer(formula); } /** * The IR01 (Interest-Rate 01) is by definition the change in the price of a CDS when the * market interest rates (these are money-market and swap rates) all increased by 1bps. * This assumes that the quoted (or flat) spread is invariant to a change in the yield curve. * * @param cds analytic description of a CDS traded at a certain time * @param quote This can be a QuotedSpread or PointsUpFront. For quoted spread this is taken as invariant; * for PUF this is converted to a quoted spread (which is then invariant). ParSpread is not supported * @param yieldCurveBuilder yield curve builder * @param marketRates the money-market and swap rates (in the correct order for the yield curve * builder, i.e. in ascending order of maturity) * @return the parallel IR01 */ public double parallelIR01( CdsAnalytic cds, CdsQuoteConvention quote, IsdaCompliantYieldCurveBuild yieldCurveBuilder, double[] marketRates) { ArgChecker.notNull(cds, "cds"); ArgChecker.notNull(quote, "quote"); ArgChecker.notNull(yieldCurveBuilder, "yieldCurveBuilder"); ArgChecker.notEmpty(marketRates, "marketRates"); IsdaCompliantYieldCurve ycUP = bumpYieldCurve(yieldCurveBuilder, marketRates, ONE_BPS); IsdaCompliantYieldCurve yc = yieldCurveBuilder.build(marketRates); if (quote instanceof CdsQuotedSpread) { CdsQuotedSpread qs = (CdsQuotedSpread) quote; double puf = CONVERTER.quotedSpreadToPUF(cds, qs.getCoupon(), yc, qs.getQuotedSpread()); double pufUp = CONVERTER.quotedSpreadToPUF(cds, qs.getCoupon(), ycUP, qs.getQuotedSpread()); return pufUp - puf; } else if (quote instanceof PointsUpFront) { CdsQuotedSpread qs = CONVERTER.convert(cds, (PointsUpFront) quote, yc); PointsUpFront pufUp = CONVERTER.convert(cds, qs, ycUP); return pufUp.getPointsUpFront() - ((PointsUpFront) quote).getPointsUpFront(); } else if (quote instanceof CdsParSpread) { throw new UnsupportedOperationException( "This type of claculation don't make sense for par spreads. Use a fixed credit curve method."); } else { throw new IllegalArgumentException("Unknown quote type: " + quote.getClass()); } } /** * The IR01 (Interest-Rate 01) is by definition the change in the price of a CDS when the * market interest rates (these are money-market and swap rates) all increased by 1bps. * This assumes that the quoted (or flat) spread is invariant to a change in the yield curve. * In addition the bumps are applied directly to the yield curve and NOT the instruments. * * @param cds analytic description of a CDS traded at a certain time * @param quote This can be a QuotedSpread or PointsUpFront. For quoted spread this is taken as invariant; * for PUF this is converted to a quoted spread (which is then invariant). ParSpread is not supported * @param yieldCurve yield curve * @param marketRates the money-market and swap rates (in the correct order for the yield curve * builder, i.e. in ascending order of maturity) * @return the parallel IR01 */ public double parallelIR01( CdsAnalytic cds, CdsQuoteConvention quote, IsdaCompliantYieldCurve yieldCurve, double[] marketRates) { ArgChecker.notNull(cds, "cds"); ArgChecker.notNull(quote, "quote"); ArgChecker.notNull(yieldCurve, "yieldCurve"); ArgChecker.notEmpty(marketRates, "marketRates"); IsdaCompliantYieldCurve ycUP = bumpYieldCurve(yieldCurve, ONE_BPS); if (quote instanceof CdsQuotedSpread) { CdsQuotedSpread qs = (CdsQuotedSpread) quote; double puf = CONVERTER.quotedSpreadToPUF(cds, qs.getCoupon(), yieldCurve, qs.getQuotedSpread()); double pufUp = CONVERTER.quotedSpreadToPUF(cds, qs.getCoupon(), ycUP, qs.getQuotedSpread()); return pufUp - puf; } else if (quote instanceof PointsUpFront) { CdsQuotedSpread qs = CONVERTER.convert(cds, (PointsUpFront) quote, yieldCurve); PointsUpFront pufUp = CONVERTER.convert(cds, qs, ycUP); return pufUp.getPointsUpFront() - ((PointsUpFront) quote).getPointsUpFront(); } else if (quote instanceof CdsParSpread) { throw new UnsupportedOperationException( "This type of claculation don't make sense for par spreads. Use a fixed credit curve method."); } else { throw new IllegalArgumentException("Unknown quote type: " + quote.getClass()); } } /** * The IR01 (Interest-Rate 01) is by definition the change in the price of a CDS when the * market interest rates (these are money-market and swap rates) all increased by 1bps. * This assumes that the credit curve is invariant. * * @param cds analytic description of a CDS traded at a certain time * @param coupon The cds's coupon (as a fraction) * @param creditCurve the credit (or survival) curve * @param yieldCurveBuilder yield curve builder * @param marketRates the money-market and swap rates (in the correct order for the yield * curve builder, i.e. in ascending order of maturity) * @return the parallel IR01 */ public double parallelIR01( CdsAnalytic cds, double coupon, IsdaCompliantCreditCurve creditCurve, IsdaCompliantYieldCurveBuild yieldCurveBuilder, double[] marketRates) { ArgChecker.notNull(cds, "cds"); ArgChecker.notNull(creditCurve, "creditCurve"); ArgChecker.notNull(yieldCurveBuilder, "yieldCurveBuilder"); ArgChecker.notEmpty(marketRates, "marketRates"); IsdaCompliantYieldCurve ycUP = bumpYieldCurve(yieldCurveBuilder, marketRates, ONE_BPS); IsdaCompliantYieldCurve yc = yieldCurveBuilder.build(marketRates); return priceDiff(cds, creditCurve, coupon, ycUP, yc); } /** * The IR01 (Interest-Rate 01) is by definition the change in the price of a CDS when the * yield curve is bumped by 1bps. * <p> * Note, this bumps the yield curve not the underlying instrument market data. * See methods which take a {@code ISDACompliantYieldCurveBuild} * for bumping of the underlying instruments. * * @param cds analytic description of a CDS traded at a certain time * @param coupon The cds's coupon (as a fraction) * @param creditCurve the credit (or survival) curve * @param yieldCurve yield curve * @return the parallel IR01 */ public double parallelIR01( CdsAnalytic cds, double coupon, IsdaCompliantCreditCurve creditCurve, IsdaCompliantYieldCurve yieldCurve) { ArgChecker.notNull(cds, "cds"); ArgChecker.notNull(creditCurve, "creditCurve"); ArgChecker.notNull(yieldCurve, "yieldCurve"); IsdaCompliantYieldCurve ycUP = bumpYieldCurve(yieldCurve, ONE_BPS); return priceDiff(cds, creditCurve, coupon, ycUP, yieldCurve); } /** * The bucketed IR01 (Interest-Rate 01) is by definition the vector of changes in the * price of a CDS when the market interest rates * (these are money-market and swap rates) increased by 1bps in turn. * * @param cds analytic description of a CDS traded at a certain time * @param coupon The cds's coupon (as a fraction) * @param creditCurve the credit (or survival) curve * @param yieldCurveBuilder yield curve builder * @param marketRates the money-market and swap rates (in the correct order for the yield * curve builder, i.e. in ascending order of maturity) * @return the bucketed IR01 */ public double[] bucketedIR01( CdsAnalytic cds, double coupon, IsdaCompliantCreditCurve creditCurve, IsdaCompliantYieldCurveBuild yieldCurveBuilder, double[] marketRates) { ArgChecker.notNull(cds, "cds"); ArgChecker.notNull(creditCurve, "creditCurve"); ArgChecker.notNull(yieldCurveBuilder, "yieldCurveBuilder"); ArgChecker.notEmpty(marketRates, "marketRates"); IsdaCompliantYieldCurve baseYC = yieldCurveBuilder.build(marketRates); int n = marketRates.length; IsdaCompliantYieldCurve[] bumpedYC = new IsdaCompliantYieldCurve[n]; for (int i = 0; i < n; i++) { bumpedYC[i] = bumpYieldCurve(yieldCurveBuilder, marketRates, ONE_BPS, i); } return priceDiff(cds, creditCurve, coupon, bumpedYC, baseYC); } /** * The bucketed IR01 (Interest-Rate 01) is by definition the vector of changes in the * price of a CDS when the points on the yield curve are bumped. * * Note, this bumps the yield curve not the underlying instrument market data. * See methods which take a {@code ISDACompliantYieldCurveBuild} * for bumping of the underlying instruments. * * @param cds analytic description of a CDS traded at a certain time * @param coupon The cds's coupon (as a fraction) * @param creditCurve the credit (or survival) curve * @param yieldCurve yield curve * @return the bucketed IR01 */ public double[] bucketedIR01( CdsAnalytic cds, double coupon, IsdaCompliantCreditCurve creditCurve, IsdaCompliantYieldCurve yieldCurve) { ArgChecker.notNull(cds, "cds"); ArgChecker.notNull(creditCurve, "creditCurve"); ArgChecker.notNull(yieldCurve, "yieldCurve"); IsdaCompliantYieldCurve baseYC = yieldCurve; int n = yieldCurve.getNumberOfKnots(); IsdaCompliantYieldCurve[] bumpedYC = new IsdaCompliantYieldCurve[n]; for (int i = 0; i < n; i++) { bumpedYC[i] = bumpYieldCurve(yieldCurve, ONE_BPS, i); } return priceDiff(cds, creditCurve, coupon, bumpedYC, baseYC); } private double priceDiff( CdsAnalytic cds, IsdaCompliantCreditCurve creditCurve, double coupon, IsdaCompliantYieldCurve yc1, IsdaCompliantYieldCurve yc2) { double pv1 = _pricer.pv(cds, yc1, creditCurve, coupon, CdsPriceType.DIRTY); double pv2 = _pricer.pv(cds, yc2, creditCurve, coupon, CdsPriceType.DIRTY); return pv1 - pv2; } private double[] priceDiff( CdsAnalytic cds, IsdaCompliantCreditCurve creditCurve, double coupon, IsdaCompliantYieldCurve[] bumpedYC, IsdaCompliantYieldCurve baseYC) { double basePV = _pricer.pv(cds, baseYC, creditCurve, coupon, CdsPriceType.DIRTY); int n = bumpedYC.length; double[] res = new double[n]; for (int i = 0; i < n; i++) { double pv = _pricer.pv(cds, bumpedYC[i], creditCurve, coupon, CdsPriceType.DIRTY); res[i] = pv - basePV; } return res; } private IsdaCompliantYieldCurve bumpYieldCurve(IsdaCompliantYieldCurveBuild builder, double[] rates, double bumpAmount) { int n = rates.length; double[] bumped = new double[n]; System.arraycopy(rates, 0, bumped, 0, n); for (int i = 0; i < n; i++) { bumped[i] += bumpAmount; } return builder.build(bumped); } private IsdaCompliantYieldCurve bumpYieldCurve(IsdaCompliantYieldCurve curve, double bumpAmount) { int n = curve.getNumberOfKnots(); double[] bumped = curve.getKnotZeroRates(); for (int i = 0; i < n; i++) { bumped[i] += bumpAmount; } return curve.withRates(bumped); } private IsdaCompliantYieldCurve bumpYieldCurve( IsdaCompliantYieldCurveBuild builder, double[] rates, double bumpAmount, int index) { int n = rates.length; double[] bumped = new double[n]; System.arraycopy(rates, 0, bumped, 0, n); bumped[index] += bumpAmount; return builder.build(bumped); } private IsdaCompliantYieldCurve bumpYieldCurve(IsdaCompliantYieldCurve curve, double bumpAmount, int index) { return curve.withRate(curve.getZeroRateAtIndex(index) + bumpAmount, index); } }