/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.component; import static com.opengamma.sesame.config.ConfigBuilder.argument; import static com.opengamma.sesame.config.ConfigBuilder.arguments; import static com.opengamma.sesame.config.ConfigBuilder.column; import static com.opengamma.sesame.config.ConfigBuilder.config; import static com.opengamma.sesame.config.ConfigBuilder.configureView; import static com.opengamma.sesame.config.ConfigBuilder.function; import static com.opengamma.sesame.config.ConfigBuilder.nonPortfolioOutput; import static com.opengamma.sesame.config.ConfigBuilder.output; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.testng.Assert.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.testng.annotations.Test; import org.threeten.bp.LocalDate; import org.threeten.bp.Period; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.opengamma.core.config.impl.ConfigItem; import com.opengamma.core.id.ExternalSchemes; import com.opengamma.core.link.ConfigLink; import com.opengamma.financial.analytics.conversion.FXForwardSecurityConverter; import com.opengamma.financial.analytics.curve.ConfigDBCurveConstructionConfigurationSource; import com.opengamma.financial.analytics.curve.CurveConstructionConfiguration; import com.opengamma.financial.analytics.curve.exposure.ConfigDBInstrumentExposuresProvider; import com.opengamma.financial.convention.businessday.BusinessDayConventions; import com.opengamma.financial.convention.daycount.DayCounts; import com.opengamma.financial.convention.frequency.PeriodFrequency; import com.opengamma.financial.currency.SimpleCurrencyMatrix; import com.opengamma.financial.security.irs.FixedInterestRateSwapLeg; import com.opengamma.financial.security.irs.FloatingInterestRateSwapLeg; import com.opengamma.financial.security.irs.InterestRateSwapLeg; import com.opengamma.financial.security.irs.InterestRateSwapNotional; import com.opengamma.financial.security.irs.InterestRateSwapSecurity; import com.opengamma.financial.security.irs.PayReceiveType; import com.opengamma.financial.security.irs.Rate; import com.opengamma.financial.security.swap.FloatingRateType; import com.opengamma.id.ExternalId; import com.opengamma.id.ExternalIdBundle; import com.opengamma.id.VersionCorrection; import com.opengamma.service.ThreadLocalServiceContext; import com.opengamma.service.VersionCorrectionProvider; import com.opengamma.sesame.CurveDefinitionCurveLabellingFn; 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.DefaultFXReturnSeriesFn; import com.opengamma.sesame.DefaultFixingsFn; import com.opengamma.sesame.DiscountingMulticurveBundleResolverFn; import com.opengamma.sesame.EngineTestUtils; import com.opengamma.sesame.ExposureFunctionsDiscountingMulticurveCombinerFn; import com.opengamma.sesame.MarketExposureSelector; import com.opengamma.sesame.OutputNames; import com.opengamma.sesame.RootFinderConfiguration; import com.opengamma.sesame.config.ViewConfig; import com.opengamma.sesame.engine.CycleArguments; import com.opengamma.sesame.engine.Results; import com.opengamma.sesame.engine.View; import com.opengamma.sesame.engine.ViewFactory; import com.opengamma.sesame.engine.ViewInputs; import com.opengamma.sesame.engine.ViewOutputs; import com.opengamma.sesame.engine.ViewResultsDeserializer; import com.opengamma.sesame.engine.ViewResultsSerializer; import com.opengamma.sesame.equity.DefaultEquityPresentValueFn; import com.opengamma.sesame.equity.EquityPresentValueFn; import com.opengamma.sesame.fra.FRAFn; import com.opengamma.sesame.function.AvailableImplementations; import com.opengamma.sesame.function.AvailableImplementationsImpl; import com.opengamma.sesame.function.AvailableOutputs; import com.opengamma.sesame.function.AvailableOutputsImpl; import com.opengamma.sesame.fxforward.DiscountingFXForwardPVFn; import com.opengamma.sesame.fxforward.DiscountingFXForwardSpotPnLSeriesFn; import com.opengamma.sesame.fxforward.DiscountingFXForwardYCNSPnLSeriesFn; import com.opengamma.sesame.fxforward.DiscountingFXForwardYieldCurveNodeSensitivitiesFn; import com.opengamma.sesame.fxforward.FXForwardDiscountingCalculatorFn; import com.opengamma.sesame.fxforward.FXForwardPVFn; import com.opengamma.sesame.fxforward.FXForwardPnLSeriesFn; import com.opengamma.sesame.fxforward.FXForwardYCNSPnLSeriesFn; import com.opengamma.sesame.fxforward.FXForwardYieldCurveNodeSensitivitiesFn; import com.opengamma.sesame.interestrate.InterestRateMockSources; import com.opengamma.sesame.irs.DefaultInterestRateSwapConverterFn; import com.opengamma.sesame.irs.DiscountingInterestRateSwapCalculatorFactory; import com.opengamma.sesame.irs.DiscountingInterestRateSwapFn; import com.opengamma.sesame.irs.InterestRateSwapFn; import com.opengamma.sesame.marketdata.DefaultHistoricalMarketDataFn; import com.opengamma.sesame.marketdata.DefaultMarketDataFn; import com.opengamma.sesame.pnl.DefaultHistoricalPnLFXConverterFn; import com.opengamma.util.GUIDGenerator; import com.opengamma.util.money.Currency; import com.opengamma.util.result.Result; import com.opengamma.util.test.TestGroup; /** * Tests that recording of data for a view, allows the view * to be reproduced using just that data. */ @Test(groups = TestGroup.UNIT) public class RecordingDataTest { public void testCurveViewCanBeSavedAndRead() { AvailableOutputs availableOutputs = createAvailableOutputs(); AvailableImplementations availableImplementations = createAvailableImplementations(); ImmutableMap<Class<?>, Object> components = InterestRateMockSources.generateBaseComponents(); ViewFactory viewFactory = EngineTestUtils.createViewFactory(components, availableOutputs, availableImplementations); View view = viewFactory.createView(createCurveBundleConfig("TEST")); VersionCorrection versionCorrection = ThreadLocalServiceContext.getInstance().get(VersionCorrectionProvider.class).getConfigVersionCorrection(); CycleArguments cycleArguments = CycleArguments.builder(InterestRateMockSources.createMarketDataEnvironment()) .versionCorrection(versionCorrection) .captureInputs(true) .build(); Results run = view.run(cycleArguments); Result<Object> result = run.getNonPortfolioResults().get("TEST").getResult(); if (!result.isSuccess()) { fail(result.getFailureMessage()); } // Capture results ViewResultsSerializer serializer = new ViewResultsSerializer(run); // Write inputs to stream ByteArrayOutputStream inputsBaos = new ByteArrayOutputStream(1_000_000); serializer.serializeViewInputs(inputsBaos); ByteArrayOutputStream outputsBaos = new ByteArrayOutputStream(1_000_000); serializer.serializeViewOutputs(outputsBaos); ViewResultsDeserializer inputsDeserializer = new ViewResultsDeserializer(new ByteArrayInputStream(inputsBaos.toByteArray())); ViewInputs viewInputs2 = inputsDeserializer.deserialize(ViewInputs.class); assertThat(viewInputs2, is(notNullValue())); ViewResultsDeserializer outputsDeserializer = new ViewResultsDeserializer(new ByteArrayInputStream(outputsBaos.toByteArray())); ViewOutputs viewOutputs = outputsDeserializer.deserialize(ViewOutputs.class); assertThat(viewOutputs, is(notNullValue())); CapturedResultsLoader loader = new CapturedResultsLoader(viewInputs2, availableOutputs, availableImplementations); loader.addExtraConfigData("EmptyCurrencyMatrix", ConfigItem.of(new SimpleCurrencyMatrix())); Results run2 = loader.runViewFromInputs(); Result<Object> result2 = run2.getNonPortfolioResults().get("TEST").getResult(); if (!result2.isSuccess()) { fail(result2.toString()); } // Check results are same as original ones ViewOutputs view2Outputs = ViewOutputs.builder() .nonPortfolioResults(run2.getNonPortfolioResults()) .columnNames(run2.getColumnNames()) .rows(run2.getRows()) .build(); assertThat(result2.getValue(), is(result.getValue())); assertThat(view2Outputs, is(viewOutputs)); } public void testPricingMethodCanBeCaptured() { // Test that we can capture results when we have trades/securities involved AvailableOutputs availableOutputs = createAvailableOutputs(); AvailableImplementations availableImplementations = createAvailableImplementations(); ViewFactory viewFactory = EngineTestUtils.createViewFactory(InterestRateMockSources.generateBaseComponents(), availableOutputs, availableImplementations); // Run view View view = viewFactory.createView(createIrsPricerConfig(), InterestRateSwapSecurity.class); VersionCorrection versionCorrection = ThreadLocalServiceContext.getInstance().get(VersionCorrectionProvider.class).getConfigVersionCorrection(); CycleArguments cycleArguments = CycleArguments.builder(InterestRateMockSources.createMarketDataEnvironment()) .versionCorrection(versionCorrection) .captureInputs(true) .build(); Results run = view.run(cycleArguments, ImmutableList.of(createFixedVsLibor3mSwap())); Result<Object> pvResult = run.get(0, 0).getResult(); if (!pvResult.isSuccess()) { fail(pvResult.toString()); } Result<Object> pv01Result = run.get(0, 1).getResult(); if (!pv01Result.isSuccess()) { fail(pv01Result.toString()); } // Capture results ViewResultsSerializer serializer = new ViewResultsSerializer(run); // Write inputs to stream ByteArrayOutputStream inputsBaos = new ByteArrayOutputStream(1_000_000); serializer.serializeViewInputs(inputsBaos); ByteArrayOutputStream outputsBaos = new ByteArrayOutputStream(1_000_000); serializer.serializeViewOutputs(outputsBaos); ViewResultsDeserializer inputsDeserializer = new ViewResultsDeserializer(new ByteArrayInputStream(inputsBaos.toByteArray())); ViewInputs viewInputs = inputsDeserializer.deserialize(ViewInputs.class); assertThat(viewInputs, is(notNullValue())); ViewResultsDeserializer outputsDeserializer = new ViewResultsDeserializer(new ByteArrayInputStream(outputsBaos.toByteArray())); ViewOutputs viewOutputs = outputsDeserializer.deserialize(ViewOutputs.class); assertThat(viewOutputs, is(notNullValue())); CapturedResultsLoader loader = new CapturedResultsLoader(viewInputs, availableOutputs, availableImplementations); loader.addExtraConfigData("EmptyCurrencyMatrix", ConfigItem.of(new SimpleCurrencyMatrix())); Results run2 = loader.runViewFromInputs(); System.out.println(run2); Result<Object> pvResult2 = run2.get(0, 0).getResult(); if (!pvResult2.isSuccess()) { fail(pvResult2.toString()); } Result<Object> pv01Result2 = run2.get(0, 1).getResult(); if (!pv01Result2.isSuccess()) { fail(pv01Result2.toString()); } // Check results are same as original ones assertThat(pvResult2.getValue(), is(pvResult.getValue())); assertThat(pv01Result2.getValue(), is(pv01Result.getValue())); // TODO - this should work but seems to be prevented by a Fudge issue // assertThat(view2Outputs, is(viewOutputs)); } private InterestRateSwapSecurity createFixedVsLibor3mSwap() { InterestRateSwapNotional notional = new InterestRateSwapNotional(Currency.USD, 100_000_000); PeriodFrequency freq6m = PeriodFrequency.of(Period.ofMonths(6)); PeriodFrequency freq3m = PeriodFrequency.of(Period.ofMonths(3)); Set<ExternalId> calendarUSNY = Sets.newHashSet(ExternalId.of(ExternalSchemes.ISDA_HOLIDAY, "USNY")); List<InterestRateSwapLeg> legs = new ArrayList<>(); FixedInterestRateSwapLeg payLeg = new FixedInterestRateSwapLeg(); payLeg.setNotional(notional); payLeg.setDayCountConvention(DayCounts.THIRTY_U_360); payLeg.setPaymentDateFrequency(freq6m); payLeg.setPaymentDateBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING); payLeg.setPaymentDateCalendars(calendarUSNY); payLeg.setMaturityDateBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING); payLeg.setAccrualPeriodFrequency(freq6m); payLeg.setAccrualPeriodBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING); payLeg.setAccrualPeriodCalendars(calendarUSNY); payLeg.setRate(new Rate(0.0150)); payLeg.setPayReceiveType(PayReceiveType.PAY); legs.add(payLeg); FloatingInterestRateSwapLeg receiveLeg = new FloatingInterestRateSwapLeg(); receiveLeg.setNotional(notional); receiveLeg.setDayCountConvention(DayCounts.ACT_360); receiveLeg.setPaymentDateFrequency(freq3m); receiveLeg.setPaymentDateBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING); receiveLeg.setPaymentDateCalendars(calendarUSNY); receiveLeg.setMaturityDateBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING); receiveLeg.setAccrualPeriodFrequency(freq3m); receiveLeg.setAccrualPeriodBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING); receiveLeg.setAccrualPeriodCalendars(calendarUSNY); receiveLeg.setResetPeriodFrequency(freq3m); receiveLeg.setResetPeriodBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING); receiveLeg.setResetPeriodCalendars(calendarUSNY); receiveLeg.setFixingDateBusinessDayConvention(BusinessDayConventions.PRECEDING); receiveLeg.setFixingDateCalendars(calendarUSNY); receiveLeg.setFixingDateOffset(-2); receiveLeg.setFloatingRateType(FloatingRateType.IBOR); receiveLeg.setFloatingReferenceRateId(InterestRateMockSources.getLiborIndexId()); receiveLeg.setPayReceiveType(PayReceiveType.RECEIVE); legs.add(receiveLeg); return new InterestRateSwapSecurity( ExternalIdBundle.of(ExternalId.of("UUID", GUIDGenerator.generate().toString())), "Fixed vs Libor 3m", LocalDate.of(2014, 9, 12), // effective date LocalDate.of(2021, 9, 12), // maturity date, legs); } private ViewConfig createCurveBundleConfig(String curveBundleOutputName) { CurveConstructionConfiguration curveConstructionConfiguration = ConfigLink.resolvable("USD_ON-OIS_LIBOR3M-FRAIRS_1U", CurveConstructionConfiguration.class).resolve(); return configureView( "Curve Bundle only", nonPortfolioOutput( curveBundleOutputName, output( OutputNames.DISCOUNTING_MULTICURVE_BUNDLE, 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( DefaultDiscountingMulticurveBundleResolverFn.class, argument("curveConfig", curveConstructionConfiguration)), function( DefaultDiscountingMulticurveBundleFn.class, argument("impliedCurveNames", StringSet.of()))))))); } private ViewConfig createIrsPricerConfig() { return configureView( "IRS Pricer", config( arguments( function( MarketExposureSelector.class, argument("exposureFunctions", ConfigLink.resolved(InterestRateMockSources.mockExposureFunctions()))), function( RootFinderConfiguration.class, argument("rootFinderAbsoluteTolerance", 1e-10), argument("rootFinderRelativeTolerance", 1e-10), argument("rootFinderMaxIterations", 5000)), function( DefaultCurrencyPairsFn.class, argument("currencyPairs", ImmutableSet.of(/*no pairs*/))), function( DefaultCurveNodeConverterFn.class, argument("timeSeriesDuration", RetrievalPeriod.of(Period.ofYears(1)))), function( DefaultDiscountingMulticurveBundleFn.class, argument("impliedCurveNames", StringSet.of())))), column( "Present Value", output(OutputNames.PRESENT_VALUE, InterestRateSwapSecurity.class)), column( "PV01", output(OutputNames.PV01, InterestRateSwapSecurity.class))); } /** * Create the available outputs. * * @return the available outputs, not null */ protected AvailableOutputs createAvailableOutputs() { AvailableOutputs available = new AvailableOutputsImpl(); available.register( DiscountingMulticurveBundleResolverFn.class, EquityPresentValueFn.class, FRAFn.class, InterestRateSwapFn.class, FXForwardPnLSeriesFn.class, FXForwardPVFn.class, FXForwardYCNSPnLSeriesFn.class, FXForwardYieldCurveNodeSensitivitiesFn.class); return available; } /** * Create the available implementations. * * @return the available implementations, not null */ protected AvailableImplementations createAvailableImplementations() { AvailableImplementations available = new AvailableImplementationsImpl(); available.register( DiscountingInterestRateSwapFn.class, DiscountingInterestRateSwapCalculatorFactory.class, DiscountingFXForwardYieldCurveNodeSensitivitiesFn.class, DiscountingFXForwardSpotPnLSeriesFn.class, DiscountingFXForwardYCNSPnLSeriesFn.class, DiscountingFXForwardPVFn.class, DefaultFXReturnSeriesFn.class, DefaultCurrencyPairsFn.class, DefaultEquityPresentValueFn.class, FXForwardSecurityConverter.class, ConfigDBInstrumentExposuresProvider.class, DefaultCurveSpecificationMarketDataFn.class, DefaultFXMatrixFn.class, DefaultCurveDefinitionFn.class, CurveDefinitionCurveLabellingFn.class, DefaultDiscountingMulticurveBundleFn.class, DefaultDiscountingMulticurveBundleResolverFn.class, DefaultCurveSpecificationFn.class, ConfigDBCurveConstructionConfigurationSource.class, DefaultFixingsFn.class, FXForwardDiscountingCalculatorFn.class, ExposureFunctionsDiscountingMulticurveCombinerFn.class, DefaultMarketDataFn.class, DefaultHistoricalMarketDataFn.class, DefaultCurveNodeConverterFn.class, DefaultInterestRateSwapConverterFn.class, DefaultHistoricalPnLFXConverterFn.class); return available; } }