/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.credit.measures; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.core.Is.is; import static org.mockito.Mockito.mock; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.threeten.bp.Instant; import org.threeten.bp.ZonedDateTime; import com.google.common.collect.ImmutableMap; import com.opengamma.analytics.financial.model.interestrate.curve.DiscountCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve; import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlockBundle; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory; import com.opengamma.analytics.math.interpolation.Interpolator1D; import com.opengamma.engine.marketdata.spec.LiveMarketDataSpecification; import com.opengamma.financial.security.credit.IndexCDSSecurity; import com.opengamma.financial.security.credit.StandardCDSSecurity; import com.opengamma.service.ServiceContext; import com.opengamma.service.ThreadLocalServiceContext; import com.opengamma.service.VersionCorrectionProvider; import com.opengamma.sesame.Environment; import com.opengamma.sesame.MulticurveBundle; import com.opengamma.sesame.config.FunctionModelConfig; import com.opengamma.sesame.credit.CreditPricingSampleData; import com.opengamma.sesame.engine.CalculationArguments; import com.opengamma.sesame.engine.ComponentMap; import com.opengamma.sesame.engine.FixedInstantVersionCorrectionProvider; import com.opengamma.sesame.engine.FunctionRunner; import com.opengamma.sesame.graph.FunctionModel; import com.opengamma.sesame.interestrate.InterestRateMockSources; import com.opengamma.sesame.marketdata.EmptyMarketDataFactory; import com.opengamma.sesame.marketdata.EmptyMarketDataSpec; import com.opengamma.sesame.marketdata.MarketDataEnvironment; import com.opengamma.sesame.marketdata.MarketDataEnvironmentBuilder; import com.opengamma.sesame.marketdata.MarketDataFactory; import com.opengamma.sesame.marketdata.MulticurveId; import com.opengamma.sesame.marketdata.builders.MarketDataBuilders; import com.opengamma.sesame.marketdata.builders.MarketDataEnvironmentFactory; import com.opengamma.sesame.trade.IndexCDSTrade; import com.opengamma.sesame.trade.StandardCDSTrade; import com.opengamma.util.function.Function; import com.opengamma.util.money.Currency; import com.opengamma.util.money.CurrencyAmount; import com.opengamma.util.result.Result; import com.opengamma.util.test.TestGroup; import com.opengamma.util.time.DateUtils; /** * Test the CDS PV using the mapping ISDA compliant yield curve provider * Pricing CDS tenor P5Y - validated in CDSYieldCurveExampleTest * log-linear: pv = -37154.039151 cs01 = 531.936202 */ @Test(groups = TestGroup.UNIT) public class MappingIsdaCompliantYieldCurveFnTest { private static final ZonedDateTime VALUATION_TIME = DateUtils.getUTCDate(2014, 10, 16); private static final double STD_TOLERANCE_PV = 1.0E-3; // Validated in OG Analytics public static final double SINGLE_NAME_EXPECTED_PV = -37154.043; private static final double BP = 1e-3; private static final CalculationArguments ARGS = CalculationArguments.builder() .valuationTime(VALUATION_TIME) .marketDataSpecification(EmptyMarketDataSpec.INSTANCE) .build(); private FunctionRunner _functionRunner; private DefaultCreditPvFn _pvFunction; private static final Interpolator1D s_interpolator = CombinedInterpolatorExtrapolatorFactory.getInterpolator("LogNaturalCubicWithMonotonicity", "QuadraticLeftExtrapolator", "LinearExtrapolator"); @BeforeMethod public void setUpClass() { FunctionModelConfig config = CreditPricingSampleData.createYCMappingFunctionModelConfig(); ImmutableMap<Class<?>, Object> components = InterestRateMockSources.generateBaseComponents(); VersionCorrectionProvider vcProvider = new FixedInstantVersionCorrectionProvider(Instant.now()); ServiceContext serviceContext = ServiceContext.of(components).with(VersionCorrectionProvider.class, vcProvider); ThreadLocalServiceContext.init(serviceContext); ComponentMap componentMap = ComponentMap.of(components); EmptyMarketDataFactory dataFactory = new EmptyMarketDataFactory(); MarketDataEnvironmentFactory environmentFactory = new MarketDataEnvironmentFactory(dataFactory, MarketDataBuilders.creditCurve(), MarketDataBuilders.isdaYieldCurve()); _functionRunner = new FunctionRunner(environmentFactory); _pvFunction = FunctionModel.build(DefaultCreditPvFn.class, config, componentMap); } private MarketDataEnvironment getSuppliedData(MulticurveBundle multicurveBundle) { MulticurveId multicurveId = MulticurveId.of("Curve Bundle"); return new MarketDataEnvironmentBuilder() .add(multicurveId, multicurveBundle) .add(CreditPricingSampleData.getCreditCurveDataSnapshotId(), CreditPricingSampleData.createCreditCurveDataSnapshot()) .valuationTime(VALUATION_TIME) .build(); } @Test public void testDiscountingStandardCdsPV() { final StandardCDSTrade trade = CreditPricingSampleData.createStandardCDSSecurity(); StandardCDSSecurity security = (StandardCDSSecurity) trade.getTrade().getSecurity(); double tolerance = security.getNotional().getAmount() * BP; Result<CurrencyAmount> result = _functionRunner.runFunction(ARGS, getSuppliedData(getDiscountCurveBundle()), new Function<Environment, Result<CurrencyAmount>>() { @Override public Result<CurrencyAmount> apply(Environment env) { return _pvFunction.priceStandardCds(env, trade); } }); assertThat(result.isSuccess(), is(true)); CurrencyAmount amount = result.getValue(); assertThat(amount.getCurrency(), is(Currency.USD)); assertThat(amount.getAmount(), is(closeTo(CreditPvFnTest.SINGLE_NAME_EXPECTED_PV, tolerance))); assertThat(amount.getAmount(), is(closeTo(SINGLE_NAME_EXPECTED_PV, STD_TOLERANCE_PV))); } @Test public void testYieldCurveStandardCdsPV() { final StandardCDSTrade trade = CreditPricingSampleData.createStandardCDSSecurity(); StandardCDSSecurity security = (StandardCDSSecurity) trade.getTrade().getSecurity(); double tolerance = security.getNotional().getAmount() * BP; Result<CurrencyAmount> result = _functionRunner.runFunction(ARGS, getSuppliedData(getYieldCurveBundle()), new Function<Environment, Result<CurrencyAmount>>() { @Override public Result<CurrencyAmount> apply(Environment env) { return _pvFunction.priceStandardCds(env, trade); } }); assertThat(result.isSuccess(), is(true)); CurrencyAmount amount = result.getValue(); assertThat(amount.getCurrency(), is(Currency.USD)); assertThat(amount.getAmount(), is(closeTo(CreditPvFnTest.SINGLE_NAME_EXPECTED_PV, tolerance))); assertThat(amount.getAmount(), is(closeTo(SINGLE_NAME_EXPECTED_PV, STD_TOLERANCE_PV))); } @Test public void testDiscountingIndexCdsPV() { final IndexCDSTrade trade = CreditPricingSampleData.createIndexCDSSecurity(); IndexCDSSecurity security = (IndexCDSSecurity) trade.getTrade().getSecurity(); double tolerance = security.getNotional().getAmount() * BP; Result<CurrencyAmount> result = _functionRunner.runFunction(ARGS, getSuppliedData(getDiscountCurveBundle()), new Function<Environment, Result<CurrencyAmount>>() { @Override public Result<CurrencyAmount> apply(Environment env) { return _pvFunction.priceIndexCds(env, trade); } }); assertThat(result.isSuccess(), is(true)); CurrencyAmount amount = result.getValue(); assertThat(amount.getCurrency(), is(Currency.USD)); assertThat(amount.getAmount(), is(closeTo(CreditPvFnTest.INDEX_EXPECTED_PV, tolerance))); } private static final double[] DAY = new double[] {91, 183, 274, 365, 457, 548, 639, 731, 1096, 1461, 1826, 2192, 2557, 2922, 3287, 3653, 4383, 5479, 7305, 9131, 10958, 14610, 18263 }; private static final int NUM_NODE = DAY.length; private static final double[] TIME; static { TIME = new double[NUM_NODE]; for (int i = 0; i < NUM_NODE; ++i) { TIME[i] = DAY[i] / 365.; } } private static final double[] ZERO_RATES = new double[] {6.85141288906939E-4, 6.927004450275529E-4, 7.052829705582995E-4, 7.229431397828344E-4, 7.47375407385545E-4, 7.834380855687433E-4, 8.347416042149961E-4, 9.038326597250597E-4, 0.0013722796143422762, 0.002176305350143962, 0.0032811647971705956, 0.00460755549100302, 0.006040682945580618, 0.00750878742104914, 0.008941312543172845, 0.010292758463102044, 0.012637449883912839, 0.015245511253328924, 0.01770642792742225, 0.018777178300023893, 0.01925642557322497, 0.01971509345664727, 0.019728349624112827}; private static final double[] DISCOUNT_FACTOR = new double[] {0.999829198540859, 0.9996527611741278, 0.9994706948329838, 0.9992773181206462, 0.999064682720019, 0.9988244611565431, 0.9985396977323168, 0.9981914957448704, 0.9958878795040178, 0.9913266485465384, 0.9837191754362143, 0.9727087439132421, 0.9585650232460775, 0.9416595859820656, 0.922635706673141, 0.90211598325509, 0.8592002467360544, 0.7954480462459721, 0.7016145412095421, 0.6251659631564156, 0.5609548680303263, 0.45423350825036723, 0.3726483241596532 }; private static MulticurveBundle getDiscountCurveBundle() { String curveName = "USD DISC"; InterpolatedDoublesCurve rawCurve = InterpolatedDoublesCurve.from(TIME, DISCOUNT_FACTOR, s_interpolator, curveName); YieldAndDiscountCurve yc = new DiscountCurve(curveName, rawCurve); MulticurveProviderDiscount multicurveProvider = new MulticurveProviderDiscount(); CurveBuildingBlockBundle curveBuildingBlockBundle = new CurveBuildingBlockBundle(); multicurveProvider.setCurve(Currency.USD, yc); return new MulticurveBundle(multicurveProvider, curveBuildingBlockBundle); } private static MulticurveBundle getYieldCurveBundle() { String curveName = "USD DISC"; InterpolatedDoublesCurve rawCurve = InterpolatedDoublesCurve.from(TIME, ZERO_RATES, s_interpolator, curveName); YieldAndDiscountCurve yc = new YieldCurve(curveName, rawCurve); MulticurveProviderDiscount multicurveProvider = new MulticurveProviderDiscount(); CurveBuildingBlockBundle curveBuildingBlockBundle = new CurveBuildingBlockBundle(); multicurveProvider.setCurve(Currency.USD, yc); return new MulticurveBundle(multicurveProvider, curveBuildingBlockBundle); } }