/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.credit.index;
import static com.opengamma.analytics.financial.credit.options.CDSIndexPrvider.CDX_NA_HY_20140213_PRICES;
import static com.opengamma.analytics.financial.credit.options.CDSIndexPrvider.CDX_NA_HY_20140213_RECOVERY_RATES;
import static com.opengamma.analytics.financial.credit.options.CDSIndexPrvider.CDX_NA_HY_21_COUPON;
import static com.opengamma.analytics.financial.credit.options.CDSIndexPrvider.CDX_NA_HY_21_RECOVERY_RATE;
import static com.opengamma.analytics.financial.credit.options.CDSIndexPrvider.INDEX_TENORS;
import static com.opengamma.analytics.financial.credit.options.CDSIndexPrvider.getCDX_NA_HY_20140213_CreditCurves;
import static com.opengamma.analytics.financial.credit.options.YieldCurveProvider.ISDA_USD_20140213;
import static org.testng.AssertJUnit.assertEquals;
import java.util.Arrays;
import java.util.BitSet;
import org.testng.annotations.Test;
import org.threeten.bp.LocalDate;
import org.threeten.bp.Period;
import com.opengamma.analytics.financial.credit.isdastandardmodel.CDSAnalytic;
import com.opengamma.analytics.financial.credit.isdastandardmodel.CDSAnalyticFactory;
import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDABaseTest;
import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDACompliantCreditCurve;
import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDACompliantYieldCurve;
/**
*
*/
public class PortfolioSwapAdjustmentTest 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_COUPON = CDX_NA_HY_21_COUPON;
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 double[] PRICES = CDX_NA_HY_20140213_PRICES;
private static PortfolioSwapAdjustment PSA = new PortfolioSwapAdjustment();
private static CDSIndexCalculator INDEX_CAL = new CDSIndexCalculator();
private static CDSAnalyticFactory FACTORY = new CDSAnalyticFactory(INDEX_RR);
@Test
public void singleTermAdjustmentTest() {
final CDSAnalytic[] cdx = FACTORY.makeCDX(TRADE_DATE, INDEX_PILLARS);
final int n = cdx.length;
for (int i = 0; i < n; i++) {
final double puf = 1 - PRICES[i];
final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(puf, cdx[i], INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA);
final double puf2 = INDEX_CAL.indexPUF(cdx[i], INDEX_COUPON, YIELD_CURVE, adjCurves);
assertEquals(puf, puf2, 1e-14);
}
}
@Test
public void singleTermAdjustmentWithDefaultsTest() {
final CDSAnalytic[] cdx = FACTORY.makeCDX(TRADE_DATE, INDEX_PILLARS);
final int n = cdx.length;
final IntrinsicIndexDataBundle data = INTRINSIC_DATA.withDefault(7);
for (int i = 0; i < n; i++) {
final double puf = 1 - PRICES[i];
final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(puf, cdx[i], INDEX_COUPON, YIELD_CURVE, data);
final double puf2 = INDEX_CAL.indexPUF(cdx[i], INDEX_COUPON, YIELD_CURVE, adjCurves);
assertEquals(puf, puf2, 1e-14);
}
/**
* Exception expected
*/
final double largePuf = 1.2;
try {
PSA.adjustCurves(largePuf, cdx[1], INDEX_COUPON, YIELD_CURVE, data);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("indexPUF must be given as a fraction. Value of " + largePuf + " is too high.", e.getMessage());
}
final double negativeCoupon = -100. * 1.e-4;
try {
PSA.adjustCurves(0.6, cdx[1], negativeCoupon, YIELD_CURVE, data);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("indexCoupon cannot be negative", e.getMessage());
}
final double largeCoupon = 250.;
try {
PSA.adjustCurves(0.6, cdx[1], largeCoupon, YIELD_CURVE, data);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("indexCoupon should be a fraction. The value of " + largeCoupon + " would be a coupon of " + largeCoupon * 1.e4, e.getMessage());
}
/**
* null curve for a defaulted names
*/
final int[] defaulted = new int[] {1, 33, 45, 89 };
BitSet bitset = new BitSet(INTRINSIC_DATA.getIndexSize());
final ISDACompliantCreditCurve[] ccs = INTRINSIC_DATA.getCreditCurves().clone();
final double[] rrs = new double[INTRINSIC_DATA.getIndexSize()];
for (int i = 0; i < defaulted.length; ++i) {
bitset.set(defaulted[i]);
ccs[defaulted[i]] = null;
}
for (int i = 0; i < INTRINSIC_DATA.getIndexSize(); ++i) {
rrs[i] = 1. - INTRINSIC_DATA.getLGD(i);
}
final IntrinsicIndexDataBundle dataWithDefault = new IntrinsicIndexDataBundle(ccs, rrs, bitset);
for (int i = 0; i < n; i++) {
final double puf = 1 - PRICES[i];
final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(puf, cdx[i], INDEX_COUPON, YIELD_CURVE, dataWithDefault);
final double puf2 = INDEX_CAL.indexPUF(cdx[i], INDEX_COUPON, YIELD_CURVE, adjCurves);
assertEquals(puf, puf2, 1e-14);
}
}
@Test
public void multiTermAdjustmentTest() {
final CDSAnalytic[] cdx = FACTORY.makeCDX(TRADE_DATE, INDEX_PILLARS);
final int n = cdx.length;
final double[] puf = new double[n];
for (int i = 0; i < n; i++) {
puf[i] = 1 - PRICES[i];
}
final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(puf, cdx, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA);
for (int i = 0; i < n; i++) {
final double puf2 = INDEX_CAL.indexPUF(cdx[i], INDEX_COUPON, YIELD_CURVE, adjCurves);
assertEquals(puf[i], puf2, 1e-14);
}
/**
* Reduces into the flat curve case
*/
for (int i = 0; i < n; ++i) {
final IntrinsicIndexDataBundle sglCv = PSA.adjustCurves(puf[i], cdx[i], INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA);
final IntrinsicIndexDataBundle mltCv = PSA.adjustCurves(new double[] {puf[i] }, new CDSAnalytic[] {cdx[i] }, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA);
sglCv.equals(mltCv);
}
/**
* Exception expected
*/
final double[] shortPufs = Arrays.copyOf(puf, n - 2);
try {
PSA.adjustCurves(shortPufs, cdx, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("number of indexCDS (" + cdx.length + ") does not match number of indexPUF (" + shortPufs.length + ")", e.getMessage());
}
final double negativeCoupon = -100. * 1.e-4;
try {
PSA.adjustCurves(puf, cdx, negativeCoupon, YIELD_CURVE, INTRINSIC_DATA);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("indexCoupon cannot be negative", e.getMessage());
}
final double largeCoupon = 250.;
try {
PSA.adjustCurves(puf, cdx, largeCoupon, YIELD_CURVE, INTRINSIC_DATA);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("indexCoupon should be a fraction. The value of " + largeCoupon + " would be a coupon of " + largeCoupon * 1.e4, e.getMessage());
}
final double[] largePufs = Arrays.copyOf(puf, n);
final int position = 2;
largePufs[position] += 10.;
try {
PSA.adjustCurves(largePufs, cdx, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("indexPUF must be given as a fraction. Value of " + largePufs[position] + " is too high.", e.getMessage());
}
final CDSAnalytic[] wrongCdx = Arrays.copyOf(cdx, n);
wrongCdx[1] = cdx[2];
wrongCdx[2] = cdx[1];
try {
PSA.adjustCurves(puf, wrongCdx, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA);
throw new RuntimeException();
} catch (final Exception e) {
assertEquals("indexCDS must be in assending order of maturity", e.getMessage());
}
}
@Test
public void multiTermAdjustmentWithDefaultTest() {
final CDSAnalytic[] cdx = FACTORY.makeCDX(TRADE_DATE, INDEX_PILLARS);
final int n = cdx.length;
final IntrinsicIndexDataBundle data = INTRINSIC_DATA.withDefault(0, 4, 78);
final double[] puf = new double[n];
for (int i = 0; i < n; i++) {
puf[i] = 1 - PRICES[i];
}
final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(puf, cdx, INDEX_COUPON, YIELD_CURVE, data);
for (int i = 0; i < n; i++) {
final double puf2 = INDEX_CAL.indexPUF(cdx[i], INDEX_COUPON, YIELD_CURVE, adjCurves);
assertEquals(puf[i], puf2, 1e-14);
}
/**
* null curve for a defaulted names
*/
final int[] defaulted = new int[] {1, 33, 45, 89 };
BitSet bitset = new BitSet(INTRINSIC_DATA.getIndexSize());
final ISDACompliantCreditCurve[] ccs = INTRINSIC_DATA.getCreditCurves().clone();
final double[] rrs = new double[INTRINSIC_DATA.getIndexSize()];
for (int i = 0; i < defaulted.length; ++i) {
bitset.set(defaulted[i]);
ccs[defaulted[i]] = null;
}
for (int i = 0; i < INTRINSIC_DATA.getIndexSize(); ++i) {
rrs[i] = 1. - INTRINSIC_DATA.getLGD(i);
}
final IntrinsicIndexDataBundle dataWithDefault = new IntrinsicIndexDataBundle(ccs, rrs, bitset);
final IntrinsicIndexDataBundle dataWithDefaultAdj = PSA.adjustCurves(puf, cdx, INDEX_COUPON, YIELD_CURVE, dataWithDefault);
for (int i = 0; i < n; i++) {
final double puf2 = INDEX_CAL.indexPUF(cdx[i], INDEX_COUPON, YIELD_CURVE, dataWithDefaultAdj);
assertEquals(puf[i], puf2, 1e-14);
}
}
/**
* breakpoints of a credit curve are not modified
*/
@Test
public void knotsCoincideTest() {
final CDSAnalytic[] cdx = FACTORY.makeCDX(TRADE_DATE, INDEX_PILLARS);
final int nPillars = cdx.length;
final int indexSize = INTRINSIC_DATA.getIndexSize();
final double[] indexKnots = new double[nPillars];
final double[] rt = new double[nPillars];
final double[] puf = new double[nPillars];
for (int i = 0; i < nPillars; ++i) {
indexKnots[i] = cdx[i].getProtectionEnd();
rt[i] = indexKnots[i] * 0.05;
puf[i] = 1 - PRICES[i];
}
final int position = 32;
final ISDACompliantCreditCurve[] ccs = INTRINSIC_DATA.getCreditCurves().clone();
ccs[position] = ISDACompliantCreditCurve.makeFromRT(indexKnots, rt);
final double[] rrs = new double[indexSize];
for (int i = 0; i < indexSize; ++i) {
rrs[i] = 1. - INTRINSIC_DATA.getLGD(i);
}
final IntrinsicIndexDataBundle data = new IntrinsicIndexDataBundle(ccs, rrs);
final IntrinsicIndexDataBundle dataAdj = PSA.adjustCurves(puf, cdx, INDEX_COUPON, YIELD_CURVE, data);
for (int i = 0; i < nPillars; ++i) {
final double puf2 = INDEX_CAL.indexPUF(cdx[i], INDEX_COUPON, YIELD_CURVE, dataAdj);
assertEquals(puf[i], puf2, 1e-14);
}
}
}