/**
* 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;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.array.DoubleMatrix;
import com.opengamma.strata.math.impl.linearalgebra.LUDecompositionCommons;
import com.opengamma.strata.math.impl.linearalgebra.LUDecompositionResult;
import com.opengamma.strata.math.impl.matrix.MatrixAlgebra;
import com.opengamma.strata.math.impl.matrix.OGMatrixAlgebra;
/**
* Calculates the hedge ratio.
*/
public class HedgeRatioCalculator {
private static final MatrixAlgebra MA = new OGMatrixAlgebra();
private final AnalyticCdsPricer pricer;
private final IsdaCompliantCreditCurveBuilder builder;
/**
* Default constructor.
*/
public HedgeRatioCalculator() {
this.pricer = new AnalyticCdsPricer();
this.builder = new FastCreditCurveBuilder();
}
/**
* Constructor specifying formula used in pricer and credit curve builder.
*
* @param formula The formula
*/
public HedgeRatioCalculator(AccrualOnDefaultFormulae formula) {
ArgChecker.notNull(formula, "formula");
this.pricer = new AnalyticCdsPricer(formula);
this.builder = new FastCreditCurveBuilder(formula);
}
//-------------------------------------------------------------------------
/**
* The sensitivity of the PV of a CDS to the zero hazard rates at the knots of the credit curve.
*
* @param cds the CDS
* @param coupon the coupon
* @param creditCurve the credit Curve
* @param yieldCurve the yield curve
* @return vector of sensitivities
*/
public DoubleArray getCurveSensitivities(
CdsAnalytic cds,
double coupon,
IsdaCompliantCreditCurve creditCurve,
IsdaCompliantYieldCurve yieldCurve) {
// TODO this should be handled directly by the pricer
ArgChecker.notNull(cds, "cds");
ArgChecker.notNull(creditCurve, "creditCurve");
ArgChecker.notNull(yieldCurve, "yieldCurve");
return DoubleArray.of(
creditCurve.getNumberOfKnots(),
i -> pricer.pvCreditSensitivity(cds, yieldCurve, creditCurve, coupon, i));
}
/**
* The sensitivity of a set of CDSs to the zero hazard rates at the knots of the credit curve.
* The element (i,j) is the sensitivity of the PV of the jth CDS to the ith knot.
*
* @param cds the set of CDSs
* @param coupons the coupons of the CDSs
* @param creditCurve the credit Curve
* @param yieldCurve the yield curve
* @return matrix of sensitivities
*/
public DoubleMatrix getCurveSensitivities(
CdsAnalytic[] cds,
double[] coupons,
IsdaCompliantCreditCurve creditCurve,
IsdaCompliantYieldCurve yieldCurve) {
ArgChecker.noNulls(cds, "cds");
ArgChecker.notEmpty(coupons, "coupons");
ArgChecker.notNull(creditCurve, "creditCurve");
ArgChecker.notNull(yieldCurve, "yieldCurve");
int nCDS = cds.length;
ArgChecker.isTrue(nCDS == coupons.length, "number of coupons not equal number of CDS");
int nKnots = creditCurve.getNumberOfKnots();
double[][] sense = new double[nKnots][nCDS];
for (int i = 0; i < nCDS; i++) {
for (int j = 0; j < nKnots; j++) {
sense[j][i] = pricer.pvCreditSensitivity(cds[i], yieldCurve, creditCurve, coupons[i], j);
}
}
return DoubleMatrix.copyOf(sense);
}
//-------------------------------------------------------------------------
/**
* Hedge a CDS with other CDSs on the same underlying (single-name or index) at different maturities.
* <p>
* The hedge is such that the total portfolio (the CDS <b>minus</b> the hedging CDSs, with notionals of the
* CDS notional times the computed hedge ratios) is insensitive to infinitesimal changes to the credit curve.
* <p>
* Here the credit curve is built using the hedging CDSs as pillars.
*
* @param cds the CDS to be hedged
* @param coupon the coupon of the CDS to be hedged
* @param hedgeCdsArray the CDSs to hedge with - these are also used to build the credit curve
* @param hedgeCdsCoupons the coupons of the CDSs to hedge with/build credit curve
* @param hedgeCdsPuf the PUF of the CDSs to build credit curve
* @param yieldCurve the yield curve
* @return the hedge ratios,
* since we use a unit notional, the ratios should be multiplied by -notional to give the hedge notional amounts
*/
public DoubleArray getHedgeRatios(
CdsAnalytic cds,
double coupon,
CdsAnalytic[] hedgeCdsArray,
double[] hedgeCdsCoupons,
double[] hedgeCdsPuf,
IsdaCompliantYieldCurve yieldCurve) {
IsdaCompliantCreditCurve cc = builder.calibrateCreditCurve(hedgeCdsArray, hedgeCdsCoupons, yieldCurve, hedgeCdsPuf);
return getHedgeRatios(cds, coupon, hedgeCdsArray, hedgeCdsCoupons, cc, yieldCurve);
}
/**
* Hedge a CDS with other CDSs on the same underlying (single-name or index) at different maturities.
* <p>
* The hedge is such that the total portfolio (the CDS <b>minus</b> the hedging CDSs, with notionals of the
* CDS notional times the computed hedge ratios) is insensitive to infinitesimal changes to the credit curve.
* <p>
* If the number of hedge-CDSs equals the number of credit-curve knots, the system is square
* and is solved exactly (see below).<br>
* If the number of hedge-CDSs is less than the number of credit-curve knots, the system is
* solved in a least-square sense (i.e. is hedge is not exact).<br>
* If the number of hedge-CDSs is greater than the number of credit-curve knots, the system
* cannot be solved. <br>
* The system may not solve if the maturities if the hedging CDSs and very different from the
* knot times (i.e. the sensitivity matrix is singular).
*
* @param cds the CDS to be hedged
* @param coupon the coupon of the CDS to be hedged
* @param hedgeCDSs the CDSs to hedge with - these are also used to build the credit curve
* @param hedgeCDSCoupons the coupons of the CDSs to hedge with/build credit curve
* @param creditCurve The credit curve
* @param yieldCurve the yield curve
* @return the hedge ratios,
* since we use a unit notional, the ratios should be multiplied by -notional to give the hedge notional amounts
*/
public DoubleArray getHedgeRatios(
CdsAnalytic cds,
double coupon,
CdsAnalytic[] hedgeCDSs,
double[] hedgeCDSCoupons,
IsdaCompliantCreditCurve creditCurve,
IsdaCompliantYieldCurve yieldCurve) {
DoubleArray cdsSense = getCurveSensitivities(cds, coupon, creditCurve, yieldCurve);
DoubleMatrix hedgeSense = getCurveSensitivities(hedgeCDSs, hedgeCDSCoupons, creditCurve, yieldCurve);
return getHedgeRatios(cdsSense, hedgeSense);
}
/**
* Hedge a CDS with other CDSs on the same underlying (single-name or index) at different maturities.
* <p>
* The hedge is such that the total portfolio (the CDS <b>minus</b> the hedging CDSs, with notionals of the
* CDS notional times the computed hedge ratios) is insensitive to infinitesimal changes to the credit curve.
* <p>
* If the number of hedge-CDSs equals the number of credit-curve knots, the system is
* square and is solved exactly (see below).<br>
* If the number of hedge-CDSs is less than the number of credit-curve knots, the system is
* solved in a least-square sense (i.e. is hedge is not exact).<br>
* If the number of hedge-CDSs is greater than the number of credit-curve knots, the system
* cannot be solved. <br>
* The system may not solve if the maturities if the hedging CDSs and very different from the
* knot times (i.e. the sensitivity matrix is singular).
*
* @param cdsSensitivities the vector of sensitivities of the CDS to the zero hazard rates at the credit curve knots
* @param hedgeCDSSensitivities the matrix of sensitivities of the hedging-CDSs to the zero hazard rates
* at the credit curve knots. The (i,j) element is the sensitivity of the jth CDS to the ith knot.
* @return the hedge ratios,
* since we use a unit notional, the ratios should be multiplied by -notional to give the hedge notional amounts
*/
public DoubleArray getHedgeRatios(DoubleArray cdsSensitivities, DoubleMatrix hedgeCDSSensitivities) {
ArgChecker.notNull(hedgeCDSSensitivities, "hedgeCDSSensitivities");
int nRows = hedgeCDSSensitivities.rowCount();
int nCols = hedgeCDSSensitivities.columnCount();
ArgChecker.isTrue(nRows == cdsSensitivities.size(), "Number of matrix rows does not match vector length");
if (nCols == nRows) {
LUDecompositionCommons decomp = new LUDecompositionCommons();
LUDecompositionResult luRes = decomp.apply(hedgeCDSSensitivities);
return getHedgeRatios(cdsSensitivities, luRes);
} else {
if (nRows < nCols) {
//Under-specified. No unique solution exists. There are curve knots but hedging instruments
throw new IllegalArgumentException("Under-specified. No unique solution exists. There are " + nRows +
" curve knots but " + nCols + " hedging instruments.");
} else {
//over-specified. Solve in a least-square sense
DoubleMatrix senseT = MA.getTranspose(hedgeCDSSensitivities);
DoubleMatrix a = (DoubleMatrix) MA.multiply(senseT, hedgeCDSSensitivities);
DoubleArray b = (DoubleArray) MA.multiply(senseT, cdsSensitivities);
LUDecompositionCommons decomp = new LUDecompositionCommons();
LUDecompositionResult luRes = decomp.apply(a);
return getHedgeRatios(b, luRes);
}
}
}
public DoubleArray getHedgeRatios(DoubleArray cdsSensitivities, LUDecompositionResult luRes) {
ArgChecker.notNull(cdsSensitivities, "cdsSensitivities");
ArgChecker.notNull(luRes, " luRes");
DoubleArray w = luRes.solve(cdsSensitivities);
return w;
}
}