/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.impl.credit.isda.e2e;
import java.util.Arrays;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.pricer.impl.credit.isda.AnalyticCdsPricer;
import com.opengamma.strata.pricer.impl.credit.isda.CdsAnalytic;
import com.opengamma.strata.pricer.impl.credit.isda.CdsPriceType;
import com.opengamma.strata.pricer.impl.credit.isda.CreditCurveCalibrator;
import com.opengamma.strata.pricer.impl.credit.isda.InterestRateSensitivityCalculator;
import com.opengamma.strata.pricer.impl.credit.isda.IsdaCompliantCreditCurve;
import com.opengamma.strata.pricer.impl.credit.isda.IsdaCompliantYieldCurve;
/**
*
*/
public class CdsIndexCalculator {
// this code has been moved from src/main/java to src/test/java
private static final double ONE_BPS = 1e-4;
private final AnalyticCdsPricer _pricer;
public CdsIndexCalculator() {
_pricer = new AnalyticCdsPricer();
}
/**
* The Points-Up-Front (PUF) of an index. This is the (clean) price of a unit notional index.
* The actual clean price is this multiplied by the (current) index notional
* (i.e. the initial notional times the index factor).
*
* @param indexCDS analytic description of a CDS traded at a certain time
* @param indexCoupon The coupon of the index (as a fraction)
* @param yieldCurve The yield curve
* @param intrinsicData credit curves, weights and recover
* @return PUF of an index
*/
public double indexPUF(
CdsAnalytic indexCDS,
double indexCoupon,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.notNull(intrinsicData, "intrinsicData");
if (intrinsicData.getNumOfDefaults() == intrinsicData.getIndexSize()) {
throw new IllegalArgumentException("Index completely defaulted - not possible to rescale for PUF");
}
return indexPV(indexCDS, indexCoupon, yieldCurve, intrinsicData) / intrinsicData.getIndexFactor();
}
/**
* Intrinsic (normalised) price an index from the credit curves of the individual single names.
* To get the actual index value, this multiplied by the <b>initial</b> notional of the index.
*
* @param indexCDS analytic description of a CDS traded at a certain time
* @param indexCoupon The coupon of the index (as a fraction)
* @param yieldCurve The yield curve
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @return The index value for a unit notional.
*/
public double indexPV(
CdsAnalytic indexCDS,
double indexCoupon,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
double prot = indexProtLeg(indexCDS, yieldCurve, intrinsicData);
double annuity = indexAnnuity(indexCDS, yieldCurve, intrinsicData);
return prot - indexCoupon * annuity;
}
/**
* Intrinsic (normalised) price an index from the credit curves of the individual single names.
* To get the actual index value, this multiplied by the <b>initial</b> notional of the index.
*
* @param indexCDS analytic description of a CDS traded at a certain time
* @param indexCoupon The coupon of the index (as a fraction)
* @param yieldCurve The yield curve
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @param priceType Clean or dirty price
* @return The index value for a unit notional.
*/
public double indexPV(
CdsAnalytic indexCDS,
double indexCoupon,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData,
CdsPriceType priceType) {
double prot = indexProtLeg(indexCDS, yieldCurve, intrinsicData);
double annuity = indexAnnuity(indexCDS, yieldCurve, intrinsicData, priceType);
return prot - indexCoupon * annuity;
}
/**
* Intrinsic (normalised) price an index from the credit curves of the individual single names.
* To get the actual index value, this multiplied by the <b>initial</b> notional of the index.
*
* @param indexCDS analytic description of a CDS traded at a certain time
* @param indexCoupon The coupon of the index (as a fraction)
* @param yieldCurve The yield curve
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @param priceType Clean or dirty price
* @param valuationTime The leg value is calculated for today (t=0), then rolled
* forward (using the risk free yield curve) to the valuation time.
* This is because cash payments occur on the cash-settlement-date, which is usually
* three working days after the trade date (today)
* @return The index value for a unit notional.
*/
public double indexPV(
CdsAnalytic indexCDS,
double indexCoupon,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData,
CdsPriceType priceType,
double valuationTime) {
double prot = indexProtLeg(indexCDS, yieldCurve, intrinsicData, valuationTime);
double annuity = indexAnnuity(indexCDS, yieldCurve, intrinsicData, priceType, valuationTime);
return prot - indexCoupon * annuity;
}
/**
* The intrinsic index spread. this is defined as the ratio of the intrinsic protection leg to the intrinsic annuity.
*
* @see #averageSpread
* @param indexCDS analytic description of a CDS traded at a certain time
* @param yieldCurve The yield curve
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @return intrinsic index spread (as a fraction)
*/
public double intrinsicIndexSpread(
CdsAnalytic indexCDS,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.notNull(intrinsicData, "intrinsicData");
if (intrinsicData.getNumOfDefaults() == intrinsicData.getIndexSize()) {
throw new IllegalArgumentException("Every name in the index is defaulted - cannot calculate a spread");
}
double prot = indexProtLeg(indexCDS, yieldCurve, intrinsicData);
double annuity = indexAnnuity(indexCDS, yieldCurve, intrinsicData);
return prot / annuity;
}
/**
* The normalised intrinsic value of the protection leg of a CDS portfolio (index).
* The actual value of the leg is this multiplied by the <b>initial</b> notional of the index.
*
* @param indexCDS representation of the index cashflows (seen from today).
* @param yieldCurve The current yield curves
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @return The normalised intrinsic value of the protection leg.
*/
public double indexProtLeg(
CdsAnalytic indexCDS,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.notNull(indexCDS, "indexCDS");
return indexProtLeg(indexCDS, yieldCurve, intrinsicData, indexCDS.getCashSettleTime());
}
/**
* The normalised intrinsic value of the protection leg of a CDS portfolio (index).
* The actual value of the leg is this multiplied by the <b>initial</b> notional of the index.
*
* @param indexCDS representation of the index cashflows (seen from today).
* @param yieldCurve The current yield curves
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @param valuationTime Valuation time. The leg value is calculated for today (t=0),
* then rolled forward (using the risk free yield curve) to the valuation time.
* This is because cash payments occur on the cash-settlement-date, which is usually
* three working days after the trade date (today)
* @return The normalised intrinsic value of the protection leg.
*/
public double indexProtLeg(
CdsAnalytic indexCDS,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData,
double valuationTime) {
ArgChecker.notNull(indexCDS, "indexCDS");
ArgChecker.notNull(yieldCurve, "yieldCurve");
ArgChecker.notNull(intrinsicData, "intrinsicData");
CdsAnalytic cds = indexCDS.withRecoveryRate(0.0);
int n = intrinsicData.getIndexSize();
double protLeg = 0;
for (int i = 0; i < n; i++) {
if (!intrinsicData.isDefaulted(i)) {
protLeg += intrinsicData.getWeight(i) * intrinsicData.getLGD(i) *
_pricer.protectionLeg(cds, yieldCurve, intrinsicData.getCreditCurve(i), 0);
}
}
protLeg /= yieldCurve.getDiscountFactor(valuationTime);
return protLeg;
}
/**
* The intrinsic annuity of a CDS portfolio (index) for a unit (initial) notional.
* The value of the premium leg is this multiplied by the <b> initial</b> notional of the index
* and the index coupon (as a fraction).
*
* @param indexCDS representation of the index cashflows (seen from today).
* @param yieldCurve The current yield curves
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @param valuationTime Valuation time. The leg value is calculated for today (t=0), then rolled
* forward (using the risk free yield curve) to the valuation time.
* This is because cash payments occur on the cash-settlement-date, which is usually
* three working days after the trade date (today)
* @return The intrinsic annuity of a CDS portfolio (index)
*/
public double indexAnnuity(
CdsAnalytic indexCDS,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData,
double valuationTime) {
return indexAnnuity(indexCDS, yieldCurve, intrinsicData, CdsPriceType.CLEAN, valuationTime);
}
/**
* The intrinsic annuity of a CDS portfolio (index) for a unit (initial) notional.
* The value of the premium leg is this multiplied by the <b> initial</b> notional of the index
* and the index coupon (as a fraction).
*
* @param indexCDS representation of the index cashflows (seen from today).
* @param yieldCurve The current yield curves
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @return The normalised intrinsic annuity of a CDS portfolio (index)
*/
public double indexAnnuity(
CdsAnalytic indexCDS,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
return indexAnnuity(indexCDS, yieldCurve, intrinsicData, CdsPriceType.CLEAN, indexCDS.getCashSettleTime());
}
/**
* The intrinsic annuity of a CDS portfolio (index) for a unit (initial) notional.
* The value of the premium leg is this multiplied by the <b> initial</b> notional of the index
* and the index coupon (as a fraction).
*
* @param indexCDS representation of the index cashflows (seen from today).
* @param yieldCurve The current yield curves
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @param priceType Clean or dirty
* @return The normalised intrinsic annuity of a CDS portfolio (index)
*/
public double indexAnnuity(
CdsAnalytic indexCDS,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData,
CdsPriceType priceType) {
ArgChecker.notNull(indexCDS, "indexCDS");
return indexAnnuity(indexCDS, yieldCurve, intrinsicData, priceType, indexCDS.getCashSettleTime());
}
/**
* The intrinsic annuity of a CDS portfolio (index) for a unit (initial) notional.
* The value of the premium leg is this multiplied by the <b> initial</b> notional of the index
* and the index coupon (as a fraction).
*
* @param indexCDS representation of the index cashflows (seen from today).
* @param yieldCurve The current yield curves
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @param priceType Clean or dirty
* @param valuationTime Valuation time. The leg value is calculated for today (t=0),
* then rolled forward (using the risk free yield curve) to the valuation time.
* This is because cash payments occur on the cash-settlement-date, which is usually
* three working days after the trade date (today)
* @return The intrinsic annuity of a CDS portfolio (index)
*/
public double indexAnnuity(
CdsAnalytic indexCDS,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData,
CdsPriceType priceType,
double valuationTime) {
ArgChecker.notNull(indexCDS, "indexCDS");
ArgChecker.notNull(yieldCurve, "yieldCurve");
ArgChecker.notNull(intrinsicData, "intrinsicData");
int n = intrinsicData.getIndexSize();
double a = 0;
for (int i = 0; i < n; i++) {
if (!intrinsicData.isDefaulted(i)) {
a += intrinsicData.getWeight(i) * _pricer.annuity(indexCDS, yieldCurve, intrinsicData.getCreditCurve(i), priceType, 0);
}
}
a /= yieldCurve.getDiscountFactor(valuationTime);
return a;
}
/**
* The average spread of a CDS portfolio (index), defined as the weighted average of the
* (implied) par spreads of the constituent names.
*
* @see #intrinsicIndexSpread
* @param indexCDS representation of the index cashflows (seen from today).
* @param yieldCurve The current yield curves
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @return The average spread
*/
public double averageSpread(
CdsAnalytic indexCDS,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.notNull(indexCDS, "indexCDS");
ArgChecker.notNull(yieldCurve, "yieldCurve");
ArgChecker.notNull(intrinsicData, "intrinsicData");
if (intrinsicData.getNumOfDefaults() == intrinsicData.getIndexSize()) {
throw new IllegalArgumentException("Every name in the index is defaulted - cannot calculate a spread");
}
CdsAnalytic cds = indexCDS.withRecoveryRate(0.0);
int n = intrinsicData.getIndexSize();
double sum = 0;
for (int i = 0; i < n; i++) {
if (!intrinsicData.isDefaulted(i)) {
double protLeg = intrinsicData.getLGD(i) * _pricer.protectionLeg(cds, yieldCurve, intrinsicData.getCreditCurve(i));
double annuity = _pricer.annuity(cds, yieldCurve, intrinsicData.getCreditCurve(i));
double s = protLeg / annuity;
sum += intrinsicData.getWeight(i) * s;
}
}
sum /= intrinsicData.getIndexFactor();
return sum;
}
/**
* Imply a single (pseudo) credit curve for an index that will give the same index values
* at a set of terms (supplied via pillarCDS) as the intrinsic value.
*
* @param pillarCDS Point to build the curve
* @param indexCoupon The index coupon
* @param yieldCurve The current yield curves
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @return A (pseudo) credit curve for an index
*/
public IsdaCompliantCreditCurve impliedIndexCurve(
CdsAnalytic[] pillarCDS,
double indexCoupon,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.noNulls(pillarCDS, "pillarCDS");
ArgChecker.notNull(yieldCurve, "yieldCurve");
ArgChecker.notNull(intrinsicData, "intrinsicData");
if (intrinsicData.getNumOfDefaults() == intrinsicData.getIndexSize()) {
throw new IllegalArgumentException("Every name in the index is defaulted - cannot calculate implied index curve");
}
int n = pillarCDS.length;
double[] puf = new double[n];
double indexFactor = intrinsicData.getIndexFactor();
for (int i = 0; i < n; i++) {
// PUF are always given for full index
puf[i] = indexPV(pillarCDS[i], indexCoupon, yieldCurve, intrinsicData) / indexFactor;
}
CreditCurveCalibrator calibrator = new CreditCurveCalibrator(pillarCDS, yieldCurve);
double[] coupons = new double[n];
Arrays.fill(coupons, indexCoupon);
return calibrator.calibrate(coupons, puf);
}
//*******************************************************************************************************************
//* Forward values adjusted for defaults
//****************************************************************************************************************
/**
* For a future expiry date, the default adjusted forward index value is the expected (full)
* value of the index plus the cash settlement of any defaults before
* the expiry date, valued on the (forward) cash settlement date (usually 3 working days after
* the expiry date - i.e. the expiry settlement date).
*
* @param fwdStartingCDS A forward starting CDS to represent cash flows in the index.
* The stepin date should be one day after the expiry and the cashSettlement
* date (usually) 3 working days after expiry.
* @param timeToExpiry the time in years between the trade date and expiry.
* This should use the same DCC as the curves (ACT365F unless manually changed).
* @param yieldCurve The yield curve
* @param indexCoupon The coupon of the index
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* initially 100 entries, and the realised recovery rates are 0.2 and 0.35, the this value is (0.8 + 0.65)/100 )
* @return the default adjusted forward index value
*/
public double defaultAdjustedForwardIndexValue(
CdsAnalytic fwdStartingCDS,
double timeToExpiry,
IsdaCompliantYieldCurve yieldCurve,
double indexCoupon,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.notNull(fwdStartingCDS, "fwdCDS");
ArgChecker.isTrue(timeToExpiry >= 0.0, "timeToExpiry given as {}", timeToExpiry);
ArgChecker.isTrue(
fwdStartingCDS.getEffectiveProtectionStart() >= timeToExpiry,
"effective protection start of {} is less than time to expiry of {}. Must provide a forward starting CDS",
fwdStartingCDS.getEffectiveProtectionStart(), timeToExpiry);
//the expected value of the index (not including default settlement) at the expiry settlement date
double indexPV = indexPV(fwdStartingCDS, indexCoupon, yieldCurve, intrinsicData);
double d = expectedDefaultSettlementValue(timeToExpiry, intrinsicData);
return indexPV + d;
}
/**
* For a future expiry date, the default adjusted forward index value is the expected (full)
* value of the index plus the cash settlement of any defaults before
* the expiry date, valued on the (forward) cash settlement date (usually 3 working days after
* the expiry date - i.e. the expiry settlement date).
* This calculation assumes an homogeneous pool that can be described by a single index curve.
*
* @param fwdStartingCDS A forward starting CDS to represent cash flows in the index.
* The stepin date should be one day after the expiry and the cashSettlement
* date (usually) 3 working days after expiry. This must contain the index recovery rate.
* @param timeToExpiry the time in years between the trade date and expiry.
* This should use the same DCC as the curves (ACT365F unless manually changed).
* @param yieldCurve The yield curve
* @param indexCoupon The coupon of the index
* @param indexCurve Pseudo credit curve for the index.
* @return the default adjusted forward index value
*/
public double defaultAdjustedForwardIndexValue(
CdsAnalytic fwdStartingCDS,
double timeToExpiry,
IsdaCompliantYieldCurve yieldCurve,
double indexCoupon,
IsdaCompliantCreditCurve indexCurve) {
ArgChecker.notNull(fwdStartingCDS, "fwdCDS");
ArgChecker.isTrue(timeToExpiry >= 0.0, "timeToExpiry given as {}", timeToExpiry);
ArgChecker.isTrue(
fwdStartingCDS.getEffectiveProtectionStart() >= timeToExpiry,
"effective protection start of {} is less than time to expiry of {}. Must provide a forward starting CDS",
fwdStartingCDS.getEffectiveProtectionStart(), timeToExpiry);
double defSet = expectedDefaultSettlementValue(timeToExpiry, indexCurve, fwdStartingCDS.getLGD());
return defSet + _pricer.pv(fwdStartingCDS, yieldCurve, indexCurve, indexCoupon);
}
/**
* For a future expiry date, the default adjusted forward index value is the expected (full)
* value of the index plus the cash settlement of any defaults before
* the expiry date, valued on the (forward) cash settlement date (usually 3 working days after
* the expiry date - i.e. the expiry settlement date).
* This calculation assumes an homogeneous pool that can be described by a single index curve.
*
* @param fwdStartingCDS A forward starting CDS to represent cash flows in the index.
* The stepin date should be one day after the expiry and the cashSettlement
* date (usually) 3 working days after expiry. This must contain the index recovery rate.
* @param timeToExpiry the time in years between the trade date and expiry.
* This should use the same DCC as the curves (ACT365F unless manually changed).
* @param initialIndexSize The initial number of names in the index
* @param yieldCurve The yield curve
* @param indexCoupon The coupon of the index
* @param indexCurve Pseudo credit curve for the index.
* @param initialDefaultSettlement The (normalised) value of any defaults that have already
* occurred (e.g. if two defaults have occurred from an index with
* initially 100 entries, and the realised recovery rates are 0.2 and 0.35, the this value is (0.8 + 0.65)/100 )
* @param numDefaults The number of defaults that have already occurred
* @return the default adjusted forward index value
*/
public double defaultAdjustedForwardIndexValue(
CdsAnalytic fwdStartingCDS,
double timeToExpiry,
int initialIndexSize,
IsdaCompliantYieldCurve yieldCurve,
double indexCoupon,
IsdaCompliantCreditCurve indexCurve,
double initialDefaultSettlement,
int numDefaults) {
ArgChecker.notNull(fwdStartingCDS, "fwdCDS");
ArgChecker.isTrue(timeToExpiry >= 0.0, "timeToExpiry given as {}", timeToExpiry);
ArgChecker.isTrue(
fwdStartingCDS.getEffectiveProtectionStart() >= timeToExpiry,
"effective protection start of {} is less than time to expiry of {}. Must provide a forward starting CDS",
fwdStartingCDS.getEffectiveProtectionStart(), timeToExpiry);
double f = (initialIndexSize - numDefaults) / ((double) initialIndexSize);
double defSet = expectedDefaultSettlementValue(initialIndexSize, timeToExpiry, indexCurve, fwdStartingCDS.getLGD(),
initialDefaultSettlement, numDefaults);
return defSet + f * _pricer.pv(fwdStartingCDS, yieldCurve, indexCurve, indexCoupon);
}
/**
* The (default adjusted) intrinsic forward spread of an index.
* This is defined as the ratio of expected value of the protection leg and default settlement to
* the expected value of the annuity at expiry.
*
* @param fwdStartingCDS forward starting CDS to represent cash flows in the index.
* The stepin date should be one day after the expiry and the cashSettlement
* date (usually) 3 working days after expiry the time in years between the trade date and expiry.
* This should use the same DCC as the curves (ACT365F unless manually changed).
* @param timeToExpiry the time in years between the trade date and expiry.
* This should use the same DCC as the curves (ACT365F unless manually changed).
* @param yieldCurve The yield curve
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* initially 100 entries, and the realised recovery rates are 0.2 and 0.35, the this value is (0.8 + 0.65)/100 )
* @return The (default adjusted) forward spread (as a fraction)
*/
public double defaultAdjustedForwardSpread(
CdsAnalytic fwdStartingCDS,
double timeToExpiry,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.notNull(fwdStartingCDS, "fwdStartingCDS");
ArgChecker.isTrue(timeToExpiry >= 0.0, "timeToExpiry given as {}", timeToExpiry);
ArgChecker.isTrue(
fwdStartingCDS.getEffectiveProtectionStart() >= timeToExpiry,
"effective protection start of {} is less than time to expiry of {}. Must provide a forward starting CDS",
fwdStartingCDS.getEffectiveProtectionStart(), timeToExpiry);
// Note: these values are all calculated for payment on the (forward) cash settlement date
// there is no point discounting to today
double protLeg = indexProtLeg(fwdStartingCDS, yieldCurve, intrinsicData);
double defSettle = expectedDefaultSettlementValue(timeToExpiry, intrinsicData);
double ann = indexAnnuity(fwdStartingCDS, yieldCurve, intrinsicData);
return (protLeg + defSettle) / ann;
}
/**
* The (default adjusted) intrinsic forward spread of an index <b>when no defaults have yet occurred</b>.
* This is defined as the ratio of expected value of the
* protection leg and default settlement to the expected value of the annuity at expiry.
* This calculation assumes an homogeneous pool that can be described by a single index curve.
*
* @param fwdStartingCDS forward starting CDS to represent cash flows in the index.
* The stepin date should be one day after the expiry and the cashSettlement
* date (usually) 3 working days after expiry
* @param timeToExpiry the time in years between the trade date and expiry.
* This should use the same DCC as the curves (ACT365F unless manually changed).
* @param yieldCurve The yield curve
* @param indexCurve Pseudo credit curve for the index.
* @return The normalised expected default settlement value
*/
public double defaultAdjustedForwardSpread(
CdsAnalytic fwdStartingCDS,
double timeToExpiry,
IsdaCompliantYieldCurve yieldCurve,
IsdaCompliantCreditCurve indexCurve) {
ArgChecker.notNull(fwdStartingCDS, "fwdCDS");
ArgChecker.isTrue(timeToExpiry >= 0.0, "timeToExpiry given as {}", timeToExpiry);
ArgChecker.isTrue(
fwdStartingCDS.getEffectiveProtectionStart() >= timeToExpiry,
"effective protection start of {} is less than time to expiry of {}. Must provide a forward starting CDS",
fwdStartingCDS.getEffectiveProtectionStart(), timeToExpiry);
double defSettle = expectedDefaultSettlementValue(timeToExpiry, indexCurve, fwdStartingCDS.getLGD());
double protLeg = _pricer.protectionLeg(fwdStartingCDS, yieldCurve, indexCurve);
double ann = _pricer.annuity(fwdStartingCDS, yieldCurve, indexCurve);
return (protLeg + defSettle) / ann;
}
/**
* The (default adjusted) intrinsic forward spread of an index.
* This is defined as the ratio of expected value of the protection leg and default settlement to
* the expected value of the annuity at expiry. This calculation assumes an homogeneous pool that
* can be described by a single index curve.
*
* @param fwdStartingCDS forward starting CDS to represent cash flows in the index.
* The stepin date should be one day after the expiry and the cashSettlement
* date (usually) 3 working days after expiry
* @param timeToExpiry the time in years between the trade date and expiry.
* This should use the same DCC as the curves (ACT365F unless manually changed).
* @param initialIndexSize The initial number of names in the index
* @param yieldCurve The yield curve
* @param indexCurve Pseudo credit curve for the index.
* @param initialDefaultSettlement The (normalised) value of any defaults that have
* already occurred (e.g. if two defaults have occurred from an index with
* initially 100 entries, and the realised recovery rates are 0.2 and 0.35, the this value is (0.8 + 0.65)/100 )
* @param numDefaults The number of defaults that have already occurred
* @return The normalised expected default settlement value
*/
public double defaultAdjustedForwardSpread(
CdsAnalytic fwdStartingCDS,
double timeToExpiry,
int initialIndexSize,
IsdaCompliantYieldCurve yieldCurve,
IsdaCompliantCreditCurve indexCurve,
double initialDefaultSettlement,
int numDefaults) {
ArgChecker.notNull(fwdStartingCDS, "fwdCDS");
ArgChecker.isTrue(timeToExpiry >= 0.0, "timeToExpiry given as {}", timeToExpiry);
ArgChecker.isTrue(
fwdStartingCDS.getEffectiveProtectionStart() >= timeToExpiry,
"effective protection start of {} is less than time to expiry of {}. Must provide a forward starting CDS",
fwdStartingCDS.getEffectiveProtectionStart(), timeToExpiry);
double f = (initialIndexSize - numDefaults) / ((double) initialIndexSize);
double defSettle = expectedDefaultSettlementValue(initialIndexSize, timeToExpiry, indexCurve, fwdStartingCDS.getLGD(),
initialDefaultSettlement, numDefaults);
double protLeg = f * _pricer.protectionLeg(fwdStartingCDS, yieldCurve, indexCurve);
double ann = f * _pricer.annuity(fwdStartingCDS, yieldCurve, indexCurve);
return (protLeg + defSettle) / ann;
}
/**
* The normalised expected default settlement value paid on the exercise settlement date.
* The actual default settlement is this multiplied by the (initial) index notional.
*
* @param timeToExpiry Time to expiry
* @param intrinsicData credit curves, weights and recovery rates of the intrinsic names
* @return The normalised expected default settlement value
*/
public double expectedDefaultSettlementValue(
double timeToExpiry,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.notNull(intrinsicData, "intrinsicData");
int indexSize = intrinsicData.getIndexSize();
double d = 0.0; //computed the expected default settlement amount (paid on the expiry settlement date)
for (int i = 0; i < indexSize; i++) {
double qBar = intrinsicData.isDefaulted(i) ? 1.0 : 1.0 - intrinsicData.getCreditCurve(i).getSurvivalProbability(
timeToExpiry);
d += intrinsicData.getWeight(i) * intrinsicData.getLGD(i) * qBar;
}
return d;
}
/**
* The normalised expected default settlement value paid on the exercise settlement
* date <b>when no defaults have yet occurred</b>.
* The actual default settlement is this multiplied by the (initial)
* index notional. This calculation assumes an homogeneous pool that can be described by a single index curve.
*
* @param timeToExpiry Time to expiry
* @param indexCurve Pseudo credit curve for the index.
* @param lgd The index Loss Given Default (LGD)
* @return The normalised expected default settlement value
*/
public double expectedDefaultSettlementValue(
double timeToExpiry,
IsdaCompliantCreditCurve indexCurve,
double lgd) {
ArgChecker.notNull(indexCurve, "indexCurve");
ArgChecker.inRangeInclusive(lgd, 0, 1, "lgd");
double q = indexCurve.getSurvivalProbability(timeToExpiry);
double d = lgd * (1 - q);
return d;
}
/**
* The normalised expected default settlement value paid on the exercise settlement date.
* The actual default settlement is this multiplied by the (initial)
* index notional. This calculation assumes an homogeneous pool that can be described by a single index curve.
* @param initialIndexSize Initial index size
* @param timeToExpiry Time to expiry
* @param indexCurve Pseudo credit curve for the index.
* @param lgd The index Loss Given Default (LGD)
* @param initialDefaultSettlement The (normalised) value of any defaults that have already occurred
* (e.g. if two defaults have occurred from an index with
* initially 100 entries, and the realised recovery rates are 0.2 and 0.35, the this value is (0.8 + 0.65)/100 )
* @param numDefaults The number of defaults that have already occurred
* @return The normalised expected default settlement value
*/
public double expectedDefaultSettlementValue(
int initialIndexSize,
double timeToExpiry,
IsdaCompliantCreditCurve indexCurve,
double lgd,
double initialDefaultSettlement,
int numDefaults) {
ArgChecker.isTrue(initialIndexSize > 1, "initialIndexSize is {}", initialIndexSize);
ArgChecker.notNull(indexCurve, "indexCurve");
ArgChecker.isTrue(numDefaults >= 0, "negative numDefaults");
ArgChecker.isTrue(
numDefaults <= initialIndexSize, "More defaults ({}) than size of index ({})", numDefaults, initialIndexSize);
double defFrac = numDefaults / ((double) initialIndexSize);
// this upper range is if all current defaults have zero recovery
ArgChecker.inRangeInclusive(initialDefaultSettlement, 0, defFrac, "initialDefaultSettlement");
ArgChecker.inRangeInclusive(lgd, 0, 1, "lgd");
double q = indexCurve.getSurvivalProbability(timeToExpiry);
double d = (1 - defFrac) * lgd * (1 - q) + initialDefaultSettlement;
return d;
}
/**
* The change in the intrinsic value of a CDS index when the yield curve is bumped by 1bps.
* If the index is priced as a single name CDS, use {@link InterestRateSensitivityCalculator}.
*
* @param indexCDS The CDS index
* @param indexCoupon The index coupon
* @param yieldCurve The yield curve
* @param intrinsicData Credit curves, weights and recovery rates of the intrinsic names
* @return parallel IR01
*/
public double parallelIR01(
CdsAnalytic indexCDS,
double indexCoupon,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.notNull(indexCDS, "indexCDS");
ArgChecker.notNull(yieldCurve, "yieldCurve");
ArgChecker.notNull(intrinsicData, "intrinsicData");
double pv = indexPV(indexCDS, indexCoupon, yieldCurve, intrinsicData, CdsPriceType.DIRTY);
int nKnots = yieldCurve.getNumberOfKnots();
double[] rates = yieldCurve.getKnotZeroRates();
for (int i = 0; i < nKnots; ++i) {
rates[i] += ONE_BPS;
}
IsdaCompliantYieldCurve yieldCurveUp = yieldCurve.withRates(rates);
double pvUp = indexPV(indexCDS, indexCoupon, yieldCurveUp, intrinsicData, CdsPriceType.DIRTY);
return pvUp - pv;
}
/**
* The change in the intrinsic value of a CDS index when zero rate at node points of the yield curve is bumped by 1bps.
* If the index is priced as a single name CDS, use {@link InterestRateSensitivityCalculator}.
*
* @param indexCDS The CDS index
* @param indexCoupon The index coupon
* @param yieldCurve The yield curve
* @param intrinsicData Credit curves, weights and recovery rates of the intrinsic names
* @return bucketed IR01
*/
public double[] bucketedIR01(
CdsAnalytic indexCDS,
double indexCoupon,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.notNull(indexCDS, "indexCDS");
ArgChecker.notNull(yieldCurve, "yieldCurve");
ArgChecker.notNull(intrinsicData, "intrinsicData");
double basePV = indexPV(indexCDS, indexCoupon, yieldCurve, intrinsicData, CdsPriceType.DIRTY);
int n = yieldCurve.getNumberOfKnots();
double[] res = new double[n];
for (int i = 0; i < n; ++i) {
IsdaCompliantYieldCurve bumpedYieldCurve = yieldCurve.withRate(yieldCurve.getZeroRateAtIndex(i) + ONE_BPS, i);
double bumpedPV = indexPV(indexCDS, indexCoupon, bumpedYieldCurve, intrinsicData, CdsPriceType.DIRTY);
res[i] = bumpedPV - basePV;
}
return res;
}
/**
* Sensitivity of the intrinsic value of a CDS index to intrinsic CDS recovery rates.
*
* @param indexCDS The CDS index
* @param indexCoupon The index coupon
* @param yieldCurve The yield curve
* @param intrinsicData Credit curves, weights and recovery rates of the intrinsic names
* @return The sensitivity
*/
public double[] recovery01(
CdsAnalytic indexCDS,
double indexCoupon,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.notNull(indexCDS, "indexCDS");
ArgChecker.notNull(yieldCurve, "yieldCurve");
ArgChecker.notNull(intrinsicData, "intrinsicData");
CdsAnalytic zeroRR = indexCDS.withRecoveryRate(0.0);
int indexSize = intrinsicData.getIndexSize();
double[] res = new double[indexSize];
for (int i = 0; i < indexSize; ++i) {
if (intrinsicData.isDefaulted(i)) {
res[i] = 0.0;
} else {
res[i] = -_pricer.protectionLeg(zeroRR, yieldCurve, intrinsicData.getCreditCurve(i)) *
intrinsicData.getWeight(i);
}
}
return res;
}
/**
* Values on per-name default
* @param indexCDS The CDS index
* @param indexCoupon The index coupon
* @param yieldCurve The yield curve
* @param intrinsicData Credit curves, weights and recovery rates of the intrinsic names
* @return The jump to default
*/
public double[] jumpToDefault(
CdsAnalytic indexCDS,
double indexCoupon,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData) {
ArgChecker.notNull(indexCDS, "indexCDS");
ArgChecker.notNull(yieldCurve, "yieldCurve");
ArgChecker.notNull(intrinsicData, "intrinsicData");
int indexSize = intrinsicData.getIndexSize();
double[] res = new double[indexSize];
for (int i = 0; i < indexSize; ++i) {
if (intrinsicData.isDefaulted(i)) {
res[i] = 0.0;
} else {
res[i] = decomposedValueOnDefault(indexCDS, indexCoupon, yieldCurve, intrinsicData, i);
}
}
return res;
}
private double decomposedValueOnDefault(
CdsAnalytic indexCDS,
double indexCoupon,
IsdaCompliantYieldCurve yieldCurve,
IntrinsicIndexDataBundle intrinsicData,
int singleName) {
double weight = intrinsicData.getWeight(singleName);
double protection = intrinsicData.getLGD(singleName);
double singleNamePV = _pricer.pv(indexCDS, yieldCurve, intrinsicData.getCreditCurves()[singleName], indexCoupon);
return weight * (protection - singleNamePV);
}
}