/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.fxforward; 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.money.Currency.EUR; import static com.opengamma.util.money.Currency.GBP; import static com.opengamma.util.money.Currency.JPY; import static com.opengamma.util.money.Currency.USD; import static com.opengamma.util.result.FailureStatus.MISSING_DATA; import static com.opengamma.util.result.SuccessStatus.SUCCESS; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.mockito.Mockito.mock; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertTrue; import java.io.IOException; import java.net.URI; import java.util.Map; import org.hamcrest.MatcherAssert; import org.testng.annotations.Test; import org.threeten.bp.LocalDate; import org.threeten.bp.Period; import org.threeten.bp.ZoneOffset; import org.threeten.bp.ZonedDateTime; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.opengamma.core.config.ConfigSource; import com.opengamma.core.convention.ConventionSource; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeriesSource; import com.opengamma.core.holiday.HolidaySource; import com.opengamma.core.id.ExternalSchemes; import com.opengamma.core.legalentity.LegalEntitySource; import com.opengamma.core.link.ConfigLink; import com.opengamma.core.region.RegionSource; import com.opengamma.core.security.SecuritySource; import com.opengamma.financial.analytics.conversion.FXForwardSecurityConverter; import com.opengamma.financial.analytics.curve.ConfigDBCurveConstructionConfigurationSource; import com.opengamma.financial.analytics.curve.CurveConstructionConfigurationSource; import com.opengamma.financial.analytics.curve.exposure.ConfigDBInstrumentExposuresProvider; import com.opengamma.financial.analytics.curve.exposure.ExposureFunctions; import com.opengamma.financial.analytics.curve.exposure.InstrumentExposuresProvider; import com.opengamma.financial.convention.ConventionBundleSource; import com.opengamma.financial.currency.CurrencyMatrix; import com.opengamma.financial.currency.CurrencyPair; import com.opengamma.financial.security.FinancialSecurityVisitor; import com.opengamma.financial.security.fx.FXForwardSecurity; import com.opengamma.id.ExternalId; import com.opengamma.id.UniqueId; import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesResolver; import com.opengamma.master.historicaltimeseries.impl.RemoteHistoricalTimeSeriesResolver; 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.DefaultFXReturnSeriesFn; import com.opengamma.sesame.DefaultFixingsFn; import com.opengamma.sesame.DiscountingMulticurveBundleFn; import com.opengamma.sesame.DiscountingMulticurveBundleResolverFn; import com.opengamma.sesame.DiscountingMulticurveCombinerFn; import com.opengamma.sesame.Environment; import com.opengamma.sesame.ExposureFunctionsDiscountingMulticurveCombinerFn; import com.opengamma.sesame.FXMatrixFn; import com.opengamma.sesame.FXReturnSeriesFn; import com.opengamma.sesame.FixingsFn; import com.opengamma.sesame.MarketExposureSelector; import com.opengamma.sesame.RootFinderConfiguration; import com.opengamma.sesame.SimpleEnvironment; import com.opengamma.sesame.TimeSeriesReturnConverterFactory; import com.opengamma.sesame.cache.CachingProxyDecorator; import com.opengamma.sesame.component.CurrencyPairSet; import com.opengamma.sesame.component.RetrievalPeriod; import com.opengamma.sesame.component.StringSet; import com.opengamma.sesame.config.EngineUtils; import com.opengamma.sesame.config.FunctionModelConfig; import com.opengamma.sesame.engine.ComponentMap; import com.opengamma.sesame.engine.TraceType; import com.opengamma.sesame.function.FunctionMetadata; import com.opengamma.sesame.function.scenarios.curvedata.FunctionTestUtils; import com.opengamma.sesame.graph.FunctionBuilder; import com.opengamma.sesame.graph.FunctionModel; import com.opengamma.sesame.marketdata.DefaultHistoricalMarketDataFn; import com.opengamma.sesame.marketdata.DefaultMarketDataFn; import com.opengamma.sesame.marketdata.HistoricalMarketDataFn; import com.opengamma.sesame.marketdata.MarketDataBundle; import com.opengamma.sesame.marketdata.MarketDataFn; import com.opengamma.sesame.pnl.DefaultHistoricalPnLFXConverterFn; import com.opengamma.sesame.pnl.HistoricalPnLFXConverterFn; import com.opengamma.sesame.pnl.PnLPeriodBound; import com.opengamma.sesame.trace.Tracer; import com.opengamma.sesame.trace.TracingProxy; import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries; import com.opengamma.util.money.Currency; import com.opengamma.util.result.Result; import com.opengamma.util.result.ResultStatus; import com.opengamma.util.test.TestGroup; import com.opengamma.util.time.LocalDateRange; @Test(groups = TestGroup.UNIT) public class FXForwardPnlSeriesFunctionTest { private static final ZonedDateTime s_valuationTime = ZonedDateTime.of(2013, 11, 7, 11, 0, 0, 0, ZoneOffset.UTC); @Test public void buildGraph() { FunctionMetadata calculatePnl = EngineUtils.createMetadata(FXForwardPnLSeriesFn.class, "calculatePnlSeries"); FunctionModelConfig config = createFunctionConfig(mock(CurrencyMatrix.class)); ComponentMap componentMap = componentMap(ConfigSource.class, ConventionSource.class, ConventionBundleSource.class, HistoricalTimeSeriesResolver.class, LegalEntitySource.class, SecuritySource.class, HolidaySource.class, HistoricalTimeSeriesSource.class, MarketDataFn.class, HistoricalMarketDataFn.class, RegionSource.class); FunctionModel functionModel = FunctionModel.forFunction(calculatePnl, config, componentMap.getComponentTypes()); Object fn = functionModel.build(new FunctionBuilder(), componentMap).getReceiver(); assertTrue(fn instanceof FXForwardPnLSeriesFn); System.out.println(functionModel.prettyPrint(true)); } //@Test(groups = TestGroup.INTEGRATION) @Test(groups = TestGroup.INTEGRATION, enabled = false) public void executeAgainstRemoteServerWithNoData() throws IOException { Result<LocalDateDoubleTimeSeries> pnl = executeAgainstRemoteServer(); assertNotNull(pnl); MatcherAssert.assertThat(pnl.getStatus(), is((ResultStatus) MISSING_DATA)); } //@Test(groups = TestGroup.INTEGRATION) @Test(groups = TestGroup.INTEGRATION, enabled = false) public void executeAgainstRemoteServerWithData() throws IOException { Result<LocalDateDoubleTimeSeries> pnl = executeAgainstRemoteServer(); assertNotNull(pnl); assertThat(pnl.getStatus(), is((ResultStatus) SUCCESS)); } // TODO this will probably have to be completely rewritten to use the new API that drives 2-stage execution private Result<LocalDateDoubleTimeSeries> executeAgainstRemoteServer() { String serverUrl = "http://devsvr-lx-2:8080"; //String serverUrl = "http://localhost:8080"; ComponentMap serverComponents = ComponentMap.loadComponents(serverUrl); ConfigSource configSource = serverComponents.getComponent(ConfigSource.class); HistoricalTimeSeriesSource timeSeriesSource = serverComponents.getComponent(HistoricalTimeSeriesSource.class); LocalDate date = LocalDate.of(2013, 11, 7); // TODO need a bundle backed by historical data MarketDataBundle marketDataBundle = null; // TODO set up a service context and do this with a link CurrencyMatrix currencyMatrix = configSource.getLatestByName(CurrencyMatrix.class, "BloombergLiveData"); URI htsResolverUri = URI.create(serverUrl + "/jax/components/HistoricalTimeSeriesResolver/shared"); HistoricalTimeSeriesResolver htsResolver = new RemoteHistoricalTimeSeriesResolver(htsResolverUri); Map<Class<?>, Object> comps = ImmutableMap.<Class<?>, Object>of(HistoricalTimeSeriesResolver.class, htsResolver); ComponentMap componentMap = serverComponents.with(comps); CachingProxyDecorator cachingDecorator = new CachingProxyDecorator(FunctionTestUtils.createCacheProvider()); FXForwardPnLSeriesFn pvFunction = FunctionModel.build(FXForwardPnLSeriesFn.class, createFunctionConfig(currencyMatrix), componentMap, TracingProxy.INSTANCE, cachingDecorator); ExternalId regionId = ExternalId.of(ExternalSchemes.FINANCIAL, "US"); ZonedDateTime forwardDate = ZonedDateTime.of(2014, 11, 7, 12, 0, 0, 0, ZoneOffset.UTC); FXForwardSecurity security = new FXForwardSecurity(EUR, 10_000_000, USD, 14_000_000, forwardDate, regionId); security.setUniqueId(UniqueId.of("sec", "123")); TracingProxy.start(Tracer.create(TraceType.FULL)); Result<LocalDateDoubleTimeSeries> result = null; int nRuns = 100; //int nRuns = 1; Environment env = new SimpleEnvironment(s_valuationTime, marketDataBundle); for (int i = 0; i < nRuns; i++) { result = pvFunction.calculatePnlSeries(env, security); System.out.println(); } System.out.println(TracingProxy.end().prettyPrint()); return result; } private static FunctionModelConfig createFunctionConfig(CurrencyMatrix currencyMatrix) { ConfigLink<ExposureFunctions> exposureConfig = ConfigLink.resolved(mock(ExposureFunctions.class)); LocalDateRange range = LocalDateRange.of(LocalDate.of(2013, 1, 1), LocalDate.of(2014, 1, 1), true); return config( arguments( function( MarketExposureSelector.class, argument("exposureFunctions", exposureConfig)), function( DefaultHistoricalPnLFXConverterFn.class, argument("periodBound", PnLPeriodBound.START)), function( DiscountingFXForwardSpotPnLSeriesFn.class, argument("useHistoricalSpot", true), argument("dateRange", range), argument("outputCurrency", Optional.of(Currency.USD)), argument("timeSeriesConverter", TimeSeriesReturnConverterFactory.absolute())), function( RootFinderConfiguration.class, argument("rootFinderAbsoluteTolerance", 1e-9), argument("rootFinderRelativeTolerance", 1e-9), argument("rootFinderMaxIterations", 1000)), function( DefaultCurrencyPairsFn.class, argument("currencyPairs", CurrencyPairSet.of(CurrencyPair.of(USD, JPY), CurrencyPair.of(EUR, USD), CurrencyPair.of(GBP, USD)))), function( DefaultDiscountingMulticurveBundleFn.class, argument("impliedCurveNames", StringSet.of())), function( DefaultCurveNodeConverterFn.class, argument("timeSeriesDuration", RetrievalPeriod.of(Period.ofYears(1))))), implementations( FXForwardPnLSeriesFn.class, DiscountingFXForwardSpotPnLSeriesFn.class, FXReturnSeriesFn.class, DefaultFXReturnSeriesFn.class, FXForwardCalculatorFn.class, FXForwardDiscountingCalculatorFn.class, DiscountingMulticurveCombinerFn.class, ExposureFunctionsDiscountingMulticurveCombinerFn.class, CurrencyPairsFn.class, DefaultCurrencyPairsFn.class, FinancialSecurityVisitor.class, FXForwardSecurityConverter.class, InstrumentExposuresProvider.class, ConfigDBInstrumentExposuresProvider.class, CurveSpecificationMarketDataFn.class, DefaultCurveSpecificationMarketDataFn.class, FXMatrixFn.class, DefaultFXMatrixFn.class, CurveDefinitionFn.class, DefaultCurveDefinitionFn.class, DiscountingMulticurveBundleFn.class, DefaultDiscountingMulticurveBundleFn.class, DiscountingMulticurveBundleResolverFn.class, DefaultDiscountingMulticurveBundleResolverFn.class, CurveSpecificationFn.class, DefaultCurveSpecificationFn.class, CurveConstructionConfigurationSource.class, ConfigDBCurveConstructionConfigurationSource.class, CurveNodeConverterFn.class, DefaultCurveNodeConverterFn.class, FixingsFn.class, DefaultFixingsFn.class, MarketDataFn.class, DefaultMarketDataFn.class, HistoricalMarketDataFn.class, DefaultHistoricalMarketDataFn.class, HistoricalPnLFXConverterFn.class, DefaultHistoricalPnLFXConverterFn.class)); } private static ComponentMap componentMap(Class<?>... componentTypes) { Map<Class<?>, Object> compMap = Maps.newHashMap(); for (Class<?> componentType : componentTypes) { compMap.put(componentType, mock(componentType)); } return ComponentMap.of(compMap); } }