/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.marketdata; import static com.opengamma.sesame.config.ConfigBuilder.argument; import static com.opengamma.sesame.config.ConfigBuilder.arguments; import static com.opengamma.sesame.config.ConfigBuilder.config; import static com.opengamma.sesame.config.ConfigBuilder.function; import static com.opengamma.sesame.config.ConfigBuilder.implementations; import static com.opengamma.util.result.ResultTestUtils.assertSuccess; import static org.mockito.Mockito.mock; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import java.util.List; import java.util.Map; import org.testng.annotations.Test; import org.threeten.bp.Instant; import org.threeten.bp.Period; import org.threeten.bp.ZonedDateTime; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.opengamma.analytics.financial.forex.method.FXMatrix; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.provider.curve.multicurve.MulticurveDiscountBuildingRepository; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.core.link.ConfigLink; import com.opengamma.financial.analytics.curve.ConfigDBCurveConstructionConfigurationSource; import com.opengamma.financial.analytics.curve.ConfigDBCurveSpecificationBuilder; import com.opengamma.financial.analytics.curve.CurveConstructionConfiguration; import com.opengamma.financial.analytics.curve.CurveConstructionConfigurationSource; import com.opengamma.financial.analytics.curve.credit.CurveSpecificationBuilder; import com.opengamma.id.VersionCorrection; import com.opengamma.service.ServiceContext; import com.opengamma.service.ThreadLocalServiceContext; import com.opengamma.service.VersionCorrectionProvider; import com.opengamma.sesame.CurrencyPairsFn; import com.opengamma.sesame.CurveDefinitionFn; import com.opengamma.sesame.CurveNodeConverterFn; import com.opengamma.sesame.CurveSpecificationFn; import com.opengamma.sesame.CurveSpecificationMarketDataFn; import com.opengamma.sesame.DefaultCurrencyPairsFn; import com.opengamma.sesame.DefaultCurveDefinitionFn; import com.opengamma.sesame.DefaultCurveNodeConverterFn; import com.opengamma.sesame.DefaultCurveSpecificationFn; import com.opengamma.sesame.DefaultCurveSpecificationMarketDataFn; import com.opengamma.sesame.DefaultDiscountingMulticurveBundleFn; import com.opengamma.sesame.DefaultDiscountingMulticurveBundleResolverFn; import com.opengamma.sesame.DefaultFXMatrixFn; import com.opengamma.sesame.DiscountingMulticurveBundleFn; import com.opengamma.sesame.Environment; import com.opengamma.sesame.FXMatrixFn; import com.opengamma.sesame.MulticurveBundle; import com.opengamma.sesame.RootFinderConfiguration; import com.opengamma.sesame.SimpleEnvironment; import com.opengamma.sesame.component.RetrievalPeriod; import com.opengamma.sesame.component.StringSet; import com.opengamma.sesame.config.FunctionModelConfig; import com.opengamma.sesame.engine.ComponentMap; import com.opengamma.sesame.engine.FixedInstantVersionCorrectionProvider; import com.opengamma.sesame.graph.FunctionModel; import com.opengamma.sesame.interestrate.InterestRateMockSources; import com.opengamma.sesame.marketdata.builders.MarketDataBuilder; import com.opengamma.sesame.marketdata.scenarios.CurveNameMulticurveFilter; import com.opengamma.sesame.marketdata.scenarios.CyclePerturbations; import com.opengamma.sesame.marketdata.scenarios.MulticurveInputParallelShift; import com.opengamma.sesame.marketdata.scenarios.SinglePerturbationMapping; import com.opengamma.sesame.marketdata.scenarios.SingleScenarioDefinition; import com.opengamma.util.money.Currency; import com.opengamma.util.result.Result; import com.opengamma.util.test.TestGroup; import com.opengamma.util.time.DateUtils; @Test(groups = TestGroup.UNIT) public class MulticurveMarketDataBuilderTest { private static MarketDataBuilder curveBuilder() { FunctionModelConfig curveBuilderConfig = config( arguments( function( DefaultCurveNodeConverterFn.class, argument("timeSeriesDuration", RetrievalPeriod.of(Period.ofYears(1)))), function( MulticurveDiscountBuildingRepository.class, argument("toleranceAbs", 1e-9), argument("toleranceRel", 1e-9), argument("stepMaximum", 1000)), function( ConfigDBCurveSpecificationBuilder.class, argument("versionCorrection", VersionCorrection.LATEST))), implementations( CurveSpecificationBuilder.class, ConfigDBCurveSpecificationBuilder.class, CurveNodeConverterFn.class, DefaultCurveNodeConverterFn.class, HistoricalMarketDataFn.class, DefaultHistoricalMarketDataFn.class)); Map<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); return FunctionModel.build(MulticurveMarketDataBuilder.class, curveBuilderConfig, componentMap); } private static MarketDataBundle createMarketDataBundle() { MarketDataEnvironmentBuilder builder = InterestRateMockSources.createMarketDataEnvironment().toBuilder(); RawId liborId = RawId.of(InterestRateMockSources.LIBOR_INDEX_ID.toBundle()); builder.add(liborId, InterestRateMockSources.FLAT_TIME_SERIES); return new MapMarketDataBundle(builder.build()); } /** * Compares a curve built by {@link MulticurveMarketDataBuilder} to one built by * {@link DefaultDiscountingMulticurveBundleResolverFn} */ public void buildSingleCurveBundle() { MarketDataBuilder curveBuilder = curveBuilder(); CyclePerturbations cyclePerturbations = new CyclePerturbations(ImmutableSet.<MarketDataRequirement>of(), SingleScenarioDefinition.base()); MarketDataEnvironment baseMarketData = InterestRateMockSources.createMarketDataEnvironment(); FXMatrix fxMatrix = new FXMatrix(); MarketDataEnvironment marketData = baseMarketData.toBuilder().add(FxMatrixId.of(Currency.USD), fxMatrix).build(); SingleValueRequirement curveReq = SingleValueRequirement.of(MulticurveId.of("USD_ON-OIS_LIBOR3M-FRAIRS_1U")); Map<SingleValueRequirement, Result<?>> results = curveBuilder.buildSingleValues(marketData.toBundle(), marketData.getValuationTime(), ImmutableSet.of(curveReq), mock(MarketDataSource.class), cyclePerturbations); MulticurveBundle curveBundle1 = (MulticurveBundle) results.get(curveReq).getValue(); FunctionModelConfig config = config( arguments( function( RootFinderConfiguration.class, argument("rootFinderAbsoluteTolerance", 1e-9), argument("rootFinderRelativeTolerance", 1e-9), argument("rootFinderMaxIterations", 1000)), function( DefaultCurveNodeConverterFn.class, argument("timeSeriesDuration", RetrievalPeriod.of(Period.ofYears(1)))), function( DefaultDiscountingMulticurveBundleFn.class, argument("impliedCurveNames", StringSet.of()))), implementations( CurrencyPairsFn.class, DefaultCurrencyPairsFn.class, CurveSpecificationMarketDataFn.class, DefaultCurveSpecificationMarketDataFn.class, FXMatrixFn.class, DefaultFXMatrixFn.class, CurveNodeConverterFn.class, DefaultCurveNodeConverterFn.class, CurveDefinitionFn.class, DefaultCurveDefinitionFn.class, DiscountingMulticurveBundleFn.class, DefaultDiscountingMulticurveBundleFn.class, HistoricalMarketDataFn.class, DefaultHistoricalMarketDataFn.class, CurveSpecificationFn.class, DefaultCurveSpecificationFn.class, CurveConstructionConfigurationSource.class, ConfigDBCurveConstructionConfigurationSource.class, MarketDataFn.class, DefaultMarketDataFn.class)); Map<Class<?>, Object> components = InterestRateMockSources.generateBaseComponents(); DefaultDiscountingMulticurveBundleResolverFn fn = FunctionModel.build(DefaultDiscountingMulticurveBundleResolverFn.class, config, ComponentMap.of(components)); CurveConstructionConfiguration curveConfig = ConfigLink.resolvable("USD_ON-OIS_LIBOR3M-FRAIRS_1U", CurveConstructionConfiguration.class).resolve(); ZonedDateTime valuationTime = DateUtils.getUTCDate(2014, 1, 22); Environment env = new SimpleEnvironment(valuationTime, createMarketDataBundle()); Result<MulticurveBundle> result = fn.generateBundle(env, curveConfig); MulticurveBundle curveBundle2 = result.getValue(); assertEquals(curveBundle1.getCurveBuildingBlockBundle(), curveBundle2.getCurveBuildingBlockBundle()); MulticurveProviderDiscount multicurveProvider1 = curveBundle1.getMulticurveProvider(); MulticurveProviderDiscount multicurveProvider2 = curveBundle2.getMulticurveProvider(); assertEquals(multicurveProvider1.getDiscountingCurves(), multicurveProvider2.getDiscountingCurves()); assertEquals(multicurveProvider1.getForwardIborCurves(), multicurveProvider2.getForwardIborCurves()); assertEquals(multicurveProvider1.getForwardONCurves(), multicurveProvider2.getForwardONCurves()); assertEquals(multicurveProvider1.getFxRates(), multicurveProvider2.getFxRates()); assertEquals(multicurveProvider1.getAllCurveNames(), multicurveProvider2.getAllCurveNames()); } public void parallelShiftInputs() { MarketDataBuilder curveBuilder = curveBuilder(); CyclePerturbations emptyPerturbations = new CyclePerturbations(ImmutableSet.<MarketDataRequirement>of(), SingleScenarioDefinition.base()); MarketDataEnvironment baseMarketData = InterestRateMockSources.createMarketDataEnvironment(); FXMatrix fxMatrix = new FXMatrix(); MarketDataEnvironment marketData = baseMarketData.toBuilder().add(FxMatrixId.of(Currency.USD), fxMatrix).build(); SingleValueRequirement curveReq = SingleValueRequirement.of(MulticurveId.of("USD_ON-OIS_LIBOR3M-FRAIRS_1U")); Map<SingleValueRequirement, Result<?>> unshiftedResults = curveBuilder.buildSingleValues(marketData.toBundle(), marketData.getValuationTime(), ImmutableSet.of(curveReq), new EmptyMarketDataFactory.DataSource(), emptyPerturbations); // discounting: USD-ON-OIS, forwardON: USD-ON-OIS, forwardIbor: USD-LIBOR3M-FRAIRS String curveName = "USD-ON-OIS"; double shiftAmount = 0.01; SinglePerturbationMapping mapping = SinglePerturbationMapping.builder() .filter(new CurveNameMulticurveFilter(curveName)) .perturbation(MulticurveInputParallelShift.absolute(shiftAmount)) .build(); List<SinglePerturbationMapping> mappings = ImmutableList.of(mapping); SingleScenarioDefinition scenario = SingleScenarioDefinition.of("scenarioName", mappings); ImmutableSet<SingleValueRequirement> requirements = ImmutableSet.of(curveReq); CyclePerturbations shiftPerturbations = new CyclePerturbations(requirements, scenario); Map<SingleValueRequirement, Result<?>> shiftedResults = curveBuilder.buildSingleValues(marketData.toBundle(), marketData.getValuationTime(), ImmutableSet.of(curveReq), new EmptyMarketDataFactory.DataSource(), shiftPerturbations); Result<?> unshiftedResult = unshiftedResults.get(curveReq); assertSuccess(unshiftedResult); MulticurveBundle unshiftedBundle = (MulticurveBundle) unshiftedResult.getValue(); YieldAndDiscountCurve unshiftedDiscountCurve = unshiftedBundle.getMulticurveProvider().getCurve(curveName); Result<?> shiftedResult = shiftedResults.get(curveReq); assertSuccess(shiftedResult); MulticurveBundle shiftedBundle = (MulticurveBundle) shiftedResult.getValue(); YieldAndDiscountCurve shiftedDiscountCurve = shiftedBundle.getMulticurveProvider().getCurve(curveName); double unshiftedRate = unshiftedDiscountCurve.getForwardRate(2); double shiftedRate = shiftedDiscountCurve.getForwardRate(2); // TODO confirm this is close enough assertEquals(unshiftedRate + shiftAmount, shiftedRate, 1e-4); } /** * Tests that missing FX rates are handled correctly and a failure is returned and no exception is thrown. */ public void handleMissingFxMatrix() { MarketDataBuilder curveBuilder = curveBuilder(); CyclePerturbations cyclePerturbations = new CyclePerturbations(ImmutableSet.<MarketDataRequirement>of(), SingleScenarioDefinition.base()); MarketDataEnvironment marketData = InterestRateMockSources.createMarketDataEnvironment(); SingleValueRequirement curveReq = SingleValueRequirement.of(MulticurveId.of("USD_ON-OIS_LIBOR3M-FRAIRS_1U")); Map<SingleValueRequirement, Result<?>> results = curveBuilder.buildSingleValues( marketData.toBundle(), marketData.getValuationTime(), ImmutableSet.of(curveReq), mock(MarketDataSource.class), cyclePerturbations); Result<?> result = results.get(curveReq); assertFalse(result.isSuccess()); } }