/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.credit.isdastandardmodel;
import org.apache.commons.lang.NotImplementedException;
import com.opengamma.util.ArgumentChecker;
/**
*
*/
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(final 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(final CDSAnalytic cds, final CDSQuoteConvention quote, final ISDACompliantYieldCurveBuild yieldCurveBuilder, final double[] marketRates) {
ArgumentChecker.notNull(cds, "cds");
ArgumentChecker.notNull(quote, "quote");
ArgumentChecker.notNull(yieldCurveBuilder, "yieldCurveBuilder");
ArgumentChecker.notEmpty(marketRates, "marketRates");
final ISDACompliantYieldCurve ycUP = bumpYieldCurve(yieldCurveBuilder, marketRates, ONE_BPS);
final ISDACompliantYieldCurve yc = yieldCurveBuilder.build(marketRates);
if (quote instanceof QuotedSpread) {
final QuotedSpread qs = (QuotedSpread) quote;
final double puf = CONVERTER.quotedSpreadToPUF(cds, qs.getCoupon(), yc, qs.getQuotedSpread());
final double pufUp = CONVERTER.quotedSpreadToPUF(cds, qs.getCoupon(), ycUP, qs.getQuotedSpread());
return pufUp - puf;
} else if (quote instanceof PointsUpFront) {
final QuotedSpread qs = CONVERTER.convert(cds, (PointsUpFront) quote, yc);
final PointsUpFront pufUp = CONVERTER.convert(cds, qs, ycUP);
return pufUp.getPointsUpFront() - ((PointsUpFront) quote).getPointsUpFront();
} else if (quote instanceof ParSpread) {
throw new NotImplementedException("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(final CDSAnalytic cds, final CDSQuoteConvention quote, final ISDACompliantYieldCurve yieldCurve, final double[] marketRates) {
ArgumentChecker.notNull(cds, "cds");
ArgumentChecker.notNull(quote, "quote");
ArgumentChecker.notNull(yieldCurve, "yieldCurve");
ArgumentChecker.notEmpty(marketRates, "marketRates");
final ISDACompliantYieldCurve ycUP = bumpYieldCurve(yieldCurve, ONE_BPS);
if (quote instanceof QuotedSpread) {
final QuotedSpread qs = (QuotedSpread) quote;
final double puf = CONVERTER.quotedSpreadToPUF(cds, qs.getCoupon(), yieldCurve, qs.getQuotedSpread());
final double pufUp = CONVERTER.quotedSpreadToPUF(cds, qs.getCoupon(), ycUP, qs.getQuotedSpread());
return pufUp - puf;
} else if (quote instanceof PointsUpFront) {
final QuotedSpread qs = CONVERTER.convert(cds, (PointsUpFront) quote, yieldCurve);
final PointsUpFront pufUp = CONVERTER.convert(cds, qs, ycUP);
return pufUp.getPointsUpFront() - ((PointsUpFront) quote).getPointsUpFront();
} else if (quote instanceof ParSpread) {
throw new NotImplementedException("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(final CDSAnalytic cds, final double coupon, final ISDACompliantCreditCurve creditCurve, final ISDACompliantYieldCurveBuild yieldCurveBuilder, final double[] marketRates) {
ArgumentChecker.notNull(cds, "cds");
ArgumentChecker.notNull(creditCurve, "creditCurve");
ArgumentChecker.notNull(yieldCurveBuilder, "yieldCurveBuilder");
ArgumentChecker.notEmpty(marketRates, "marketRates");
final ISDACompliantYieldCurve ycUP = bumpYieldCurve(yieldCurveBuilder, marketRates, ONE_BPS);
final 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.
*
* 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(final CDSAnalytic cds, final double coupon, final ISDACompliantCreditCurve creditCurve, final ISDACompliantYieldCurve yieldCurve) {
ArgumentChecker.notNull(cds, "cds");
ArgumentChecker.notNull(creditCurve, "creditCurve");
ArgumentChecker.notNull(yieldCurve, "yieldCurve");
final 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(final CDSAnalytic cds, final double coupon, final ISDACompliantCreditCurve creditCurve, final ISDACompliantYieldCurveBuild yieldCurveBuilder, final double[] marketRates) {
ArgumentChecker.notNull(cds, "cds");
ArgumentChecker.notNull(creditCurve, "creditCurve");
ArgumentChecker.notNull(yieldCurveBuilder, "yieldCurveBuilder");
ArgumentChecker.notEmpty(marketRates, "marketRates");
final ISDACompliantYieldCurve baseYC = yieldCurveBuilder.build(marketRates);
final int n = marketRates.length;
final 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(final CDSAnalytic cds, final double coupon, final ISDACompliantCreditCurve creditCurve, final ISDACompliantYieldCurve yieldCurve) {
ArgumentChecker.notNull(cds, "cds");
ArgumentChecker.notNull(creditCurve, "creditCurve");
ArgumentChecker.notNull(yieldCurve, "yieldCurve");
final ISDACompliantYieldCurve baseYC = yieldCurve;
final int n = yieldCurve.getNumberOfKnots();
final 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(final CDSAnalytic cds, final ISDACompliantCreditCurve creditCurve, final double coupon, final ISDACompliantYieldCurve yc1, final ISDACompliantYieldCurve yc2) {
final double pv1 = _pricer.pv(cds, yc1, creditCurve, coupon, PriceType.DIRTY);
final double pv2 = _pricer.pv(cds, yc2, creditCurve, coupon, PriceType.DIRTY);
return pv1 - pv2;
}
private double[] priceDiff(final CDSAnalytic cds, final ISDACompliantCreditCurve creditCurve, final double coupon, final ISDACompliantYieldCurve[] bumpedYC, final ISDACompliantYieldCurve baseYC) {
final double basePV = _pricer.pv(cds, baseYC, creditCurve, coupon, PriceType.DIRTY);
final int n = bumpedYC.length;
final double[] res = new double[n];
for (int i = 0; i < n; i++) {
final double pv = _pricer.pv(cds, bumpedYC[i], creditCurve, coupon, PriceType.DIRTY);
res[i] = pv - basePV;
}
return res;
}
private ISDACompliantYieldCurve bumpYieldCurve(final ISDACompliantYieldCurveBuild builder, final double[] rates, final double bumpAmount) {
final int n = rates.length;
final 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(final ISDACompliantYieldCurve curve, final double bumpAmount) {
final int n = curve.getNumberOfKnots();
final double[] bumped = curve.getKnotZeroRates();
for (int i = 0; i < n; i++) {
bumped[i] += bumpAmount;
}
return curve.withRates(bumped);
}
private ISDACompliantYieldCurve bumpYieldCurve(final ISDACompliantYieldCurveBuild builder, final double[] rates, final double bumpAmount, final int index) {
final int n = rates.length;
final double[] bumped = new double[n];
System.arraycopy(rates, 0, bumped, 0, n);
bumped[index] += bumpAmount;
return builder.build(bumped);
}
private ISDACompliantYieldCurve bumpYieldCurve(final ISDACompliantYieldCurve curve, final double bumpAmount, final int index) {
return curve.withRate(curve.getZeroRateAtIndex(index) + bumpAmount, index);
}
// @SuppressWarnings("unused")
// private ISDACompliantYieldCurve bumpYieldCurve(final ISDACompliantYieldCurveBuild builder, final double[] rates, final double[] bumpAmounts) {
// final int n = bumpAmounts.length;
// ArgumentChecker.isTrue(n == rates.length, "rates length does not match bumpAmounts");
// final double[] bumped = new double[n];
// System.arraycopy(rates, 0, bumped, 0, n);
// for (int i = 0; i < n; i++) {
// bumped[i] += bumpAmounts[i];
// }
// return builder.build(bumped);
// }
}