/**
* 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 static com.opengamma.strata.pricer.impl.credit.isda.YieldCurveProvider.ISDA_USD_20140213;
import static com.opengamma.strata.pricer.impl.credit.isda.e2e.CdsIndexProvider.CDX_NA_HY_20140213_PRICES;
import static com.opengamma.strata.pricer.impl.credit.isda.e2e.CdsIndexProvider.CDX_NA_HY_20140213_RECOVERY_RATES;
import static com.opengamma.strata.pricer.impl.credit.isda.e2e.CdsIndexProvider.CDX_NA_HY_21_COUPON;
import static com.opengamma.strata.pricer.impl.credit.isda.e2e.CdsIndexProvider.CDX_NA_HY_21_RECOVERY_RATE;
import static com.opengamma.strata.pricer.impl.credit.isda.e2e.CdsIndexProvider.INDEX_TENORS;
import static com.opengamma.strata.pricer.impl.credit.isda.e2e.CdsIndexProvider.getCDX_NA_HY_20140213_CreditCurves;
import static org.testng.AssertJUnit.assertEquals;
import java.time.LocalDate;
import java.time.Period;
import java.util.Arrays;
import java.util.BitSet;
import org.testng.annotations.Test;
import com.opengamma.strata.basics.date.DayCounts;
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.CdsAnalyticFactory;
import com.opengamma.strata.pricer.impl.credit.isda.CdsPriceType;
import com.opengamma.strata.pricer.impl.credit.isda.IsdaBaseTest;
import com.opengamma.strata.pricer.impl.credit.isda.IsdaCompliantCreditCurve;
import com.opengamma.strata.pricer.impl.credit.isda.IsdaCompliantYieldCurve;
import com.opengamma.strata.pricer.impl.credit.isda.PointsUpFront;
/**
*
*/
@Test
public class CdsIndexCalculatorTest extends IsdaBaseTest {
private static final LocalDate TRADE_DATE = LocalDate.of(2014, 2, 13);
private static final Period[] INDEX_PILLARS = INDEX_TENORS;
private static final double INDEX_RR = CDX_NA_HY_21_RECOVERY_RATE;
private static final IsdaCompliantCreditCurve[] CREDIT_CURVES = getCDX_NA_HY_20140213_CreditCurves();
private static final double[] RECOVERY_RATES = CDX_NA_HY_20140213_RECOVERY_RATES;
private static final IntrinsicIndexDataBundle INTRINSIC_DATA = new IntrinsicIndexDataBundle(CREDIT_CURVES, RECOVERY_RATES);
private static IsdaCompliantYieldCurve YIELD_CURVE = ISDA_USD_20140213;
private static CdsIndexCalculator INDEX_CAL = new CdsIndexCalculator();
private static CdsAnalyticFactory FACTORY = new CdsAnalyticFactory(INDEX_RR);
private static final LocalDate EXPIRY = LocalDate.of(2014, 3, 19);
private static final LocalDate MATURITY = LocalDate.of(2018, 12, 20);
private static final PortfolioSwapAdjustment PSA = new PortfolioSwapAdjustment();
private static final PointsUpFront[] PILLAR_PUF;
private static double[] PRICES = CDX_NA_HY_20140213_PRICES;
private static final double INDEX_COUPON = CDX_NA_HY_21_COUPON;
private static final CdsAnalytic FWD_START_CDX = FACTORY.makeForwardStartingCds(TRADE_DATE, EXPIRY, MATURITY);
private static final CdsAnalytic[] PILLAR_CDX = FACTORY.makeCdx(TRADE_DATE, INDEX_TENORS);
static {
final int n = PRICES.length;
PILLAR_PUF = new PointsUpFront[n];
for (int i = 0; i < n; i++) {
PILLAR_PUF[i] = new PointsUpFront(INDEX_COUPON, 1 - PRICES[i]);
}
}
/**
* Test IR01, JTD, recovery01
*/
@Test
public void sensitivitiesTest() {
AnalyticCdsPricer pricer = new AnalyticCdsPricer();
double tol = 1.0e-12;
CdsAnalytic cdx = FACTORY.makeCdx(TRADE_DATE, Period.ofYears(5));
double indexCoupon = 300 * ONE_BP;
int[] defaultedNames = new int[] {2, 15, 37, 51 };
IntrinsicIndexDataBundle intrinsicDataWithDefaulted = INTRINSIC_DATA.withDefault(defaultedNames);
double pv = INDEX_CAL.indexPV(cdx, indexCoupon, YIELD_CURVE, intrinsicDataWithDefaulted);
double parallelIR01 = INDEX_CAL.parallelIR01(cdx, indexCoupon, YIELD_CURVE, intrinsicDataWithDefaulted);
double[] bucketedIR01 = INDEX_CAL.bucketedIR01(cdx, indexCoupon, YIELD_CURVE, intrinsicDataWithDefaulted);
double[] jumpToDefault = INDEX_CAL.jumpToDefault(cdx, indexCoupon, YIELD_CURVE, intrinsicDataWithDefaulted);
double[] recovery01 = INDEX_CAL.recovery01(cdx, indexCoupon, YIELD_CURVE, intrinsicDataWithDefaulted);
double[] rates = YIELD_CURVE.getKnotZeroRates();
int nRates = rates.length;
double[] bumpedRatesParallel = new double[nRates];
double[][] bumpedRatesBucket = new double[nRates][nRates];
for (int i = 0; i < nRates; ++i) {
bumpedRatesBucket[i] = Arrays.copyOf(rates, nRates);
bumpedRatesBucket[i][i] += ONE_BP;
bumpedRatesParallel[i] = rates[i] + ONE_BP;
}
IsdaCompliantYieldCurve ycParallelBump = YIELD_CURVE.withRates(bumpedRatesParallel);
double pvParallelBump = INDEX_CAL.indexPV(cdx, indexCoupon, ycParallelBump, intrinsicDataWithDefaulted);
assertEquals(pvParallelBump - pv, parallelIR01, tol);
for (int i = 0; i < nRates; ++i) {
IsdaCompliantYieldCurve ycBump = YIELD_CURVE.withRates(bumpedRatesBucket[i]);
double pvBump = INDEX_CAL.indexPV(cdx, indexCoupon, ycBump, intrinsicDataWithDefaulted);
assertEquals(pvBump - pv, bucketedIR01[i], tol);
}
int size = intrinsicDataWithDefaulted.getIndexSize();
for (int i = 0; i < size; ++i) {
double ref;
if (intrinsicDataWithDefaulted.isDefaulted(i)) {
ref = 0.0;
} else {
double weight = intrinsicDataWithDefaulted.getWeight(i);
ref = intrinsicDataWithDefaulted.getLGD(i) * weight -
pricer.pv(cdx, YIELD_CURVE, intrinsicDataWithDefaulted.getCreditCurves()[i], indexCoupon) * weight;
}
assertEquals(ref, jumpToDefault[i], tol);
}
double[] zeroRecoveryRates = new double[size];
Arrays.fill(zeroRecoveryRates, 0.0);
double[] weights = new double[size];
double sum = 0.0;
for (int i = 0; i < size; ++i) {
weights[i] = intrinsicDataWithDefaulted.getWeight(i);
sum += recovery01[i];
}
IntrinsicIndexDataBundle bundleZeroRecoveryRates = new IntrinsicIndexDataBundle(
intrinsicDataWithDefaulted.getCreditCurves(),
zeroRecoveryRates, weights).withDefault(defaultedNames);
double ref = -INDEX_CAL.indexProtLeg(cdx, YIELD_CURVE, bundleZeroRecoveryRates);
assertEquals(sum, ref, Math.abs(ref) * tol);
}
/**
*
*/
@Test
public void pvAndPufTest() {
final double tol = 1.0e-12;
final CdsAnalytic cdx = FACTORY.makeCdx(TRADE_DATE, Period.ofYears(3).plusMonths(5));
final double indexCoupon = 300 * 1.0e-4;
final CdsAnalytic[] pillarCdx = FACTORY.makeCdx(TRADE_DATE, INDEX_PILLARS);
/*
* No defaulted names
*/
final double res = INDEX_CAL.indexPUF(cdx, indexCoupon, YIELD_CURVE, INTRINSIC_DATA);
final double resDirty = INDEX_CAL.indexPV(cdx, indexCoupon, YIELD_CURVE, INTRINSIC_DATA, CdsPriceType.DIRTY);
final double sp = INDEX_CAL.intrinsicIndexSpread(cdx, YIELD_CURVE, INTRINSIC_DATA);
final double resSpread = INDEX_CAL.indexPUF(cdx, sp, YIELD_CURVE, INTRINSIC_DATA);
assertEquals(0.0, resSpread, tol);
final double resAvgSpread = INDEX_CAL.averageSpread(cdx, YIELD_CURVE, INTRINSIC_DATA);
final double resAnnuity = INDEX_CAL.indexAnnuity(cdx, YIELD_CURVE, INTRINSIC_DATA, cdx.getCashSettleTime() + 0.01);
final int indexSize = CREDIT_CURVES.length;
double ref = 0.0;
double refDirty = 0.0;
double refAvgSpread = 0.0;
double refAnnuity = 0.0;
final AnalyticCdsPricer cdsPricer = new AnalyticCdsPricer();
for (int i = 0; i < indexSize; ++i) {
final CdsAnalytic cds = cdx.withRecoveryRate(1.0 - INTRINSIC_DATA.getLGD(i));
ref += INTRINSIC_DATA.getWeight(i) * cdsPricer.pv(cds, YIELD_CURVE, CREDIT_CURVES[i], indexCoupon);
refDirty += INTRINSIC_DATA.getWeight(i) * cdsPricer.pv(cds, YIELD_CURVE, CREDIT_CURVES[i], indexCoupon, CdsPriceType.DIRTY);
refAvgSpread += INTRINSIC_DATA.getWeight(i) * cdsPricer.parSpread(cds, YIELD_CURVE, CREDIT_CURVES[i]);
refAnnuity += INTRINSIC_DATA.getWeight(i) * cdsPricer.annuity(cds, YIELD_CURVE, CREDIT_CURVES[i], CdsPriceType.CLEAN, cdx.getCashSettleTime() + 0.01);
}
ref /= INTRINSIC_DATA.getIndexFactor();
assertEquals(ref, res, tol);
assertEquals(refDirty, resDirty, tol);
assertEquals(refAvgSpread, resAvgSpread, tol);
assertEquals(refAnnuity, resAnnuity, tol);
final IsdaCompliantCreditCurve impliedCurve = INDEX_CAL.impliedIndexCurve(pillarCdx, indexCoupon, YIELD_CURVE, INTRINSIC_DATA);
final int nPillars = INDEX_PILLARS.length;
for (int i = 0; i < nPillars; ++i) {
final double puf = cdsPricer.pv(pillarCdx[i], YIELD_CURVE, impliedCurve, indexCoupon);
final double pufFullCurve = INDEX_CAL.indexPV(pillarCdx[i], indexCoupon, YIELD_CURVE, INTRINSIC_DATA);
assertEquals(pufFullCurve, puf, tol);
}
/*
* Defaulted names
*/
final int[] defaultedNames = new int[] {2, 15, 37, 51 };
final IntrinsicIndexDataBundle intrinsicDataWithDefaulted = INTRINSIC_DATA.withDefault(defaultedNames);
final double resDefaulted = INDEX_CAL.indexPUF(cdx, indexCoupon, YIELD_CURVE, intrinsicDataWithDefaulted);
final double resDefaultedDirty = INDEX_CAL.indexPV(cdx, indexCoupon, YIELD_CURVE, intrinsicDataWithDefaulted, CdsPriceType.DIRTY);
final double resDefaultedAvgSpread = INDEX_CAL.averageSpread(cdx, YIELD_CURVE, intrinsicDataWithDefaulted);
ref = 0.0;
refDirty = 0.0;
refAvgSpread = 0.0;
for (int i = 0; i < indexSize; ++i) {
if (!intrinsicDataWithDefaulted.isDefaulted(i)) {
final CdsAnalytic cds = cdx.withRecoveryRate(1.0 - intrinsicDataWithDefaulted.getLGD(i));
ref += intrinsicDataWithDefaulted.getWeight(i) * cdsPricer.pv(cds, YIELD_CURVE, CREDIT_CURVES[i], indexCoupon);
refDirty += intrinsicDataWithDefaulted.getWeight(i) * cdsPricer.pv(cds, YIELD_CURVE, CREDIT_CURVES[i], indexCoupon, CdsPriceType.DIRTY);
refAvgSpread += intrinsicDataWithDefaulted.getWeight(i) * cdsPricer.parSpread(cds, YIELD_CURVE, CREDIT_CURVES[i]);
}
}
ref /= intrinsicDataWithDefaulted.getIndexFactor();
assertEquals(ref, resDefaulted, tol);
assertEquals(refDirty, resDefaultedDirty, tol);
refAvgSpread /= intrinsicDataWithDefaulted.getIndexFactor();
assertEquals(refAvgSpread, resDefaultedAvgSpread, tol);
final double valTime = DayCounts.ACT_365F.yearFraction(TRADE_DATE, DEFAULT_CALENDAR.shift(TRADE_DATE, 3));
final double resWithValTime = INDEX_CAL.indexPV(cdx, indexCoupon, YIELD_CURVE, INTRINSIC_DATA, CdsPriceType.CLEAN, valTime);
assertEquals(res * INTRINSIC_DATA.getIndexFactor(), resWithValTime, tol);
final IsdaCompliantCreditCurve impliedCurveDefaulted = INDEX_CAL.impliedIndexCurve(pillarCdx, indexCoupon, YIELD_CURVE, intrinsicDataWithDefaulted);
for (int i = 0; i < nPillars; ++i) {
final double puf = cdsPricer.pv(pillarCdx[i], YIELD_CURVE, impliedCurveDefaulted, indexCoupon);
final double pufFullCurve = INDEX_CAL.indexPV(pillarCdx[i], indexCoupon, YIELD_CURVE, intrinsicDataWithDefaulted) / intrinsicDataWithDefaulted.getIndexFactor();
assertEquals(pufFullCurve, puf, tol);
}
/*
* IllegalArgument exception thrown
*/
final int[] defInd = new int[indexSize];
for (int i = 0; i < indexSize; ++i) {
defInd[i] = i;
}
final IntrinsicIndexDataBundle allDefaulted = INTRINSIC_DATA.withDefault(defInd);
try {
INDEX_CAL.indexPUF(cdx, indexCoupon, YIELD_CURVE, allDefaulted);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("Index completely defaulted - not possible to rescale for PUF", e.getMessage());
}
try {
INDEX_CAL.intrinsicIndexSpread(cdx, YIELD_CURVE, allDefaulted);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("Every name in the index is defaulted - cannot calculate a spread", e.getMessage());
}
try {
INDEX_CAL.averageSpread(cdx, YIELD_CURVE, allDefaulted);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("Every name in the index is defaulted - cannot calculate a spread", e.getMessage());
}
try {
INDEX_CAL.impliedIndexCurve(pillarCdx, indexCoupon, YIELD_CURVE, allDefaulted);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("Every name in the index is defaulted - cannot calculate implied index curve", e.getMessage());
}
}
/**
* Default adjusted index with intrinsic data
*/
public void forwardIntrinsicTest() {
final double timeToExpiry = ACT365F.yearFraction(TRADE_DATE, EXPIRY);
/**
* Test expected default settlement
*/
final int miniIndexSize = 3;
final IsdaCompliantCreditCurve[] ccs = Arrays.copyOf(INTRINSIC_DATA.getCreditCurves(), miniIndexSize);
final IsdaCompliantCreditCurve[] ccsDefault = Arrays.copyOf(INTRINSIC_DATA.getCreditCurves(), miniIndexSize);
final double[] rrs = new double[miniIndexSize];
final BitSet bitset = new BitSet(miniIndexSize);
for (int i = 0; i < miniIndexSize; ++i) {
rrs[i] = 1.0 - INTRINSIC_DATA.getLGD(i);
}
final IntrinsicIndexDataBundle dataNoDefault = new IntrinsicIndexDataBundle(ccs, rrs);
final int defaultedName = 1;
bitset.set(defaultedName);
ccsDefault[defaultedName] = null;
final IntrinsicIndexDataBundle dataDefault = new IntrinsicIndexDataBundle(ccsDefault, rrs, bitset);
final double expectedDefaultSettlement = INDEX_CAL.expectedDefaultSettlementValue(timeToExpiry, dataDefault);
final double expectedDefaultSettlementNo = INDEX_CAL.expectedDefaultSettlementValue(timeToExpiry, dataNoDefault);
double ref = 0.0;
for (int i = 0; i < miniIndexSize; ++i) {
ref += (1.0 - ccs[i].getSurvivalProbability(timeToExpiry)) * (1.0 - rrs[i]) / miniIndexSize;
}
assertEquals(ref, expectedDefaultSettlementNo, 1.0e-12);
final double refDiff = ccs[defaultedName].getSurvivalProbability(timeToExpiry) * (1.0 - rrs[defaultedName]) / miniIndexSize;
assertEquals(expectedDefaultSettlementNo + refDiff, expectedDefaultSettlement, 1.0e-12);
/**
* Test indexProtLeg with forward starting CDS index
*/
final double prot = INDEX_CAL.indexProtLeg(FWD_START_CDX, YIELD_CURVE, dataNoDefault);
final double protDefault = INDEX_CAL.indexProtLeg(FWD_START_CDX, YIELD_CURVE, dataDefault);
final AnalyticCdsPricer defaultPricer = new AnalyticCdsPricer();
final CdsAnalytic zeroRecCDX = FWD_START_CDX.withRecoveryRate(0.0);
final double baseTime = 0.0;
final double refProtDiff = defaultPricer.protectionLeg(zeroRecCDX, YIELD_CURVE, dataNoDefault.getCreditCurve(defaultedName), baseTime) * (1.0 - rrs[defaultedName]) / miniIndexSize /
YIELD_CURVE.getDiscountFactor(FWD_START_CDX.getCashSettleTime());
ref = 0.0;
for (int i = 0; i < miniIndexSize; ++i) {
ref += defaultPricer.protectionLeg(zeroRecCDX, YIELD_CURVE, ccs[i], baseTime) * (1.0 - rrs[i]) / miniIndexSize / YIELD_CURVE.getDiscountFactor(FWD_START_CDX.getCashSettleTime());
}
assertEquals(ref, prot, 1.0e-12);
assertEquals(prot - refProtDiff, protDefault, 1.0e-12);
/**
* Test indexAnnuity with forward starting CDS index
*/
final double annuity = INDEX_CAL.indexAnnuity(FWD_START_CDX, YIELD_CURVE, dataNoDefault);
final double annuityDefault = INDEX_CAL.indexAnnuity(FWD_START_CDX, YIELD_CURVE, dataDefault);
final double refAnnuityDiff = defaultPricer.annuity(FWD_START_CDX, YIELD_CURVE, dataNoDefault.getCreditCurve(defaultedName), CdsPriceType.CLEAN, baseTime) /
miniIndexSize / YIELD_CURVE.getDiscountFactor(FWD_START_CDX.getCashSettleTime());
ref = 0.0;
for (int i = 0; i < miniIndexSize; ++i) {
ref += defaultPricer.annuity(zeroRecCDX, YIELD_CURVE, ccs[i], CdsPriceType.CLEAN, baseTime) / miniIndexSize / YIELD_CURVE.getDiscountFactor(FWD_START_CDX.getCashSettleTime());
}
assertEquals(ref, annuity, 1.0e-12);
assertEquals(annuity - refAnnuityDiff, annuityDefault, 1.0e-12);
/**
* defaultAdjustedForwardIndexValue and defaultAdjustedForwardSpread from computation above
*/
final double atmFwd = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, dataNoDefault);
final double atmFwdDefault = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, dataDefault);
assertEquals(prot - INDEX_COUPON * annuity + expectedDefaultSettlementNo, atmFwd, 1.0e-12);
assertEquals((protDefault - INDEX_COUPON * annuityDefault) + expectedDefaultSettlement, atmFwdDefault, 1.0e-12);
final double atmFwdSp = INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, timeToExpiry, YIELD_CURVE, dataNoDefault);
final double atmFwdSpDefault = INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, timeToExpiry, YIELD_CURVE, dataDefault);
assertEquals((prot + expectedDefaultSettlementNo) / annuity, atmFwdSp, 1.0e-12);
assertEquals((protDefault + expectedDefaultSettlement) / annuityDefault, atmFwdSpDefault, 1.0e-12);
/**
* Exception expected
*/
final double negativeTime = -timeToExpiry;
try {
INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, negativeTime, YIELD_CURVE, INDEX_COUPON, INTRINSIC_DATA);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("timeToExpiry given as " + negativeTime, e.getMessage());
}
final double longTime = FWD_START_CDX.getEffectiveProtectionStart() * 1.5;
try {
INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, longTime, YIELD_CURVE, INDEX_COUPON, INTRINSIC_DATA);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("effective protection start of " + FWD_START_CDX.getEffectiveProtectionStart() + " is less than time to expiry of " + longTime + ". Must provide a forward starting CDS",
e.getMessage());
}
try {
INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, negativeTime, YIELD_CURVE, INTRINSIC_DATA);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("timeToExpiry given as " + negativeTime, e.getMessage());
}
try {
INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, longTime, YIELD_CURVE, INTRINSIC_DATA);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("effective protection start of " + FWD_START_CDX.getEffectiveProtectionStart() + " is less than time to expiry of " + longTime + ". Must provide a forward starting CDS",
e.getMessage());
}
}
/**
* Default adjusted index with homogeneous pool
*/
public void forwardHomogeneousTest() {
final double timeToExpiry = ACT365F.yearFraction(TRADE_DATE, EXPIRY);
final IsdaCompliantCreditCurve indexCurve = CREDIT_CURVE_BUILDER.calibrateCreditCurve(PILLAR_CDX, PILLAR_PUF, YIELD_CURVE);
final double res = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, indexCurve);
final int nPillars = PILLAR_PUF.length;
final double[] pillarPufValues = new double[nPillars];
for (int i = 0; i < nPillars; ++i) {
pillarPufValues[i] = PILLAR_PUF[i].getPointsUpFront();
}
final int size = INTRINSIC_DATA.getIndexSize();
final IsdaCompliantCreditCurve[] ccs = new IsdaCompliantCreditCurve[size];
final double[] rrs = new double[size];
/**
* Use copies of the single credit curve, indexCurve
*/
Arrays.fill(ccs, indexCurve.clone());
Arrays.fill(rrs, 1. - FWD_START_CDX.getLGD());
final IntrinsicIndexDataBundle homData = new IntrinsicIndexDataBundle(ccs, rrs);
final double ref = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, homData);
assertEquals(ref, res, 1.0e-12);
/**
* Fwd spread
*/
final double FwdSpread = INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, timeToExpiry, YIELD_CURVE, indexCurve);
final double zeroPuf = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, YIELD_CURVE, FwdSpread, indexCurve);
final double FwdSpreadRef = INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, timeToExpiry, YIELD_CURVE, homData);
assertEquals(0.0, zeroPuf, 1.0e-12);
assertEquals(FwdSpreadRef, FwdSpread, 1.0e-12);
/**
* Start with another but identical credit curves, still close atm forward
*/
Arrays.fill(ccs, INTRINSIC_DATA.getCreditCurve(3).clone());
final IntrinsicIndexDataBundle homData1 = new IntrinsicIndexDataBundle(ccs, rrs);
final IntrinsicIndexDataBundle homData1Adj = PSA.adjustCurves(pillarPufValues, PILLAR_CDX, INDEX_COUPON, YIELD_CURVE, homData1);
final double ref1 = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, homData1Adj);
assertEquals(ref1, res, 1.0e-5);
/**
* Exception expected
*/
final double negativeTime = -timeToExpiry;
try {
INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, negativeTime, YIELD_CURVE, INDEX_COUPON, indexCurve);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("timeToExpiry given as " + negativeTime, e.getMessage());
}
final double longTime = FWD_START_CDX.getEffectiveProtectionStart() * 1.5;
try {
INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, longTime, YIELD_CURVE, INDEX_COUPON, indexCurve);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("effective protection start of " + FWD_START_CDX.getEffectiveProtectionStart() + " is less than time to expiry of " + longTime + ". Must provide a forward starting CDS",
e.getMessage());
}
try {
INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, negativeTime, YIELD_CURVE, indexCurve);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("timeToExpiry given as " + negativeTime, e.getMessage());
}
try {
INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, longTime, YIELD_CURVE, indexCurve);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("effective protection start of " + FWD_START_CDX.getEffectiveProtectionStart() + " is less than time to expiry of " + longTime + ". Must provide a forward starting CDS",
e.getMessage());
}
}
/**
*
*/
public void forwardHomogeneousDefaultTest() {
final double timeToExpiry = ACT365F.yearFraction(TRADE_DATE, EXPIRY);
final int initialIndexSize = INTRINSIC_DATA.getIndexSize();
final IsdaCompliantCreditCurve indexCurve = CREDIT_CURVE_BUILDER.calibrateCreditCurve(PILLAR_CDX, PILLAR_PUF, YIELD_CURVE);
double initialDefaultSettlement = 0.0;
int numDefaults = 0;
/**
* No default
*/
final double noDefault = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, initialIndexSize, YIELD_CURVE, INDEX_COUPON, indexCurve, initialDefaultSettlement, numDefaults);
final double noDefaultRef = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, indexCurve);
assertEquals(noDefaultRef, noDefault, 1.0e-12);
final IsdaCompliantCreditCurve[] ccs = new IsdaCompliantCreditCurve[initialIndexSize];
final double[] rrs = new double[initialIndexSize];
Arrays.fill(ccs, indexCurve.clone());
Arrays.fill(rrs, 1.0 - FWD_START_CDX.getLGD());
/**
* With default
*/
final int[] defaulted = new int[] {2, 42, 55, 56, 82, 81 };
final IntrinsicIndexDataBundle dataDefaulted = (new IntrinsicIndexDataBundle(ccs, rrs)).withDefault(defaulted);
initialDefaultSettlement = initialDefaultSettlementCalculator(dataDefaulted);
numDefaults = defaulted.length;
final double atmFwd = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, initialIndexSize, YIELD_CURVE, INDEX_COUPON, indexCurve, initialDefaultSettlement, numDefaults);
final double atmFwdRef = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, dataDefaulted);
assertEquals(atmFwdRef, atmFwd, 1.0e-12);
/**
* Fwd spread
*/
final double FwdSpread = INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, timeToExpiry, initialIndexSize, YIELD_CURVE, indexCurve, initialDefaultSettlement, numDefaults);
final double zeroPuf = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, initialIndexSize, YIELD_CURVE, FwdSpread, indexCurve, initialDefaultSettlement, numDefaults);
final double FwdSpreadRef = INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, timeToExpiry, YIELD_CURVE, dataDefaulted);
assertEquals(0.0, zeroPuf, 1.0e-12);
assertEquals(FwdSpreadRef, FwdSpread, 1.0e-12);
/**
* With default using another but identical credit curves, close atm value returned
*/
final IsdaCompliantCreditCurve sampleCC = INTRINSIC_DATA.getCreditCurve(22).clone();
Arrays.fill(ccs, sampleCC);
final IntrinsicIndexDataBundle dataDefaulted1 = (new IntrinsicIndexDataBundle(ccs, rrs)).withDefault(defaulted);
initialDefaultSettlement = initialDefaultSettlementCalculator(dataDefaulted1);
final int nPillars = PILLAR_CDX.length;
final double[] pufValues = new double[nPillars];
for (int i = 0; i < nPillars; ++i) {
pufValues[i] = PILLAR_PUF[i].getPointsUpFront();
}
final IntrinsicIndexDataBundle dataDefaulted1Adj = PSA.adjustCurves(pufValues, PILLAR_CDX, INDEX_COUPON, YIELD_CURVE, dataDefaulted1);
final double atmFwdRef1 = INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, dataDefaulted1Adj);
assertEquals(atmFwdRef1, atmFwd, 1.0e-5);
/**
* Exception expected
*/
final double negativeTime = -timeToExpiry;
try {
INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, negativeTime, initialIndexSize, YIELD_CURVE, INDEX_COUPON, indexCurve, initialDefaultSettlement, numDefaults);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("timeToExpiry given as " + negativeTime, e.getMessage());
}
final double longTime = FWD_START_CDX.getEffectiveProtectionStart() * 1.5;
try {
INDEX_CAL.defaultAdjustedForwardIndexValue(FWD_START_CDX, longTime, initialIndexSize, YIELD_CURVE, INDEX_COUPON, indexCurve, initialDefaultSettlement, numDefaults);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("effective protection start of " + FWD_START_CDX.getEffectiveProtectionStart() + " is less than time to expiry of " + longTime + ". Must provide a forward starting CDS",
e.getMessage());
}
try {
INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, negativeTime, initialIndexSize, YIELD_CURVE, indexCurve, initialDefaultSettlement, numDefaults);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("timeToExpiry given as " + negativeTime, e.getMessage());
}
try {
INDEX_CAL.defaultAdjustedForwardSpread(FWD_START_CDX, longTime, initialIndexSize, YIELD_CURVE, indexCurve, initialDefaultSettlement, numDefaults);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("effective protection start of " + FWD_START_CDX.getEffectiveProtectionStart() + " is less than time to expiry of " + longTime + ". Must provide a forward starting CDS",
e.getMessage());
}
final int smallInitialSize = 0;
try {
INDEX_CAL.expectedDefaultSettlementValue(smallInitialSize, timeToExpiry, indexCurve, FWD_START_CDX.getLGD(), initialDefaultSettlement, smallInitialSize);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("initialIndexSize is " + smallInitialSize, e.getMessage());
}
final int negativeDefault = -5;
try {
INDEX_CAL.expectedDefaultSettlementValue(initialIndexSize, timeToExpiry, indexCurve, FWD_START_CDX.getLGD(), initialDefaultSettlement, negativeDefault);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("negative numDefaults", e.getMessage());
}
final int largeDefault = initialIndexSize + 1;
try {
INDEX_CAL.expectedDefaultSettlementValue(initialIndexSize, timeToExpiry, indexCurve, FWD_START_CDX.getLGD(), initialDefaultSettlement, largeDefault);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("More defaults (" + largeDefault + ") than size of index (" + initialIndexSize + ")", e.getMessage());
}
}
private double initialDefaultSettlementCalculator(final IntrinsicIndexDataBundle intrinsicData) {
final int size = intrinsicData.getIndexSize();
double res = 0.0;
for (int i = 0; i < size; ++i) {
if (intrinsicData.isDefaulted(i)) {
res += intrinsicData.getLGD(i);
}
}
return res / size;
}
}