/**
* 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.ImmDateLogic.getIMMDateSet;
import static com.opengamma.strata.pricer.impl.credit.isda.ImmDateLogic.getNextIMMDate;
import static com.opengamma.strata.pricer.impl.credit.isda.ImmDateLogic.getPrevIMMDate;
import static org.testng.AssertJUnit.assertEquals;
import java.time.LocalDate;
import java.time.Month;
import java.time.Period;
import java.util.Arrays;
import org.testng.annotations.Test;
import com.opengamma.strata.collect.array.DoubleArray;
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.CdsRiskFactors;
import com.opengamma.strata.pricer.impl.credit.isda.FastCreditCurveBuilder;
import com.opengamma.strata.pricer.impl.credit.isda.FiniteDifferenceSpreadSensitivityCalculator;
import com.opengamma.strata.pricer.impl.credit.isda.HedgeRatioCalculator;
import com.opengamma.strata.pricer.impl.credit.isda.InterestRateSensitivityCalculator;
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.IsdaCompliantCreditCurveBuilder;
import com.opengamma.strata.pricer.impl.credit.isda.IsdaCompliantCreditCurveBuilder.ArbitrageHandling;
import com.opengamma.strata.pricer.impl.credit.isda.IsdaCompliantYieldCurve;
/**
* End-to-end test for single name CDSs
*/
@Test
public class SingleNameCdsE2ETest extends IsdaBaseTest {
// Calculators: all calculations are based on ORIGINAL_ISDA
private static final FiniteDifferenceSpreadSensitivityCalculator FD_SPREAD_SENSE_CAL = new FiniteDifferenceSpreadSensitivityCalculator();
private static final CdsRiskFactors RISK_CAL = new CdsRiskFactors();
private static final HedgeRatioCalculator HEDGE_CAL = new HedgeRatioCalculator();
private static final InterestRateSensitivityCalculator IR_CAL = new InterestRateSensitivityCalculator();
// Trade
private static final CdsAnalyticFactory CDS_FACTORY = new CdsAnalyticFactory(0.4);
private static final double NOTIONAL = 1e6;
private static final double COUPON = 0.01;
private static final LocalDate TRADE_DATE = LocalDate.of(2011, Month.JUNE, 13);
private static final LocalDate NEXT_IMM = getNextIMMDate(TRADE_DATE);
private static final LocalDate STEPIN = TRADE_DATE.plusDays(1);
private static final LocalDate CASH_SETTLE_DATE = DEFAULT_CALENDAR.shift(TRADE_DATE, 3); // AKA valuation date
private static final LocalDate STARTDATE = getPrevIMMDate(TRADE_DATE);
// Yield curve
private static final LocalDate SPOT_DATE = LocalDate.of(2011, Month.JUNE, 15);
private static final String[] YIELD_CURVE_POINTS = new String[] {"1M", "2M", "3M", "6M", "9M", "1Y", "2Y", "3Y",
"4Y", "5Y", "6Y", "7Y", "8Y", "9Y", "10Y", "11Y", "12Y", "15Y", "20Y", "25Y", "30Y"};
private static final String[] YIELD_CURVE_INSTRUMENTS = new String[] {"M", "M", "M", "M", "M", "M", "S", "S", "S",
"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"};
private static final double[] YIELD_CURVE_RATES = new double[] {0.00445, 0.009488, 0.012337, 0.017762, 0.01935,
0.020838, 0.01652, 0.02018, 0.023033, 0.02525, 0.02696, 0.02825, 0.02931, 0.03017,
0.03092, 0.0316, 0.03231, 0.03367, 0.03419, 0.03411, 0.03412};
private static final IsdaCompliantYieldCurve YIELD_CURVE = makeYieldCurve(TRADE_DATE, SPOT_DATE, YIELD_CURVE_POINTS,
YIELD_CURVE_INSTRUMENTS, YIELD_CURVE_RATES, ACT360, D30360, Period.ofYears(1));
// Credit curve form pillar CDSs
private static final Period[] TENORS = new Period[] {Period.ofMonths(6), Period.ofYears(1), Period.ofYears(3),
Period.ofYears(5), Period.ofYears(7), Period.ofYears(10)};
private static final LocalDate[] PILLAR_DATES = getIMMDateSet(NEXT_IMM, TENORS);
private static final LocalDate[] IMM_DATES = getIMMDateSet(NEXT_IMM, 41);
private static final LocalDate[] MATURITIES_6M_STEP;
private static final LocalDate[] MATURITIES_1Y_STEP;
private static final double[] SPREADS = new double[] {0.007926718, 0.007926718, 0.012239372, 0.016978579,
0.019270856, 0.02086048};
private static final CdsAnalytic[] PILLAR_CDSS;
private static final IsdaCompliantCreditCurve CREDIT_CURVE;
static {
final IsdaCompliantCreditCurveBuilder curveBuilder = new FastCreditCurveBuilder(ORIGINAL_ISDA,
ArbitrageHandling.ZeroHazardRate);
final int nPillars = PILLAR_DATES.length;
PILLAR_CDSS = new CdsAnalytic[nPillars];
for (int i = 0; i < nPillars; i++) {
PILLAR_CDSS[i] = new CdsAnalytic(TRADE_DATE, STEPIN, CASH_SETTLE_DATE, STARTDATE, PILLAR_DATES[i],
PAY_ACC_ON_DEFAULT, PAYMENT_INTERVAL, STUB, PROCTECTION_START, RECOVERY_RATE);
}
CREDIT_CURVE = curveBuilder.calibrateCreditCurve(PILLAR_CDSS, SPREADS, YIELD_CURVE);
final int n = IMM_DATES.length;
final LocalDate[] temp = new LocalDate[n];
int count = 0;
for (int i = 0; i < n; i = i + 2) {
temp[count++] = IMM_DATES[i];
}
MATURITIES_6M_STEP = new LocalDate[count];
System.arraycopy(temp, 0, MATURITIES_6M_STEP, 0, count);
count = 0;
for (int i = 0; i < n; i = i + 4) {
temp[count++] = IMM_DATES[i];
}
MATURITIES_1Y_STEP = new LocalDate[count];
System.arraycopy(temp, 0, MATURITIES_1Y_STEP, 0, count);
}
// Bucket CDSs
private static final Period[] BUCKETS = new Period[] {Period.ofMonths(6), Period.ofYears(1), Period.ofYears(2),
Period.ofYears(3), Period.ofYears(4), Period.ofYears(5), Period.ofYears(6), Period.ofYears(7), Period.ofYears(8),
Period.ofYears(9), Period.ofYears(10), Period.ofYears(12), Period.ofYears(15), Period.ofYears(20),
Period.ofYears(25), Period.ofYears(30)};
private static final LocalDate[] BUCKET_DATES = getIMMDateSet(NEXT_IMM, BUCKETS);
private static final CdsAnalytic[] BUCKET_CDSS = CDS_FACTORY.makeCds(TRADE_DATE, STARTDATE, BUCKET_DATES);
// Hedge CDSs
private static final Period[] HEDGES = new Period[] {Period.of(1, 6, 0), Period.of(2, 0, 0), Period.of(6, 0, 0),
Period.of(9, 0, 0), Period.of(20, 0, 0)};
private static final LocalDate[] HEDGE_DATES = getIMMDateSet(NEXT_IMM, HEDGES);
private static final CdsAnalytic[] HEDGE_CDSS = CDS_FACTORY.makeCds(TRADE_DATE, STARTDATE, HEDGE_DATES);
private static final double[] HEDGE_COUPON = new double[HEDGES.length];
static {
Arrays.fill(HEDGE_COUPON, COUPON);
}
private static final double TOL = 1.0e-8;
/**
* Standard CDS with short maturity
*/
@Test
public void IMMCDSTest1() {
// Build pricing CDS
Period tenor = Period.of(2, 3, 0);
LocalDate maturityDate = NEXT_IMM.plus(tenor); // 2013-09-20
CdsAnalytic pricingCDS = new CdsAnalytic(TRADE_DATE, STEPIN, CASH_SETTLE_DATE, STARTDATE, maturityDate,
PAY_ACC_ON_DEFAULT, PAYMENT_INTERVAL, STUB, PROCTECTION_START, RECOVERY_RATE);
int accrualDays = pricingCDS.getAccuredDays();
double accruedPremium = pricingCDS.getAccruedPremium(COUPON) * NOTIONAL;
double cleanPV = PRICER.pv(pricingCDS, YIELD_CURVE, CREDIT_CURVE, COUPON) * NOTIONAL;
double dirtyPV = PRICER.pv(pricingCDS, YIELD_CURVE, CREDIT_CURVE, COUPON, CdsPriceType.DIRTY) * NOTIONAL;
IsdaCompliantYieldCurve constantCurve = new IsdaCompliantYieldCurve(1.0, 0.0);
double expectedLoss = PRICER.protectionLeg(pricingCDS, constantCurve, CREDIT_CURVE) * NOTIONAL;
double cleanRPV01 = PRICER.annuity(pricingCDS, YIELD_CURVE, CREDIT_CURVE);
double dirtyRPV01 = PRICER.annuity(pricingCDS, YIELD_CURVE, CREDIT_CURVE, CdsPriceType.DIRTY);
double parSpread = PRICER.parSpread(pricingCDS, YIELD_CURVE, CREDIT_CURVE) * TEN_THOUSAND; // BPS
double parallelIR01 = IR_CAL.parallelIR01(pricingCDS, COUPON, CREDIT_CURVE, YIELD_CURVE) * NOTIONAL;
double[] bucketedIR01 = IR_CAL.bucketedIR01(pricingCDS, COUPON, CREDIT_CURVE, YIELD_CURVE);
for (int i = 0; i < bucketedIR01.length; ++i) {
bucketedIR01[i] *= NOTIONAL;
}
double parallelCS01 = FD_SPREAD_SENSE_CAL.parallelCS01FromCreditCurve(pricingCDS, COUPON, BUCKET_CDSS, YIELD_CURVE,
CREDIT_CURVE, ONE_BP) * ONE_BP * NOTIONAL;
double[] bucketedCS01 = FD_SPREAD_SENSE_CAL.bucketedCS01FromCreditCurve(pricingCDS, COUPON, BUCKET_CDSS,
YIELD_CURVE, CREDIT_CURVE, ONE_BP);
for (int i = 0; i < bucketedCS01.length; ++i) {
bucketedCS01[i] *= (NOTIONAL * ONE_BP);
}
double valueOnDefault = RISK_CAL.valueOnDefault(pricingCDS, YIELD_CURVE, CREDIT_CURVE, COUPON) * NOTIONAL;
double recovery01 = RISK_CAL.recoveryRateSensitivity(pricingCDS, YIELD_CURVE, CREDIT_CURVE) * NOTIONAL; // Analytic
double[] bucketCDSCoupons = new double[PILLAR_CDSS.length];
Arrays.fill(bucketCDSCoupons, COUPON);
DoubleArray hedgeRatio = HEDGE_CAL.getHedgeRatios(pricingCDS, COUPON, HEDGE_CDSS, HEDGE_COUPON,
CREDIT_CURVE, YIELD_CURVE);
double[] expectedBIR01 = new double[] {-3.554654175175198E-4, -0.011674986050841385, 0.027624587315561167,
0.02670611760208219, 0.03873563315243134, -0.1856733208432937, -0.4532763188576372, 0.05369920429154629, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
double[] expectedBCS01 = new double[] {-0.0598207645016724, -0.17567436897888977, 146.42339811065176,
74.44971215266743, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
double[] expectedRatio = new double[] {-0.4961595903306217, 1.496146243832723, 1.4643659774973175E-10,
-1.7784851094986757E-11, 1.6747710535268755E-11};
assertEquals("accrual days", 86, accrualDays);
assertEqualsRelativeTol("accrued premium", 2388.888888888889, accruedPremium, TOL);
assertEqualsRelativeTol("clean PV", 3353.454362533141, cleanPV, TOL);
assertEqualsRelativeTol("dirty PV", 964.5654736442459, dirtyPV, TOL);
assertEqualsRelativeTol("expected loss", 26064.87887963509, expectedLoss, TOL);
assertEqualsRelativeTol("clean RPV01", 2.2126685809458135, cleanRPV01, TOL);
assertEqualsRelativeTol("dirty RPV01", 2.451557469834703, dirtyRPV01, TOL);
assertEqualsRelativeTol("par spread (BPS)", 115.15570109057948, parSpread, TOL);
assertEqualsRelativeTol("parallel IR01", -0.5042019973929002, parallelIR01, TOL);
assertDoubleArray("bucketed IR01", expectedBIR01, bucketedIR01, TOL);
assertEqualsRelativeTol("parallel CS01", 220.59286685899292, parallelCS01, TOL);
assertDoubleArray("bucketed CS01", expectedBCS01, bucketedCS01, TOL);
assertEqualsRelativeTol("value on default", 596646.5456374668, valueOnDefault, TOL);
assertEqualsRelativeTol("recovery01", -42466.90028665213, recovery01, TOL);
assertDoubleArray("hedge ratio", expectedRatio, hedgeRatio.toArray(), TOL);
}
/**
* Standard CDS with longer maturity
*/
@Test
public void IMMCDSTest2() {
// Build pricing CDS
Period tenor = Period.of(17, 0, 0);
LocalDate maturityDate = NEXT_IMM.plus(tenor); // 2028-06-20
CdsAnalytic pricingCDS = new CdsAnalytic(TRADE_DATE, STEPIN, CASH_SETTLE_DATE, STARTDATE, maturityDate,
PAY_ACC_ON_DEFAULT, PAYMENT_INTERVAL, STUB, PROCTECTION_START, RECOVERY_RATE);
int accrualDays = pricingCDS.getAccuredDays();
double accruedPremium = pricingCDS.getAccruedPremium(COUPON) * NOTIONAL;
double cleanPV = PRICER.pv(pricingCDS, YIELD_CURVE, CREDIT_CURVE, COUPON) * NOTIONAL;
double dirtyPV = PRICER.pv(pricingCDS, YIELD_CURVE, CREDIT_CURVE, COUPON, CdsPriceType.DIRTY) * NOTIONAL;
IsdaCompliantYieldCurve constantCurve = new IsdaCompliantYieldCurve(1.0, 0.0);
double expectedLoss = PRICER.protectionLeg(pricingCDS, constantCurve, CREDIT_CURVE) * NOTIONAL;
double cleanRPV01 = PRICER.annuity(pricingCDS, YIELD_CURVE, CREDIT_CURVE);
double dirtyRPV01 = PRICER.annuity(pricingCDS, YIELD_CURVE, CREDIT_CURVE, CdsPriceType.DIRTY);
double parSpread = PRICER.parSpread(pricingCDS, YIELD_CURVE, CREDIT_CURVE) * TEN_THOUSAND; // BPS
double parallelIR01 = IR_CAL.parallelIR01(pricingCDS, COUPON, CREDIT_CURVE, YIELD_CURVE) * NOTIONAL;
double[] bucketedIR01 = IR_CAL.bucketedIR01(pricingCDS, COUPON, CREDIT_CURVE, YIELD_CURVE);
for (int i = 0; i < bucketedIR01.length; ++i) {
bucketedIR01[i] *= NOTIONAL;
}
double parallelCS01 = FD_SPREAD_SENSE_CAL.parallelCS01FromCreditCurve(pricingCDS, COUPON, BUCKET_CDSS, YIELD_CURVE,
CREDIT_CURVE, ONE_BP) * ONE_BP * NOTIONAL;
double[] bucketedCS01 = FD_SPREAD_SENSE_CAL.bucketedCS01FromCreditCurve(pricingCDS, COUPON, BUCKET_CDSS,
YIELD_CURVE, CREDIT_CURVE, ONE_BP);
for (int i = 0; i < bucketedCS01.length; ++i) {
bucketedCS01[i] *= (NOTIONAL * ONE_BP);
}
double valueOnDefault = RISK_CAL.valueOnDefault(pricingCDS, YIELD_CURVE, CREDIT_CURVE, COUPON) * NOTIONAL;
double recovery01 = RISK_CAL.recoveryRateSensitivity(pricingCDS, YIELD_CURVE, CREDIT_CURVE) * NOTIONAL;
double[] bucketCDSCoupons = new double[PILLAR_CDSS.length];
Arrays.fill(bucketCDSCoupons, COUPON);
DoubleArray hedgeRatio = HEDGE_CAL.getHedgeRatios(pricingCDS, COUPON, HEDGE_CDSS, HEDGE_COUPON,
CREDIT_CURVE, YIELD_CURVE);
double[] expectedBIR01 = new double[] {0.10195891236852717, -0.011674986061249726, 0.027624587339847295,
0.026706117595143297, 0.03873563314549244, -0.1856733208294159, -0.8543973007979488, -2.568224954563325,
-5.036815800052441, -6.087580077140942, -7.045275780076521, -7.52983564417109, -7.836262445909403,
-8.103245255519642, -8.297088633868466, -8.394421414925635, -15.941139568542706, -25.22143603045368,
-3.6720683527224907, 0.0, 0.0};
double[] expectedBCS01 = new double[] {-0.2488307417891633, -0.8251527622848975, -2.0651772812685376,
-2.5937087356209254, -3.333937820704236, -4.131638490473266, -5.022628897066728, -6.003569124668484,
-7.0661200600175, -8.199981770373732, -14.139374540145244, -30.47380986237469, 482.1018269742794,
496.1353877235286, 0.0, 0.0};
double[] expectedRatio = new double[] {3.7335290727514284E-4, -9.055784969918724E-4, -9.918711889929754E-4,
0.16704483857139465, 0.8382732358018894};
assertEquals("accrual days", 86, accrualDays);
assertEqualsRelativeTol("accrued premium", 2388.888888888889, accruedPremium, TOL);
assertEqualsRelativeTol("clean PV", 127835.89621485685, cleanPV, TOL);
assertEqualsRelativeTol("dirty PV", 125447.00732596798, dirtyPV, TOL);
assertEqualsRelativeTol("expected loss", 296188.29576502816, expectedLoss, TOL);
assertEqualsRelativeTol("clean RPV01", 10.410958359321803, cleanRPV01, TOL);
assertEqualsRelativeTol("dirty RPV01", 10.649847248210692, dirtyRPV01, TOL);
assertEqualsRelativeTol("par spread (BPS)", 222.78974884228086, parSpread, TOL);
assertEqualsRelativeTol("parallel IR01", -106.57168877478695, parallelIR01, TOL);
assertDoubleArray("bucketed IR01", expectedBIR01, bucketedIR01, TOL);
assertEqualsRelativeTol("parallel CS01", 891.7864206657855, parallelCS01, TOL);
assertDoubleArray("bucketed CS01", expectedBCS01, bucketedCS01, TOL);
assertEqualsRelativeTol("value on default", 472164.1037851431, valueOnDefault, TOL);
assertEqualsRelativeTol("recovery01", -386575.79968012485, recovery01, TOL);
assertDoubleArray("hedge ratio", expectedRatio, hedgeRatio.toArray(), TOL);
}
/**
* Example of legacy CDS
*/
@Test
public void LegacyCDSTest() {
Period tenor = Period.of(10, 0, 0);
LocalDate startDate = STEPIN; // T+1 start
LocalDate maturityDate = STEPIN.plus(tenor); // counted from effective date
CdsAnalytic pricingCDS = new CdsAnalytic(TRADE_DATE, STEPIN, CASH_SETTLE_DATE, startDate, maturityDate,
PAY_ACC_ON_DEFAULT, PAYMENT_INTERVAL, STUB, PROCTECTION_START, RECOVERY_RATE);
double coupon = PRICER.parSpread(pricingCDS, YIELD_CURVE, CREDIT_CURVE); // coupon s.t. zero upfront value
int accrualDays = pricingCDS.getAccuredDays();
double accruedPremium = pricingCDS.getAccruedPremium(COUPON) * NOTIONAL;
double cleanPV = PRICER.pv(pricingCDS, YIELD_CURVE, CREDIT_CURVE, coupon) * NOTIONAL;
double dirtyPV = PRICER.pv(pricingCDS, YIELD_CURVE, CREDIT_CURVE, coupon, CdsPriceType.DIRTY) * NOTIONAL;
IsdaCompliantYieldCurve constantCurve = new IsdaCompliantYieldCurve(1.0, 0.0);
double expectedLoss = PRICER.protectionLeg(pricingCDS, constantCurve, CREDIT_CURVE) * NOTIONAL;
double cleanRPV01 = PRICER.annuity(pricingCDS, YIELD_CURVE, CREDIT_CURVE);
double dirtyRPV01 = PRICER.annuity(pricingCDS, YIELD_CURVE, CREDIT_CURVE, CdsPriceType.DIRTY);
double parSpread = coupon * TEN_THOUSAND; // BPS
double parallelIR01 = IR_CAL.parallelIR01(pricingCDS, coupon, CREDIT_CURVE, YIELD_CURVE) * NOTIONAL;
double[] bucketedIR01 = IR_CAL.bucketedIR01(pricingCDS, coupon, CREDIT_CURVE, YIELD_CURVE);
for (int i = 0; i < bucketedIR01.length; ++i) {
bucketedIR01[i] *= NOTIONAL;
}
double parallelCS01 = FD_SPREAD_SENSE_CAL.parallelCS01FromCreditCurve(pricingCDS, coupon, BUCKET_CDSS, YIELD_CURVE,
CREDIT_CURVE, ONE_BP) * ONE_BP * NOTIONAL;
double[] bucketedCS01 = FD_SPREAD_SENSE_CAL.bucketedCS01FromCreditCurve(pricingCDS, coupon, BUCKET_CDSS,
YIELD_CURVE, CREDIT_CURVE, ONE_BP);
for (int i = 0; i < bucketedCS01.length; ++i) {
bucketedCS01[i] *= (NOTIONAL * ONE_BP);
}
double valueOnDefault = RISK_CAL.valueOnDefault(pricingCDS, YIELD_CURVE, CREDIT_CURVE, coupon) * NOTIONAL;
double recovery01 = RISK_CAL.recoveryRateSensitivity(pricingCDS, YIELD_CURVE, CREDIT_CURVE) * NOTIONAL;
double[] bucketCDSCoupons = new double[PILLAR_CDSS.length];
Arrays.fill(bucketCDSCoupons, COUPON);
DoubleArray hedgeRatio = HEDGE_CAL.getHedgeRatios(pricingCDS, coupon, HEDGE_CDSS, HEDGE_COUPON,
CREDIT_CURVE, YIELD_CURVE);
double[] expectedBIR01 = new double[] {-0.006036998789760162, -0.00868633129313956, 0.09937045325481009,
0.16375497519094395, 0.24138012230667805, 0.48729564533500636, 1.2036552658745148, 0.37070870320676796,
-1.3907831437343088, -1.8674246091698876, -2.3723618600424157, -2.4828678812094385, -2.561667216488539,
-2.595901377966392, -0.017970836957426073, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
double[] expectedBCS01 = new double[] {2.770988993816559E-5, -3.717554042381721E-6, 6.128880736255837E-5,
3.365298040236553E-4, 6.236849925400634E-4, 2.3975163521150478E-4, 5.054209273325228E-4, 6.192977519692278E-4,
2.2397053656142418E-4, 11.28402038486076, 758.7836222882893, 0.0, 0.0, 0.0, 0.0, 0.0};
double[] expectedRatio = new double[] {-0.01656660944695296, 0.06035788587176887, 0.05918772036678788,
0.887754035080094, 0.14988679402430255};
assertEquals("accrual days", 0, accrualDays);
assertEqualsRelativeTol("accrued premium", 0.0, accruedPremium, TOL);
assertEqualsRelativeTol("clean PV", 0.0, cleanPV, TOL);
assertEqualsRelativeTol("dirty PV", 0.0, dirtyPV, TOL);
assertEqualsRelativeTol("expected loss", 185422.2147273289, expectedLoss, TOL);
assertEqualsRelativeTol("clean RPV01", 7.701071597676212, cleanRPV01, TOL);
assertEqualsRelativeTol("dirty RPV01", 7.701071597676212, dirtyRPV01, TOL); // no accrued for legacy CDS
assertEqualsRelativeTol("par spread (BPS)", 208.54469451401945, parSpread, TOL);
assertEqualsRelativeTol("parallel IR01", -10.735451012489072, parallelIR01, TOL);
assertDoubleArray("bucketed IR01", expectedBIR01, bucketedIR01, TOL);
assertEqualsRelativeTol("parallel CS01", 769.5220353102217, parallelCS01, TOL);
assertDoubleArray("bucketed CS01", expectedBCS01, bucketedCS01, TOL);
assertEqualsRelativeTol("value on default", 600000.0, valueOnDefault, TOL); // zero PV, no accrued
assertEqualsRelativeTol("recovery01", -267669.60396132956, recovery01, TOL);
assertDoubleArray("hedge ratio", expectedRatio, hedgeRatio.toArray(), TOL);
}
private void assertEqualsRelativeTol(String message, double expected, double result, double relTol) {
double tol = Math.max(1.0, Math.abs(expected)) * relTol;
assertEquals(message, expected, result, tol);
}
private void assertDoubleArray(String message, double[] expected, double[] result, double relTol) {
int nValues = expected.length;
assertEquals(nValues, result.length);
for (int i = 0; i < nValues; ++i) {
assertEqualsRelativeTol(message + "(" + i + "-th element)", expected[i], result[i], relTol);
}
}
}