/** * 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 java.util.Arrays; import com.opengamma.util.ArgumentChecker; /** * Standard CDS have a standard premium (100 or 500bps in North America) and are quoted as either as Points Up-Front (PUF) or quoted * spread - these method allow conversion via a single constant hazard rate. <p> * A credit curve can be built that is consistent with market quotes; this can then be used to imply equivalent par spreads, and vice-versa. */ public class MarketQuoteConverter { private final ISDACompliantCreditCurveBuilder _builder; private final AnalyticCDSPricer _pricer; public MarketQuoteConverter() { _builder = new FastCreditCurveBuilder(); _pricer = new AnalyticCDSPricer(); } public MarketQuoteConverter(final AccrualOnDefaultFormulae formula) { _builder = new FastCreditCurveBuilder(formula); _pricer = new AnalyticCDSPricer(formula); } //************************************************************************************************************** // Various ways of quoting the price for a given credit curve //************************************************************************************************************** /** * The clean price as a fraction of notional (it is often expressed as a percentage of notional) * @param fractionalPUF The points up-front (as a fraction) * @return The clean price (as a fraction) */ public double cleanPrice(final double fractionalPUF) { return 1 - fractionalPUF; } /** * The clean price as a fraction of notional (it is often expressed as a percentage of notional) - this requires that a * credit curve is bootstrapped first * @param cds The CDS to be traded * @param yieldCurve the yield/discount curve * @param creditCurve the credit/hazard curve * @param coupon The fractional quoted spread (coupon) of the CDS * @return the clean price (as a fraction) */ public double cleanPrice(final CDSAnalytic cds, final ISDACompliantYieldCurve yieldCurve, final ISDACompliantCreditCurve creditCurve, final double coupon) { final double puf = pointsUpFront(cds, coupon, yieldCurve, creditCurve); return 1 - puf; } /** * The principal - this is the clean present value * @param notional The notional of the trade * @param cds The CDS to be traded * @param yieldCurve the yield/discount curve * @param creditCurve the credit/hazard curve * @param coupon The fractional quoted spread (coupon) of the CDS * @return The principle */ public double principal(final double notional, final CDSAnalytic cds, final ISDACompliantYieldCurve yieldCurve, final ISDACompliantCreditCurve creditCurve, final double coupon) { return notional * _pricer.pv(cds, yieldCurve, creditCurve, coupon, PriceType.CLEAN); } /** * Get the points up-front - this requires that a credit curve is bootstrapped first * @param cds The CDS to be traded * @param premium The standard premium of the CDS <b>expressed as a fraction</b> * @param yieldCurve the yield/discount curve * @param creditCurve the credit/hazard curve * @return points up-front - these are usually quoted as a percentage of the notional - here we return a fraction of notional, * so 0.01 is 1(%) points up-front */ public double pointsUpFront(final CDSAnalytic cds, final double premium, final ISDACompliantYieldCurve yieldCurve, final ISDACompliantCreditCurve creditCurve) { return _pricer.pv(cds, yieldCurve, creditCurve, premium, PriceType.CLEAN); } /** * Get the points up-front for a collection of CDSs - this requires that a credit curve is bootstrapped first. This will * give a slightly different answer to using a single (flat) credit curve for each CDS (the latter is the market standard) * @param cds collection of CDSs * @param premium The single common premium of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve the yield/discount curve * @param creditCurve the credit/hazard curve * @return points up-front (as fractions) */ public double[] pointsUpFront(final CDSAnalytic[] cds, final double premium, final ISDACompliantYieldCurve yieldCurve, final ISDACompliantCreditCurve creditCurve) { ArgumentChecker.noNulls(cds, "cds"); final int n = cds.length; final double[] res = new double[n]; for (int i = 0; i < n; i++) { res[i] = pointsUpFront(cds[i], premium, yieldCurve, creditCurve); } return res; } /** * Get the points up-front for a collection of CDSs - this requires that a credit curve is bootstrapped first. This will * give a slightly different answer to using a single (flat) credit curve for each CDS (the latter is the market standard) * @param cds collection of CDSs * @param premiums The premiums of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve the yield/discount curve * @param creditCurve the credit/hazard curve * @return points-upfront (as fractions) */ public double[] pointsUpFront(final CDSAnalytic[] cds, final double[] premiums, final ISDACompliantYieldCurve yieldCurve, final ISDACompliantCreditCurve creditCurve) { ArgumentChecker.noNulls(cds, "cds"); ArgumentChecker.notEmpty(premiums, "premiums"); final int n = cds.length; final double[] res = new double[n]; for (int i = 0; i < n; i++) { res[i] = pointsUpFront(cds[i], premiums[i], yieldCurve, creditCurve); } return res; } /** * The par spreads for a collection of CDSs where a single, non-flat, credit/hazard curve is known. * @param cds collection of CDSs * @param yieldCurve the yield/discount curve * @param creditCurve the credit/hazard curve * @return par spreads */ public double[] parSpreads(final CDSAnalytic[] cds, final ISDACompliantYieldCurve yieldCurve, final ISDACompliantCreditCurve creditCurve) { ArgumentChecker.noNulls(cds, "cds"); ArgumentChecker.notNull(yieldCurve, "yieldCurve"); ArgumentChecker.notNull(creditCurve, "creditCurve"); final int n = cds.length; final double[] res = new double[n]; for (int i = 0; i < n; i++) { res[i] = _pricer.parSpread(cds[i], yieldCurve, creditCurve); } return res; } //************************************************************************************************************** // Single Converters. These convert between a single quote as PUF or quoted spread by building a // constant hazard rate curve. //************************************************************************************************************** /** * Convert from a CDS quoted spread to points up-front (PUF). * @param cds The CDS to be traded * @param qSpread The quoted spread * @param yieldCurve the yield/discount curve * @return the PUF */ public PointsUpFront convert(final CDSAnalytic cds, final QuotedSpread qSpread, final ISDACompliantYieldCurve yieldCurve) { ArgumentChecker.notNull(qSpread, "qSpread"); final double puf = quotedSpreadToPUF(cds, qSpread.getCoupon(), yieldCurve, qSpread.getQuotedSpread()); return new PointsUpFront(qSpread.getCoupon(), puf); } /** * Convert from a CDS quoted spread to points up-front (PUF). <b>Note:</b> Quoted spread is not the same as par spread * (although they are numerically similar) - it is simply an alternative quoting convention from PUF where the CDS is priced * off a flat credit/hazard curve. * @param cds The CDS to be traded * @param premium The standard premium of the CDS <b>expressed as a fraction</b> * @param yieldCurve the yield/discount curve * @param quotedSpread The quoted spread (<b>as a fraction</b>). * @return points up-front - these are usually quoted as a percentage of the notional - here we return a fraction of notional, * so 0.01 is 1(%) points up-front */ public double quotedSpreadToPUF(final CDSAnalytic cds, final double premium, final ISDACompliantYieldCurve yieldCurve, final double quotedSpread) { ArgumentChecker.notNull(cds, "cds"); ArgumentChecker.notNull(yieldCurve, "yieldCurve"); final ISDACompliantCreditCurve creditCurve = _builder.calibrateCreditCurve(cds, quotedSpread, yieldCurve); return pointsUpFront(cds, premium, yieldCurve, creditCurve); } /** * Convert from a CDS points up-front (PUF) to a quoted spread. * @param cds The CDS to be traded * @param puf point-up-front (this contains the premium) * @param yieldCurve the yield/discount curve * @return the par spread */ public QuotedSpread convert(final CDSAnalytic cds, final PointsUpFront puf, final ISDACompliantYieldCurve yieldCurve) { ArgumentChecker.notNull(puf, "puf"); final double qs = pufToQuotedSpread(cds, puf.getCoupon(), yieldCurve, puf.getPointsUpFront()); return new QuotedSpread(puf.getCoupon(), qs); } /** * Convert from a CDS quote as points up-front (PUF) and a standard premium, to a <i>quoted</i> spread. * This is simply an alternative quoting convention from PUF where the CDS is priced off a flat credit/hazard curve. * @param cds The CDS to be traded * @param premium The standard premium of the CDS <b>expressed as a fraction</b> * @param yieldCurve the yield/discount curve * @param pointsUpfront points up-front * @return the par spread <b>expressed as a fraction</b> */ public double pufToQuotedSpread(final CDSAnalytic cds, final double premium, final ISDACompliantYieldCurve yieldCurve, final double pointsUpfront) { ArgumentChecker.notNull(cds, "cds"); ArgumentChecker.notNull(yieldCurve, "yieldCurve"); final ISDACompliantCreditCurve creditCurve = _builder.calibrateCreditCurve(cds, premium, yieldCurve, pointsUpfront); return _pricer.parSpread(cds, yieldCurve, creditCurve); } //************************************************************************************************************** // multiple Converters. These convert between N quotes as PUF or quoted spread by building N independent // constant hazard rate curves. //************************************************************************************************************** public PointsUpFront[] convert(final CDSAnalytic[] cds, final QuotedSpread[] qSpreads, final ISDACompliantYieldCurve yieldCurve) { ArgumentChecker.notNull(cds, "cds"); ArgumentChecker.notNull(qSpreads, "qSpreads"); final int n = cds.length; ArgumentChecker.isTrue(n == qSpreads.length, "numbe of CDSs does not match qSpreads"); final PointsUpFront[] res = new PointsUpFront[n]; for (int i = 0; i < n; i++) { res[i] = convert(cds[i], qSpreads[i], yieldCurve); } return res; } /** * Convert from a set of CDSs quoted spreads to points up-front (PUF). <b>Note:</b> Quoted spread is not the same as par spread * (although they are numerically similar) - it is simply an alternative quoting convention from PUF where each CDS is priced * off a <b>separate</b> flat credit/hazard curve - i.e. the CDSs are completely decoupled from each other. * @param cds collection of CDSs * @param premium The single common premium of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve yieldCurve the yield/discount curve * @param quotedSpreads The quoted spreads (<b>as a fractions</b>). * @return points up-front (expressed as fractions) */ public double[] quotedSpreadsToPUF(final CDSAnalytic[] cds, final double premium, final ISDACompliantYieldCurve yieldCurve, final double[] quotedSpreads) { ArgumentChecker.notNull(cds, "cds"); ArgumentChecker.notEmpty(quotedSpreads, "parSpreads"); final int n = cds.length; ArgumentChecker.isTrue(n == quotedSpreads.length, "parSpreads wrong length"); final double[] res = new double[n]; for (int i = 0; i < n; i++) { res[i] = quotedSpreadToPUF(cds[i], premium, yieldCurve, quotedSpreads[i]); } return res; } /** * Convert from a set of CDSs quoted spreads to points up-front (PUF). <b>Note:</b> Quoted spread is not the same as par spread * (although they are numerically similar) - it is simply an alternative quoting convention from PUF where each CDS is priced * off a <b>separate</b> flat credit/hazard curve - i.e. the CDSs are completely decoupled from each other. * @param cds collection of CDSs * @param premiums The premiums of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve yieldCurve the yield/discount curve * @param quotedSpreads The quoted spreads (<b>as a fractions</b>). * @return points up-front (expressed as fractions) */ public double[] quotedSpreadsToPUF(final CDSAnalytic[] cds, final double[] premiums, final ISDACompliantYieldCurve yieldCurve, final double[] quotedSpreads) { ArgumentChecker.notNull(cds, "cds"); ArgumentChecker.notEmpty(premiums, "premiums"); ArgumentChecker.notEmpty(quotedSpreads, "parSpreads"); final int n = cds.length; ArgumentChecker.isTrue(n == premiums.length, "premiums wrong length"); ArgumentChecker.isTrue(n == quotedSpreads.length, "parSpreads wrong length"); final double[] res = new double[n]; for (int i = 0; i < n; i++) { res[i] = quotedSpreadToPUF(cds[i], premiums[i], yieldCurve, quotedSpreads[i]); } return res; } public QuotedSpread[] convert(final CDSAnalytic[] cds, final PointsUpFront[] puf, final ISDACompliantYieldCurve yieldCurve) { ArgumentChecker.notNull(cds, "cds"); ArgumentChecker.notNull(puf, "puf"); final int n = cds.length; ArgumentChecker.isTrue(n == puf.length, "numbe of CDSs does not match puf"); final QuotedSpread[] res = new QuotedSpread[n]; for (int i = 0; i < n; i++) { res[i] = convert(cds[i], puf[i], yieldCurve); } return res; } /** * Get the equivalent <i>quoted</i> spreads for a collection of CDSs. This is simply a quoting convention -each CDS is priced * off a <b>separate</b> flat credit/hazard curve - i.e. the CDSs are completely decoupled from each other. * @param cds collection of CDSs * @param premium The single common premium of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve the yield/discount curve * @param pointsUpfront The points up-front (expressed as fractions) * @see MarketQuoteConverter#pufToQuotedSpread * @return collection of CDSs */ public double[] pufToQuotedSpreads(final CDSAnalytic[] cds, final double premium, final ISDACompliantYieldCurve yieldCurve, final double[] pointsUpfront) { ArgumentChecker.noNulls(cds, "cds"); ArgumentChecker.notNull(yieldCurve, "yieldCurve"); ArgumentChecker.notEmpty(pointsUpfront, "pointsUpfront"); final int n = cds.length; final double[] res = new double[n]; for (int i = 0; i < n; i++) { res[i] = pufToQuotedSpread(cds[i], premium, yieldCurve, pointsUpfront[i]); } return res; } /** * Get the equivalent <i>quoted</i> spreads for a collection of CDSs. This is simply a quoting convention -each CDS is priced * off a <b>separate</b> flat credit/hazard curve - i.e. the CDSs are completely decoupled from each other. * @param cds collection of CDSs * @param premiums The premiums of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve the yield/discount curve * @param pointsUpfront The points up-front (expressed as fractions) * @see MarketQuoteConverter#pufToQuotedSpread * @return equivalent par spreads */ public double[] pufToQuotedSpreads(final CDSAnalytic[] cds, final double[] premiums, final ISDACompliantYieldCurve yieldCurve, final double[] pointsUpfront) { ArgumentChecker.noNulls(cds, "cds"); ArgumentChecker.notNull(yieldCurve, "yieldCurve"); ArgumentChecker.notEmpty(premiums, "premiums"); ArgumentChecker.notEmpty(pointsUpfront, "pointsUpfront"); final int n = cds.length; final double[] res = new double[n]; for (int i = 0; i < n; i++) { res[i] = pufToQuotedSpread(cds[i], premiums[i], yieldCurve, pointsUpfront[i]); } return res; } //************************************************************************************************************** // Full Curve Converters. These convert between par spread quotes and PUF by constructing a single // credit curve consistent with the quotes. //************************************************************************************************************** /** * Convert from a set of CDSs quoted as a par spreads (the old way of quoting) to points up-front (PUF). * Each CDS is priced off a <b>single non-flat</b> credit/hazard curve. <br> * If the CDS are quoted as <b>quoted</b> spreads one must use quotedSpreadsToPUF instead * {@link #pointsUpFront} * @param cds collection of CDSs * @param premium The single common premium of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve yieldCurve the yield/discount curve * @param parSpreads The par-spreads (<b>as a fractions</b>). * @return points up-front (expressed as fractions) */ public double[] parSpreadsToPUF(final CDSAnalytic[] cds, final double premium, final ISDACompliantYieldCurve yieldCurve, final double[] parSpreads) { final ISDACompliantCreditCurve creditCurve = _builder.calibrateCreditCurve(cds, parSpreads, yieldCurve); return pointsUpFront(cds, premium, yieldCurve, creditCurve); } /** * Convert from a set of CDSs quoted as a par spreads (the old way of quoting) to points up-front (PUF). * Each CDS is priced off a <b>single non-flat</b> credit/hazard curve. <br> * If the CDS are quoted as <b>quoted</b> spreads one must use quotedSpreadsToPUF instead * @param cds collection of CDSs * @param premiums The premiums of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve yieldCurve the yield/discount curve * @param parSpreads The par-spreads (<b>as a fractions</b>). * @see MarketQuoteConverter#pointsUpFront * @return points up-front (expressed as fractions) */ public double[] parSpreadsToPUF(final CDSAnalytic[] cds, final double[] premiums, final ISDACompliantYieldCurve yieldCurve, final double[] parSpreads) { final ISDACompliantCreditCurve creditCurve = _builder.calibrateCreditCurve(cds, parSpreads, yieldCurve); return pointsUpFront(cds, premiums, yieldCurve, creditCurve); } /** * The equivalent par spreads for a collection of CDSs where a single, non-flat, credit/hazard curve is bootstrapped to * reprice all the given CDSs. * @param cds collection of CDSs * @param premium The single common premium of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve the yield/discount curve * @param pointsUpfront The points up-front (expressed as fractions) * @see MarketQuoteConverter#pufToParSpreads * @return equivalent par spreads */ public double[] pufToParSpreads(final CDSAnalytic[] cds, final double premium, final ISDACompliantYieldCurve yieldCurve, final double[] pointsUpfront) { ArgumentChecker.noNulls(cds, "cds"); final int n = cds.length; final double[] premiums = new double[n]; Arrays.fill(premiums, premium); return pufToParSpreads(cds, premiums, yieldCurve, pointsUpfront); } /** * The equivalent par spreads for a collection of CDSs where a single, non-flat, credit/hazard curve is bootstrapped to * reprice all the given CDSs. * @param cds collection of CDSs * @param premiums The premiums of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve the yield/discount curve * @param pointsUpfront The points up-front (expressed as fractions) * @see MarketQuoteConverter#parSpreads * @return equivalent par spreads */ public double[] pufToParSpreads(final CDSAnalytic[] cds, final double[] premiums, final ISDACompliantYieldCurve yieldCurve, final double[] pointsUpfront) { final ISDACompliantCreditCurve creditCurve = _builder.calibrateCreditCurve(cds, premiums, yieldCurve, pointsUpfront); return parSpreads(cds, yieldCurve, creditCurve); } /** * Convert from par spreads to quoted spreads * @param cds collection of CDSs * @param premium The single common premium of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve the yield/discount curve * @param parSpreads par spreads * @return quoted spreads */ public double[] parSpreadsToQuotedSpreads(final CDSAnalytic[] cds, final double premium, final ISDACompliantYieldCurve yieldCurve, final double[] parSpreads) { final double[] puf = parSpreadsToPUF(cds, premium, yieldCurve, parSpreads); return pufToQuotedSpreads(cds, premium, yieldCurve, puf); } /** * Convert from par spreads to quoted spreads * @param cds collection of CDSs * @param premiums The premiums of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve the yield/discount curve * @param parSpreads par spreads * @return quoted spreads */ public double[] parSpreadsToQuotedSpreads(final CDSAnalytic[] cds, final double[] premiums, final ISDACompliantYieldCurve yieldCurve, final double[] parSpreads) { final double[] puf = parSpreadsToPUF(cds, premiums, yieldCurve, parSpreads); return pufToQuotedSpreads(cds, premiums, yieldCurve, puf); } /** * Convert from quoted spreads to par spreads * @param cds collection of CDSs * @param premium The single common premium of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve the yield/discount curve * @param quotedSpreads The quoted spreads * @return par spreads */ public double[] quotedSpreadToParSpreads(final CDSAnalytic[] cds, final double premium, final ISDACompliantYieldCurve yieldCurve, final double[] quotedSpreads) { final double[] puf = quotedSpreadsToPUF(cds, premium, yieldCurve, quotedSpreads); return pufToParSpreads(cds, premium, yieldCurve, puf); } /** * Convert from quoted spreads to par spreads * @param cds collection of CDSs * @param premiums The premiums of the CDSs expressed as fractions (these are usually 0.01 or 0.05) * @param yieldCurve the yield/discount curve * @param quotedSpreads The quoted spreads * @return par spreads */ public double[] quotedSpreadToParSpreads(final CDSAnalytic[] cds, final double[] premiums, final ISDACompliantYieldCurve yieldCurve, final double[] quotedSpreads) { final double[] puf = quotedSpreadsToPUF(cds, premiums, yieldCurve, quotedSpreads); return pufToParSpreads(cds, premiums, yieldCurve, puf); } }