/** * 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 org.testng.AssertJUnit.assertEquals; import java.time.LocalDate; import org.testng.annotations.Test; 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.FastCreditCurveBuilder; 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.IsdaCompliantYieldCurve; import com.opengamma.strata.pricer.impl.credit.isda.PointsUpFront; /** * */ @Test public class CdsIndexE2ETest extends IsdaBaseTest { private static final double NOTIONAL = 1.0e8; private static final LocalDate TRADE_DATE = LocalDate.of(2014, 2, 13); private static final double INDEX_COUPON = CdsIndexProvider.CDX_NA_HY_21_COUPON; private static final double INDEX_RECOVERY = CdsIndexProvider.CDX_NA_HY_21_RECOVERY_RATE; // yield curve private static IsdaCompliantYieldCurve YIELD_CURVE = ISDA_USD_20140213; // credit curves for single names private static final double[] RECOVERY_RATES = CdsIndexProvider.CDX_NA_HY_20140213_RECOVERY_RATES; private static final IsdaCompliantCreditCurve[] CREDIT_CURVES = CdsIndexProvider.buildCreditCurves(TRADE_DATE, CdsIndexProvider.CDX_NA_HY_20140213_PAR_SPREADS, RECOVERY_RATES, CdsIndexProvider.CDS_TENORS, ISDA_USD_20140213); private static IntrinsicIndexDataBundle INTRINSIC_DATA = new IntrinsicIndexDataBundle(CREDIT_CURVES, RECOVERY_RATES); // index market data (3Y, 5Y, 7Y, 10Y) private static double[] PRICES = CdsIndexProvider.CDX_NA_HY_20140213_PRICES; private static final PointsUpFront[] PILLAR_PUF; private static final CdsAnalyticFactory FACTORY = new CdsAnalyticFactory(INDEX_RECOVERY); private static final CdsAnalytic[] CDX = FACTORY.makeCdx(TRADE_DATE, CdsIndexProvider.INDEX_TENORS); // Calculators private static final PortfolioSwapAdjustment PSA = new PortfolioSwapAdjustment(); private static final CdsIndexCalculator INDEX_CAL = new CdsIndexCalculator(); private static final InterestRateSensitivityCalculator IR_CAL = new InterestRateSensitivityCalculator(); 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]); } } private static final double TOL = 1.0e-8; /** * */ @Test public void indexSingleNodeTest() { int pos = 1; // target CDX is 5Y CdsAnalytic targentCDX = CDX[pos]; int n = PILLAR_PUF.length; double[] indexPUF = new double[n]; for (int i = 0; i < n; i++) { indexPUF[i] = PILLAR_PUF[i].getPointsUpFront(); } int accrualDays = targentCDX.getAccuredDays(); double accruedPremium = targentCDX.getAccruedPremium(INDEX_COUPON) * INTRINSIC_DATA.getIndexFactor() * NOTIONAL; // indexFactor = (initialIndexSize - numDefaults) / initialIndexSize /* * Using credit curves for constituent single name CDSs. * The curves are adjusted by using only the target CDX. */ IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(indexPUF[pos], CDX[pos], INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA); double cleanPV = INDEX_CAL.indexPV(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves) * NOTIONAL; // should be consistent with 1 - PRICES[pos] double dirtyPV = INDEX_CAL.indexPV(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves, CdsPriceType.DIRTY) * NOTIONAL; double expectedLoss = INDEX_CAL.expectedDefaultSettlementValue(targentCDX.getProtectionEnd(), adjCurves) * NOTIONAL; double cleanRPV01 = INDEX_CAL.indexAnnuity(targentCDX, YIELD_CURVE, adjCurves); double dirtyRPV01 = INDEX_CAL.indexAnnuity(targentCDX, YIELD_CURVE, adjCurves, CdsPriceType.DIRTY); double durationWeightedAverageSpread = INDEX_CAL.intrinsicIndexSpread(targentCDX, YIELD_CURVE, adjCurves) * TEN_THOUSAND; double parallelIR01 = INDEX_CAL.parallelIR01(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves) * NOTIONAL; double[] jumpToDefault = INDEX_CAL.jumpToDefault(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves); for (int i = 0; i < jumpToDefault.length; ++i) { jumpToDefault[i] *= NOTIONAL; } double[] recovery01 = INDEX_CAL.recovery01(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves); for (int i = 0; i < recovery01.length; ++i) { recovery01[i] *= NOTIONAL; } double[] expectedJTD = new double[] {757972.9229096733, 613407.5742229455, 519963.7201407489, 681248.9859724859, 665564.1215264505, 600197.0506581058, 678626.038385348, 771307.5727142713, 789968.4949320748, 741736.0689991037, 810820.0698326067, 687026.4423298091, 695315.7361986884, 757817.9137606273, 419051.39427547425, 719294.4243731356, 711655.1207634398, 732915.6111622711, 734568.6406535608, 757496.9108345185, 707804.9176387836, 701364.737981731, 824250.6482166173, 665338.3844614043, 776946.4545707188, 748748.4796897258, 731779.3787772739, 702216.3857558061, 717467.7823885867, 736059.7499518575, 733687.011400837, 692342.0376320765, 806278.6281988872, 537502.6873132278, 764299.6260590593, 732476.8228168603, 755319.7881197047, 843860.5479205761, 788630.4492388433, 805900.7859682858, 886944.1698892581, 943638.1483364917, 281413.65415537305, 660476.167823917, 700842.7389298718, 771173.929821256, 757547.5145823065, 749129.9547583151, 749490.341394089, 747754.0312154669, 776857.0510204895, 849107.8957624634, 371446.9272003154, 790859.7390816918, 737810.4013803691, 672256.8721332103, 753704.2829844486, 768582.5263705641, 795804.3191480329, 596481.9017618913, 815216.5436775064, 780842.3227550203, 776589.5246446186, 794086.6520601802, 676158.8477391114, 784148.3304128735, 771830.0928915134, 693931.9420187991, 769722.5688008837, 797520.1257725758, 789675.3769607927, 712880.7816824848, 384437.3161017737, 743865.1931337111, 945747.6873531832, 789797.9051085879, 780600.774444097, 728107.1389194163, 551456.6525330377, 706122.1371914946, 305145.4072010005, 758310.8202605139, 547575.7063821417, 696209.27662954, 649199.9336751422, 701969.3335439126, 339024.479854637, 809879.4198930457, 732279.6225383902, 273562.1475311735, 795331.152195739, 880147.7623671715, 756824.6807054353, 790385.7931144186, 675097.4303631181, 648940.0361009205, 690965.5381462189}; double[] expectedRec01 = new double[] {-139561.82865562924, -197881.1378921079, -432314.23350178037, -246429.40281746117, -132896.5670220749, -340783.49697955005, -247964.97919276363, -122193.62540432697, -161538.55935490542, -200571.65571915146, -81049.6672038405, -231217.74067230397, -315724.85933460743, -449729.8426648982, -805581.4498512819, -151437.74682177624, -197346.35239622547, -134829.2129359926, -169485.91769011554, -204268.68273479727, -204913.68430410823, -212234.2908225003, -104056.83188474829, -258305.99823900295, -115059.64125910898, -150588.5176968103, -172980.04072587506, -211414.09655336005, -193050.16571984955, -171405.21102143772, -301510.1910642546, -224349.96067567408, -191717.42651281646, -414193.7067940033, -131367.0746051117, -172744.98294699937, -142256.73983597555, -28490.703465933566, -100610.58337397527, -79978.00422443911, -226927.45065583798, -164429.32308164772, -716950.861667023, -272426.862855944, -214529.35970717695, -122574.93804834444, -139676.55892938052, -150181.33912905553, -160253.73504382258, -156643.12734773968, -107877.18850926857, -158980.8454276644, -541445.4343255089, -296458.6505670699, -202259.79047115523, -212881.5446294847, -179365.01655038638, -125941.76597175426, -93485.63393862064, -434600.13577389147, -92546.98003151374, -110433.73012572086, -115756.3137527123, -93008.19303185042, -243994.76264500545, -196361.46564759, -121623.93371234722, -221443.7498806151, -125120.92904143682, -176681.64065060052, -119782.9823218729, -210523.7826543962, -722602.0876610717, -157408.23718205903, -142430.71544873226, -98597.39368213696, -110507.86798638091, -178004.51707988168, -249476.72444247553, -205874.67398342708, -664859.7217981115, -157615.46347486821, -265492.7910438084, -217340.23562921325, -296729.42472738033, -211667.08401844872, -683952.5255339593, -170521.82833434394, -173828.313281286, -1015043.8482598866, -90847.55693602911, -152452.75305668917, -145785.09137900846, -98052.86523129685, -246226.91785943933, -291567.1650092818, -225824.67730722172}; assertEquals("accrual days", 56, accrualDays); assertEqualsRelativeTol("accrued permium", 777777.7777777779, accruedPremium, TOL); assertEqualsRelativeTol("clean PV", -7620000.000000005, cleanPV, TOL); assertEqualsRelativeTol("dirty PV", -8397777.777777774, dirtyPV, TOL); assertEqualsRelativeTol("expected loss", 1.4667475235799424E7, expectedLoss, TOL); assertEqualsRelativeTol("clean RPV01", 4.371798785409769, cleanRPV01, TOL); assertEqualsRelativeTol("dirty RPV01", 4.527354340965325, dirtyRPV01, TOL); assertEqualsRelativeTol("duration weighted average spread (BPS)", 325.70103579719597, durationWeightedAverageSpread, TOL); assertEqualsRelativeTol("parallel IR01", 1289.5612449798089, parallelIR01, TOL); assertDoubleArray("jump to default", expectedJTD, jumpToDefault, TOL); assertDoubleArray("recovery01", expectedRec01, recovery01, TOL); /* * Using credit curves for constituent single name CDSs * The curves are adjusted by using all of the market CDXs. */ final IntrinsicIndexDataBundle adjCurvesAll = PSA.adjustCurves(indexPUF, CDX, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA); double cleanPVAll = INDEX_CAL.indexPV(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurvesAll) * NOTIONAL; // should be consistent with 1 - PRICES[pos] double dirtyPVAll = INDEX_CAL.indexPV(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurvesAll, CdsPriceType.DIRTY) * NOTIONAL; double expectedLossAll = INDEX_CAL.expectedDefaultSettlementValue(targentCDX.getProtectionEnd(), adjCurvesAll) * NOTIONAL; double cleanRPV01All = INDEX_CAL.indexAnnuity(targentCDX, YIELD_CURVE, adjCurvesAll); double dirtyRPV01All = INDEX_CAL.indexAnnuity(targentCDX, YIELD_CURVE, adjCurvesAll, CdsPriceType.DIRTY); double durationWeightedAverageSpreadAll = INDEX_CAL.intrinsicIndexSpread(targentCDX, YIELD_CURVE, adjCurvesAll) * TEN_THOUSAND; double parallelIR01All = INDEX_CAL.parallelIR01(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurvesAll) * NOTIONAL; double[] jumpToDefaultAll = INDEX_CAL.jumpToDefault(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurvesAll); for (int i = 0; i < jumpToDefaultAll.length; ++i) { jumpToDefaultAll[i] *= NOTIONAL; } double[] recovery01All = INDEX_CAL.recovery01(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurvesAll); for (int i = 0; i < recovery01All.length; ++i) { recovery01All[i] *= NOTIONAL; } double[] expectedJTDAll = new double[] {757842.6331649405, 613307.6776069296, 520029.8929821387, 681078.9534097714, 665535.4719916269, 600087.5349365007, 678430.82157516, 771195.1986987122, 789815.9366526266, 741554.630468345, 810729.611188683, 686825.8828343773, 695053.0416039937, 758090.1178084902, 420598.3229713065, 719227.9394092695, 711505.1840510527, 732890.5389988142, 734423.5773103269, 757337.0820119934, 707599.4862206457, 701194.0159035764, 824146.0957861985, 665137.2320836138, 776835.1366462249, 748634.9577481012, 731615.5145576429, 702039.5540376178, 717266.1757873945, 735906.9746276754, 733507.745247165, 692151.1756613479, 806094.6357511363, 537475.5964409444, 764169.4220957252, 732308.0731964909, 755211.781495045, 843831.6840227004, 788520.0577023963, 805818.5268800054, 886813.4011945202, 943532.8941571743, 283058.66990463017, 660256.5543350514, 700681.20892525, 771056.8858757374, 757429.54166973, 749011.386452269, 749337.6130691795, 747597.9847290562, 776749.5943831704, 848957.8856691518, 372155.065930306, 790675.056760928, 737627.5270833452, 672144.8287519763, 753542.4904313916, 768455.8752541174, 795700.7289915706, 596441.2904916357, 815117.011174969, 780720.829583597, 776469.7068487643, 793991.4213964037, 675969.3096776957, 784036.9991999706, 771713.3898966095, 693759.5675511372, 769587.1821048936, 797360.6474086319, 789553.8581577024, 712782.0571633332, 385809.05482176336, 743724.3547723669, 945611.895759126, 789706.2027511207, 780485.5218823397, 727947.8564130614, 551306.519029223, 705955.8687001015, 306279.18573115405, 758179.5210074745, 547467.6715411451, 696073.8858002889, 649020.1728689888, 701797.2449162701, 340110.34548356076, 809743.2009079301, 732130.4244943967, 274820.1478672703, 795251.7261776953, 880018.0180365249, 756691.6530164307, 790277.2557351914, 674895.6677342252, 648699.7535307849, 690767.2061693986}; double[] expectedRec01All = new double[] {-139887.31363230804, -198311.04924439717, -433047.3660519826, -246956.81972983447, -133190.60745822816, -341441.83436994, -248499.94760151798, -122480.9384152883, -161910.68773983844, -201021.8763133255, -81247.61347004696, -231724.8625275148, -316375.5826441032, -450453.5274170195, -806107.3103185329, -151775.1706131768, -197783.89197669332, -135125.7824590783, -169872.03369019576, -204721.4506940941, -205375.54192548816, -212703.33025622915, -104305.74247950326, -258858.0751230893, -115332.28495826322, -150932.48081035985, -173374.9984498724, -211882.7087482122, -193490.27755215362, -171796.2826501761, -302125.25233319344, -224843.92018546446, -192151.40621680842, -414926.1845689914, -131676.43226874794, -173141.5813451517, -142583.46499727707, -28561.578246519104, -100853.27604969585, -80171.67268403518, -227414.44011780957, -164799.0828676032, -717553.3879455855, -273005.7085138976, -215000.30489616547, -122863.64286010184, -139999.8419646928, -150525.34019034752, -160623.6616064251, -157006.52488155736, -108133.97794489434, -159347.75019215606, -542171.5435839767, -297066.829632043, -202713.2880599622, -213340.7165465065, -179771.96060215813, -126238.82028127078, -93712.29745817007, -435354.9698933417, -92770.7319998512, -110698.58323401344, -116031.79605223004, -93231.99468124051, -244520.28436604247, -196790.29455107747, -121910.79247700267, -221929.16770117678, -125418.81941270511, -177083.4862533655, -120067.18407260436, -210976.47833028637, -723232.2695170578, -157770.2231849431, -142762.84388024863, -98832.65400500425, -110771.465673976, -178409.03238179607, -250005.64453195556, -206331.21164342554, -665545.2476687637, -157976.1549564491, -266040.0464683889, -217811.93003253266, -297338.00929545454, -212135.37760011316, -684636.7563914763, -170907.57243424605, -174223.26552720048, -1015047.628733981, -91064.12512676444, -152803.5773637681, -146123.19211008097, -98289.81046908897, -246759.98484680743, -292178.74657336395, -226321.8586278077}; assertEqualsRelativeTol("clean PV", -7620000.000000005, cleanPVAll, TOL); assertEqualsRelativeTol("dirty PV", -8397777.777777774, dirtyPVAll, TOL); assertEqualsRelativeTol("expected loss", 1.4697623484483391E7, expectedLossAll, TOL); assertEqualsRelativeTol("clean RPV01", 4.376857663631577, cleanRPV01All, TOL); assertEqualsRelativeTol("dirty RPV01", 4.532413219187131, dirtyRPV01All, TOL); assertEqualsRelativeTol("duration weighted average spread (BPS)", 325.90249476658744, durationWeightedAverageSpreadAll, TOL); assertEqualsRelativeTol("parallel IR01", 1248.341194454161, parallelIR01All, TOL); assertDoubleArray("jump to default", expectedJTDAll, jumpToDefaultAll, TOL); assertDoubleArray("recovery01", expectedRec01All, recovery01All, TOL); /* * using index credit curve, i.e., a single credit curve * Some sensitivities are irrelevant in this calculation method */ IsdaCompliantCreditCurve indexCurve = (new FastCreditCurveBuilder(OG_FIX)).calibrateCreditCurve(targentCDX, INDEX_COUPON, YIELD_CURVE, indexPUF[pos]); // single node index curve double cleanPriceIndexCurve = PRICER_OG_FIX.pv(targentCDX, YIELD_CURVE, indexCurve, INDEX_COUPON) * INTRINSIC_DATA.getIndexFactor() * NOTIONAL; // should be consistent with 1 - PRICES[pos] double dirtyPriceIndexCurve = PRICER_OG_FIX.pv(targentCDX, YIELD_CURVE, indexCurve, INDEX_COUPON, CdsPriceType.DIRTY) * INTRINSIC_DATA.getIndexFactor() * NOTIONAL; double cleanRPV01IndexCurve = PRICER_OG_FIX.annuity(targentCDX, YIELD_CURVE, indexCurve) * INTRINSIC_DATA.getIndexFactor(); double dirtyRPV01IndexCurve = PRICER_OG_FIX.annuity(targentCDX, YIELD_CURVE, indexCurve, CdsPriceType.DIRTY) * INTRINSIC_DATA.getIndexFactor(); double spreadIndexCurve = PRICER_OG_FIX.parSpread(targentCDX, YIELD_CURVE, indexCurve) * TEN_THOUSAND; double IR01IndexCurve = IR_CAL.parallelIR01(targentCDX, INDEX_COUPON, indexCurve, YIELD_CURVE) * INTRINSIC_DATA.getIndexFactor() * NOTIONAL; assertEqualsRelativeTol("clean PV", -7619999.999999996, cleanPriceIndexCurve, TOL); assertEqualsRelativeTol("dirty PV", -8397777.777777776, dirtyPriceIndexCurve, TOL); assertEqualsRelativeTol("clean RPV01", 4.299718387823459, cleanRPV01IndexCurve, TOL); assertEqualsRelativeTol("dirty RPV01", 4.455273943379015, dirtyRPV01IndexCurve, TOL); assertEqualsRelativeTol("duration weighted average spread (BPS)", 322.77909126375874, spreadIndexCurve, TOL); assertEqualsRelativeTol("parallel IR01", 2020.931376187085, IR01IndexCurve, TOL); } /** * one tenor is missing */ @Test public void missingMarketDataTest() { int pos = 2; // target CDX is 7Y, which is missing in the market data CdsAnalytic targentCDX = CDX[pos]; int nPillars = 3; double[] indexPUF = new double[nPillars]; CdsAnalytic[] marketCDX = new CdsAnalytic[nPillars]; int j = 0; for (int i = 0; i < CDX.length; i++) { if (i != pos) { indexPUF[j] = PILLAR_PUF[i].getPointsUpFront(); marketCDX[j] = CDX[i]; ++j; } } int accrualDays = targentCDX.getAccuredDays(); double accruedPremium = targentCDX.getAccruedPremium(INDEX_COUPON) * NOTIONAL * INTRINSIC_DATA.getIndexFactor(); /* * Using credit curves for constituent single name CDSs. * The curves are adjusted by using only the target CDX. */ IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(indexPUF, marketCDX, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA); double cleanPV = INDEX_CAL.indexPV(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves) * NOTIONAL; double dirtyPV = INDEX_CAL.indexPV(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves, CdsPriceType.DIRTY) * NOTIONAL; double expectedLoss = INDEX_CAL.expectedDefaultSettlementValue(targentCDX.getProtectionEnd(), adjCurves) * NOTIONAL; double cleanRPV01 = INDEX_CAL.indexAnnuity(targentCDX, YIELD_CURVE, adjCurves); double dirtyRPV01 = INDEX_CAL.indexAnnuity(targentCDX, YIELD_CURVE, adjCurves, CdsPriceType.DIRTY); double durationWeightedAverageSpread = INDEX_CAL.intrinsicIndexSpread(targentCDX, YIELD_CURVE, adjCurves) * TEN_THOUSAND; double parallelIR01 = INDEX_CAL.parallelIR01(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves) * NOTIONAL; double[] jumpToDefault = INDEX_CAL.jumpToDefault(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves); for (int i = 0; i < jumpToDefault.length; ++i) { jumpToDefault[i] *= NOTIONAL; } double[] recovery01 = INDEX_CAL.recovery01(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves); for (int i = 0; i < recovery01.length; ++i) { recovery01[i] *= NOTIONAL; } double[] expectedJTD = new double[] {754012.8991891549, 592354.0638368389, 456785.98546560504, 629519.1514578052, 672002.1836060358, 527737.4087053173, 627556.1360439743, 763604.1488808872, 765670.8270869099, 688270.6227659615, 826281.4035542335, 645414.2218168428, 623809.9991252772, 681870.6291503115, 373654.185842531, 711219.7814591008, 693404.7993585062, 747259.1168794596, 703481.9822860569, 712321.2940291865, 690563.3580900511, 658426.4398637532, 838573.0000299555, 595369.8794907633, 774037.4978679622, 722970.2685412527, 706574.4833422982, 655888.9300860014, 656456.6058675961, 709705.0359083242, 673026.067350552, 629873.1165401622, 779923.684651468, 475521.501829702, 746205.3526558997, 682814.9981442004, 734492.6545619091, 906260.9363809755, 788630.2791107272, 828563.113122252, 841639.4432801855, 935169.9424745009, 233389.77714174677, 597186.5633333499, 646949.9416737243, 772851.2105219007, 751779.6471873032, 718914.9347554374, 722601.5971028894, 735404.5742181429, 770930.7660701411, 822248.5039208268, 308946.7108475995, 736176.3160012246, 682676.8167910068, 631611.4086967409, 705911.3438685283, 753887.8809895903, 818237.124685416, 526315.8472187843, 828628.6823899371, 778444.5483977429, 767545.0704159061, 815411.9485270171, 624380.0781020486, 769414.3769903053, 760171.6807005968, 669889.2527526512, 774059.4052517113, 772795.24088319, 776056.4681888156, 677341.99858797, 345070.3225420915, 719464.9566560748, 938850.1153448626, 785161.7623671364, 775481.9170259774, 695685.0839617262, 482880.0530926017, 672324.4569288351, 245043.77044580987, 715798.6418183544, 499069.2076386398, 645505.2127057393, 584059.4134907085, 659763.4755100938, 284315.1060063416, 782195.961484992, 716558.5446281082, 273047.47704247356, 822280.5279180607, 880185.6908089129, 734832.000599873, 794162.7529352009, 622868.6830489627, 579058.7863440582, 655171.2833441485}; double[] expectedRec01 = new double[] {-251304.71585144178, -325203.8789943683, -586648.2875262748, -408272.41245542245, -231976.63325278417, -518427.2255781659, -408616.4390248018, -241279.61495003558, -297623.20137628133, -370556.1015631285, -174530.08252026484, -381242.83384280273, -494815.845702374, -619322.5489553664, -892482.0522617545, -267385.6926369762, -321038.81763184577, -223164.6416149089, -313651.8278537996, -362773.3462002046, -325771.75809965876, -366605.95378705044, -195975.81736100177, -442883.3332889076, -228662.95173065324, -290224.9412166137, -308905.02955725574, -370392.4924202426, -373938.7747159569, -308938.7385647126, -468159.1556456556, -403199.907467821, -326605.10170459584, -568745.8221210544, -263028.8006760732, -341328.08608307334, -276316.07932110055, -66036.79908427017, -212093.35765289754, -163935.75798797523, -382816.2282060414, -279150.0000764281, -819110.6219412663, -446205.8448061626, -383392.2773265295, -228985.1281642836, -253879.43793537148, -295829.3361200753, -299935.6077249625, -277248.3937975655, -226474.29371345937, -298828.076514057, -682568.5146048665, -455839.399224248, -374278.60690741404, -364422.11425286485, -344623.9531457179, -253888.2137283227, -175865.97374930256, -597567.4344905142, -187204.02342164938, -223912.40040134918, -237470.36147049864, -177029.48677557425, -406185.56286955916, -315368.0273917367, -246089.84691062535, -349345.485526801, -227535.51142672836, -311389.2644004584, -247077.01521361168, -355567.3399960001, -811922.6934479174, -294268.8525530383, -257757.4754723839, -216626.28059632398, -227652.85866954856, -322817.03363118856, -433442.6383821743, -348901.89762675535, -788279.2728966378, -318697.6101580127, -420792.0724158093, -381680.51683830837, -469962.64044059505, -365069.2846436973, -797769.6838405222, -310097.59439372714, -296795.64640312176, -1019053.5468024287, -167713.5129068544, -257085.80124846968, -280876.22132814466, -204914.38673509783, -408595.40992420504, -471591.03007073124, -368902.8331015535}; assertEquals("accrual days", 56, accrualDays); assertEqualsRelativeTol("accrued permium", 777777.7777777779, accruedPremium, TOL); assertEqualsRelativeTol("clean PV", -5914154.704818112, cleanPV, TOL); assertEqualsRelativeTol("dirty PV", -6691932.482595883, dirtyPV, TOL); assertEqualsRelativeTol("expected loss", 2.3610506554899197E7, expectedLoss, TOL); assertEqualsRelativeTol("clean RPV01", 5.63546372361573, cleanRPV01, TOL); assertEqualsRelativeTol("dirty RPV01", 5.791019279171284, dirtyRPV01, TOL); assertEqualsRelativeTol("duration weighted average spread (BPS)", 395.0546930142677, durationWeightedAverageSpread, TOL); assertEqualsRelativeTol("parallel IR01", 376.9183762297734, parallelIR01, TOL); assertDoubleArray("jump to default", expectedJTD, jumpToDefault, TOL); assertDoubleArray("recovery01", expectedRec01, recovery01, TOL); } /** * several single names defaulted */ @Test public void defaultedSingleNamesTest() { int pos = 1; // target CDX is 5Y CdsAnalytic targentCDX = CDX[pos]; int n = PILLAR_PUF.length; double[] indexPUF = new double[n]; for (int i = 0; i < n; i++) { indexPUF[i] = PILLAR_PUF[i].getPointsUpFront(); } int[] defaultedNames = new int[] {2, 15, 37, 51}; IntrinsicIndexDataBundle dataDefaulted = INTRINSIC_DATA.withDefault(defaultedNames); int accrualDays = targentCDX.getAccuredDays(); double accruedPremium = targentCDX.getAccruedPremium(INDEX_COUPON) * NOTIONAL * dataDefaulted.getIndexFactor(); /* * Using credit curves for constituent single name CDSs. * The curves are adjusted by using only the target CDX. */ IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(indexPUF[pos], CDX[pos], INDEX_COUPON, YIELD_CURVE, dataDefaulted); double cleanPV = INDEX_CAL.indexPV(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves) * NOTIONAL; double dirtyPV = INDEX_CAL.indexPV(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves, CdsPriceType.DIRTY) * NOTIONAL; // should be consistent with 1 - PRICES[pos] double expectedLoss = INDEX_CAL.expectedDefaultSettlementValue(targentCDX.getProtectionEnd(), adjCurves) * NOTIONAL; double cleanRPV01 = INDEX_CAL.indexAnnuity(targentCDX, YIELD_CURVE, adjCurves); double dirtyRPV01 = INDEX_CAL.indexAnnuity(targentCDX, YIELD_CURVE, adjCurves, CdsPriceType.DIRTY); double durationWeightedAverageSpread = INDEX_CAL.intrinsicIndexSpread(targentCDX, YIELD_CURVE, adjCurves) * TEN_THOUSAND; double parallelIR01 = INDEX_CAL.parallelIR01(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves) * NOTIONAL; double[] jumpToDefault = INDEX_CAL.jumpToDefault(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves); for (int i = 0; i < jumpToDefault.length; ++i) { jumpToDefault[i] *= NOTIONAL; } double[] recovery01 = INDEX_CAL.recovery01(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurves); for (int i = 0; i < recovery01.length; ++i) { recovery01[i] *= NOTIONAL; } double[] expectedJTD = new double[] {759068.3028394917, 614933.2886698751, 0.0, 683076.695174131, 666637.7038623437, 602590.6449860543, 680455.6350002288, 772276.9363898552, 791220.3689168617, 743254.9810770862, 811473.5454409182, 688745.281871273, 697533.5791405468, 760798.0654693833, 422378.9630684295, 0.0, 713169.2553326349, 734007.6733417767, 735879.1289980127, 759047.2640362546, 709348.7191050962, 702965.0765946861, 825081.6511488099, 667232.4778053069, 777861.5117464178, 749930.4845024608, 733114.7457816179, 703809.0925293413, 718927.5842627218, 737381.5406460876, 735845.7937164144, 694017.1301650583, 807738.5295482178, 540269.8424984136, 765331.1977569524, 733805.2761117393, 756440.7430030463, 0.0, 789431.3695729998, 806547.9077626362, 888654.9777140574, 944924.1648868355, 285042.30639033875, 662451.5390409094, 702463.1206378399, 772144.4055393643, 758647.3147368291, 750308.3087985716, 750731.3389948363, 748967.3075751726, 777718.0128420595, 0.0, 374801.4746663491, 792990.2876538871, 739340.929543854, 673880.1434860098, 755083.1668506702, 769577.6460849819, 796551.8634122441, 599333.597951982, 815958.8156495387, 781719.3501962055, 777507.8161667096, 794834.1034732872, 677967.7248448462, 785660.8711692409, 772794.3438765367, 695595.1655596746, 770706.3085468425, 798879.6325583884, 790623.4782224521, 714491.5070688166, 388001.31123505207, 745091.3786599116, 946863.813754441, 790587.7311345327, 781480.334049672, 729475.127873534, 553306.7230663239, 707682.3197706994, 308719.3972985043, 759539.989797125, 549540.026246675, 697855.3227330166, 651330.3742845649, 703564.3136702756, 342579.36948359717, 811202.1401152938, 733619.8367745923, 274377.1316070438, 796066.5967420674, 881337.0567764034, 757965.4185730724, 791169.5951852546, 676913.420759841, 651024.39789219, 692652.2089233205}; double[] expectedRec01 = new double[] {-138149.57548173715, -195947.6606000404, 0.0, -244097.81927352256, -131545.83603968847, -337772.9838422089, -245621.7969965156, -120944.58923027178, -159925.14300849827, -198616.35209704956, -80202.04887747848, -229008.2300340679, -312884.111402761, -446108.902912447, -801897.114757639, 0.0, -195417.74501631927, -133460.2517421897, -167801.1754219834, -202281.72000540156, -202921.73993384934, -210180.53970277583, -102982.28219564802, -255882.29176802386, -113878.770770024, -149074.3312105156, -171264.14649748185, -209367.30260189186, -191159.56727783915, -169703.46298172715, -298766.73797783174, -222196.31600997454, -189837.85736557838, -410752.4288016298, -130031.51260393477, -171031.43960547703, -140819.30802505297, 0.0, -99569.70678281639, -79141.06317141505, -224751.77205099774, -162789.4534337523, -712891.3604025444, -269896.18822910526, -212456.219020077, -121322.31691133727, -138263.1272555478, -148670.90050827194, -158651.99322127653, -155074.13898805378, -106765.52854773018, 0.0, -537481.5459520443, -293750.99132652406, -200290.1358918631, -210821.50016324708, -177592.80961845216, -124657.2175395878, -92514.62566640385, -431056.3574572183, -91585.17895701976, -109297.43874996468, -114568.80691874986, -92041.81152625445, -241682.4919747127, -194441.08950248873, -120380.33573889127, -219313.55217970957, -123844.32964480988, -174933.11167232387, -118556.99082687228, -208483.32142448518, -718565.6515061635, -155832.04031048962, -140991.85357942086, -97576.1200181287, -109370.80772858317, -176244.37022953568, -247121.04351310863, -203874.1627767233, -660745.7839831264, -156037.34057578622, -263012.5436684385, -215243.45802745805, -294020.0348680447, -209618.15267268007, -679856.293991873, -168827.63829287738, -172105.00107156037, -1014316.9351984165, -89902.42328586837, -150921.72909218515, -144315.23664537718, -97036.9515497064, -243897.55289223307, -288895.9363694398, -223658.82113488184}; assertEquals("accrual days", 56, accrualDays); assertEqualsRelativeTol("accrued permium", 745704.467353952, accruedPremium, TOL); assertEqualsRelativeTol("clean PV", -7305773.195876298, cleanPV, TOL); assertEqualsRelativeTol("dirty PV", -8051477.663230252, dirtyPV, TOL); assertEqualsRelativeTol("expected loss", 1.6613694931739897E7, expectedLoss, TOL); assertEqualsRelativeTol("clean RPV01", 4.191372219024439, cleanRPV01, TOL); assertEqualsRelativeTol("dirty RPV01", 4.34051311249523, dirtyRPV01, TOL); assertEqualsRelativeTol("duration weighted average spread (BPS)", 325.6949558735027, durationWeightedAverageSpread, TOL); assertEqualsRelativeTol("parallel IR01", 1248.160240097107, parallelIR01, TOL); assertDoubleArray("jump to default", expectedJTD, jumpToDefault, TOL); assertDoubleArray("recovery01", expectedRec01, recovery01, TOL); /* * Using credit curves for constituent single name CDSs * The curves are adjusted by using all of the market CDXs. */ final IntrinsicIndexDataBundle adjCurvesAll = PSA.adjustCurves(indexPUF, CDX, INDEX_COUPON, YIELD_CURVE, dataDefaulted); double cleanPVAll = INDEX_CAL.indexPV(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurvesAll) * NOTIONAL; double dirtyPVAll = INDEX_CAL.indexPV(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurvesAll, CdsPriceType.DIRTY) * NOTIONAL; // should be consistent with 1 - PRICES[pos] double expectedLossAll = INDEX_CAL.expectedDefaultSettlementValue(targentCDX.getProtectionEnd(), adjCurvesAll) * NOTIONAL; double cleanRPV01All = INDEX_CAL.indexAnnuity(targentCDX, YIELD_CURVE, adjCurvesAll); double dirtyRPV01All = INDEX_CAL.indexAnnuity(targentCDX, YIELD_CURVE, adjCurvesAll, CdsPriceType.DIRTY); double durationWeightedAverageSpreadAll = INDEX_CAL.intrinsicIndexSpread(targentCDX, YIELD_CURVE, adjCurvesAll) * TEN_THOUSAND; double parallelIR01All = INDEX_CAL.parallelIR01(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurvesAll) * NOTIONAL; double[] jumpToDefaultAll = INDEX_CAL.jumpToDefault(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurvesAll); for (int i = 0; i < jumpToDefaultAll.length; ++i) { jumpToDefaultAll[i] *= NOTIONAL; } double[] recovery01All = INDEX_CAL.recovery01(targentCDX, INDEX_COUPON, YIELD_CURVE, adjCurvesAll); for (int i = 0; i < recovery01All.length; ++i) { recovery01All[i] *= NOTIONAL; } double[] expectedJTDAll = new double[] {758930.299866654, 614826.7543668385, 0.0, 682895.7409981902, 666606.7828017593, 602472.2397500504, 680248.1286537352, 772157.9393281924, 791058.7231513134, 743062.5317421725, 811377.918158478, 688532.3557926381, 697254.0880434295, 761080.8104187348, 424008.15641905257, 0.0, 713010.0158238821, 733980.5075954038, 735725.2994537064, 758877.5350746658, 709130.9588061813, 702783.7885457174, 824971.0269802089, 667018.599744451, 777743.682286894, 749810.0420742312, 732941.0880933432, 703621.3798924928, 718713.9803188451, 737219.572715269, 735654.414718989, 693814.4946703071, 807543.4863189446, 540237.2782062003, 765193.3554001533, 733626.4729054376, 756326.1797716236, 0.0, 789314.6259453326, 806460.9196238826, 888515.6164291892, 944812.300566099, 286778.4289443176, 662218.024036959, 702291.4911661368, 772020.4904986884, 758522.279722958, 750182.5554369189, 750569.5261733973, 748802.036056132, 777604.3043023307, 0.0, 375543.86149918643, 792793.2705921306, 739146.9496728585, 673760.6587560963, 754911.6281532559, 769443.589410599, 796442.329512154, 599286.4517988993, 815853.5594235577, 781590.845352112, 777381.0335806673, 794733.372269709, 677766.2632189555, 785542.3106425056, 772670.7939369977, 695412.0341414144, 770563.0606063906, 798710.5555877057, 790494.8729336872, 714386.0777386478, 389446.07831872615, 744942.1106235008, 946719.9926263194, 790490.6750411455, 781358.3987650375, 729306.2437860989, 553146.6982203538, 707505.7927331995, 309911.59763660026, 759400.7651039191, 549424.1699893387, 697711.191175783, 651138.5314337813, 703381.5941323243, 343720.7530463426, 811057.6169796381, 733461.6125782285, 275745.56748707045, 795982.5162258771, 881199.5157565416, 757824.4799672131, 791054.8203866193, 676699.0433333014, 650768.8589161123, 692441.6904023722}; double[] expectedRec01All = new double[] {-138493.33344501475, -196402.1204427429, 0.0, -244655.72912760405, -131856.40099272016, -338470.5595536771, -246187.69165036365, -121247.96399271929, -160318.27855148344, -199092.2515566522, -80410.9439275213, -229544.52218031883, -313573.19211394765, -446877.686433676, -802465.4011235997, 0.0, -195880.23636656074, -133773.49873810343, -168209.14057899002, -202760.3528240727, -203409.95903249405, -210676.4280214344, -103245.04168247599, -256466.38045809127, -114166.6267475613, -149437.67249419537, -171681.46680167055, -209862.72949444086, -191624.7159472514, -170116.6720414003, -299417.9540451243, -222718.63734165512, -190296.5263422141, -411529.7389869271, -130358.19849995451, -171450.48715203497, -141164.40093737486, 0.0, -99825.88719370234, -79345.44519118138, -225266.78177346976, -163180.12719695616, -713537.8808726899, -270508.7262853777, -212954.1462548192, -121627.16124893786, -138604.56690672343, -149034.2772725999, -159042.79483750052, -155458.0211362639, -107036.61997799102, 0.0, -538254.9709705027, -294394.86062458076, -200769.5109064213, -211306.99560604902, -178022.83693937506, -124970.88889342026, -92753.86385968578, -431857.7257054895, -91821.34298993986, -109577.04596002075, -114859.65895969719, -92278.03186481408, -242238.36535867339, -194894.38894250017, -120683.22637602066, -219826.82895797506, -124158.87399956447, -175357.73497975548, -118857.06518339552, -208961.95928662075, -719242.004528812, -156214.44369808494, -141342.63982741628, -97824.45603734197, -109649.09346667683, -176671.82279488846, -247680.5846075673, -204356.78917858328, -661479.3613251017, -156418.38275965303, -263591.6592672253, -215742.21388910932, -294664.3388335037, -210113.24739366397, -680589.042199156, -169235.22279656064, -172522.3276036287, -1014322.5661839879, -90131.00824135669, -151292.3194460656, -144672.35019431746, -97287.05650471071, -244461.41670763085, -289543.31121164665, -224184.55758060206}; assertEqualsRelativeTol("clean PV", -7305773.195876298, cleanPVAll, TOL); assertEqualsRelativeTol("dirty PV", -8051477.6632302385, dirtyPVAll, TOL); assertEqualsRelativeTol("expected loss", 1.664444475101973E7, expectedLossAll, TOL); assertEqualsRelativeTol("clean RPV01", 4.196531715092685, cleanRPV01All, TOL); assertEqualsRelativeTol("dirty RPV01", 4.345672608563473, dirtyRPV01All, TOL); assertEqualsRelativeTol("duration weighted average spread (BPS)", 325.9092581237661, durationWeightedAverageSpreadAll, TOL); assertEqualsRelativeTol("parallel IR01", 1205.9793964708242, parallelIR01All, TOL); assertDoubleArray("jump to default", expectedJTDAll, jumpToDefaultAll, TOL); assertDoubleArray("recovery01", expectedRec01All, recovery01All, TOL); /* * using index credit curve, i.e., a single credit curve * Some sensitivities are irrelevant in this calculation method */ IsdaCompliantCreditCurve indexCurve = (new FastCreditCurveBuilder(OG_FIX)).calibrateCreditCurve(targentCDX, INDEX_COUPON, YIELD_CURVE, indexPUF[pos]); // single node index curve, indexFactors cancel out double cleanPriceIndexCurve = PRICER_OG_FIX.pv(targentCDX, YIELD_CURVE, indexCurve, INDEX_COUPON) * dataDefaulted.getIndexFactor() * NOTIONAL; double dirtyPriceIndexCurve = PRICER_OG_FIX.pv(targentCDX, YIELD_CURVE, indexCurve, INDEX_COUPON, CdsPriceType.DIRTY) * dataDefaulted.getIndexFactor() * NOTIONAL; double cleanRPV01IndexCurve = PRICER_OG_FIX.annuity(targentCDX, YIELD_CURVE, indexCurve) * dataDefaulted.getIndexFactor(); double dirtyRPV01IndexCurve = PRICER_OG_FIX.annuity(targentCDX, YIELD_CURVE, indexCurve, CdsPriceType.DIRTY) * dataDefaulted.getIndexFactor(); double spreadIndexCurve = PRICER_OG_FIX.parSpread(targentCDX, YIELD_CURVE, indexCurve) * TEN_THOUSAND; double IR01IndexCurve = IR_CAL.parallelIR01(targentCDX, INDEX_COUPON, indexCurve, YIELD_CURVE) * dataDefaulted.getIndexFactor() * NOTIONAL; assertEqualsRelativeTol("clean PV", -7305773.195876285, cleanPriceIndexCurve, TOL); assertEqualsRelativeTol("dirty PV", -8051477.663230239, dirtyPriceIndexCurve, TOL); assertEqualsRelativeTol("clean RPV01", 4.122410413067852, cleanRPV01IndexCurve, TOL); assertEqualsRelativeTol("dirty RPV01", 4.271551306538643, dirtyRPV01IndexCurve, TOL); assertEqualsRelativeTol("duration weighted average spread (BPS)", 322.77909126375874, spreadIndexCurve, TOL); assertEqualsRelativeTol("parallel IR01", 1937.5939998494734, IR01IndexCurve, 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); } } }