/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.web.analytics; 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.implementations; import static com.opengamma.sesame.config.ConfigBuilder.output; 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 java.net.URI; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriBuilder; import org.joda.beans.impl.flexi.FlexiBean; import org.threeten.bp.LocalDate; import org.threeten.bp.Period; import org.threeten.bp.ZonedDateTime; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.opengamma.DataNotFoundException; import com.opengamma.core.id.ExternalSchemes; import com.opengamma.core.link.ConfigLink; import com.opengamma.engine.marketdata.spec.LiveMarketDataSpecification; import com.opengamma.engine.marketdata.spec.MarketDataSpecification; 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.currency.CurrencyMatrix; import com.opengamma.financial.currency.CurrencyPair; import com.opengamma.financial.security.FinancialSecurityVisitor; import com.opengamma.financial.security.equity.EquitySecurity; import com.opengamma.financial.security.fx.FXForwardSecurity; import com.opengamma.id.ExternalId; import com.opengamma.id.ObjectId; import com.opengamma.id.UniqueId; import com.opengamma.sesame.ConfigDbMarketExposureSelectorFn; 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.DefaultFXMatrixFn; import com.opengamma.sesame.DiscountingMulticurveBundleFn; import com.opengamma.sesame.DiscountingMulticurveCombinerFn; import com.opengamma.sesame.ExposureFunctionsDiscountingMulticurveCombinerFn; import com.opengamma.sesame.FXMatrixFn; import com.opengamma.sesame.MarketExposureSelectorFn; import com.opengamma.sesame.OutputNames; import com.opengamma.sesame.RootFinderConfiguration; import com.opengamma.sesame.TimeSeriesReturnConverter; import com.opengamma.sesame.TimeSeriesReturnConverterFactory; import com.opengamma.sesame.component.RetrievalPeriod; import com.opengamma.sesame.component.StringSet; import com.opengamma.sesame.config.FunctionModelConfig; import com.opengamma.sesame.config.ViewConfig; import com.opengamma.sesame.fxforward.DiscountingFXForwardPVFn; import com.opengamma.sesame.fxforward.DiscountingFXForwardSpotPnLSeriesFn; import com.opengamma.sesame.fxforward.FXForwardCalculatorFn; import com.opengamma.sesame.fxforward.FXForwardDiscountingCalculatorFn; import com.opengamma.sesame.fxforward.FXForwardPVFn; import com.opengamma.sesame.marketdata.DefaultHistoricalMarketDataFn; import com.opengamma.sesame.marketdata.HistoricalMarketDataFn; import com.opengamma.sesame.pnl.DefaultHistoricalPnLFXConverterFn; import com.opengamma.sesame.pnl.PnLPeriodBound; import com.opengamma.sesame.server.FunctionServer; import com.opengamma.sesame.server.FunctionServerRequest; import com.opengamma.sesame.server.IndividualCycleOptions; import com.opengamma.util.money.Currency; import com.opengamma.util.time.LocalDateRange; /** * RESTful resource for all analytic views. * <p> * The analytics resource represents all analytic views. */ @Path("/analytics/views") public class WebAnalyticsViewsResource extends AbstractWebAnalyticsResource { /** * Dummy ID for inline view. */ private static final ObjectId OID_INLINE_1000 = ObjectId.of("Inline", "1000"); /** * Creates the resource. * @param functionServer the function server, not null */ public WebAnalyticsViewsResource(FunctionServer functionServer) { super(functionServer); } //------------------------------------------------------------------------- @GET @Produces(MediaType.TEXT_HTML) public String getHTML() { FlexiBean out = createRootData(); return getFreemarker().build(HTML_DIR + "views.ftl", out); } //------------------------------------------------------------------------- @Path("{viewId}") public WebAnalyticViewResource findView(@PathParam("viewId") String idStr) { data().setUriViewId(idStr); UniqueId oid = UniqueId.parse(idStr); if (oid.equalObjectId(OID_INLINE_1000)) { data().setCalculationRequest(createInline1000()); } else { throw new DataNotFoundException("View not found: " + oid); } return new WebAnalyticViewResource(this); } //------------------------------------------------------------------------- /** * Creates the output root data. * @return the output root data, not null */ protected FlexiBean createRootData() { FlexiBean out = super.createRootData(); List<ObjectId> list = ImmutableList.of(OID_INLINE_1000); out.put("views", list); return out; } //------------------------------------------------------------------------- /** * Builds a URI. * @param data the data, not null * @return the URI, not null */ public static URI uri(WebAnalyticsData data) { UriBuilder builder = data.getUriInfo().getBaseUriBuilder().path(WebAnalyticsViewsResource.class); return builder.build(); } //------------------------------------------------------------------------- //------------------------------------------------------------------------- //------------------------------------------------------------------------- private static final AtomicLong s_nextId = new AtomicLong(0); private FunctionServerRequest<IndividualCycleOptions> createInline1000() { ZonedDateTime valuationTime = ZonedDateTime.now(); LocalDate seriesStart = LocalDate.of(2013, 11, 12); LocalDate seriesEnd = LocalDate.of(2014, 1, 10); TimeSeriesReturnConverter spotVWConverter = TimeSeriesReturnConverterFactory.relativeVolatilityWeighted(0.93); LocalDateRange range = LocalDateRange.of(seriesStart, seriesEnd, true); Boolean useHistoricalSpot = false; ViewConfig viewConfig = configureView( "Example view", createFunctionConfig(), column( "Present Value", output(OutputNames.FX_PRESENT_VALUE, FXForwardSecurity.class), output(OutputNames.PRESENT_VALUE, EquitySecurity.class)), column("Spot PnL Series", output(OutputNames.PNL_SERIES, FXForwardSecurity.class, config( arguments( function( DiscountingFXForwardSpotPnLSeriesFn.class, argument("dateRange", range), argument("outputCurrency", Optional.of(Currency.USD)), argument("useHistoricalSpot", useHistoricalSpot), argument("endDate", valuationTime.toLocalDate()), argument("timeSeriesConverter", spotVWConverter))))))); MarketDataSpecification bloomberg = LiveMarketDataSpecification.of("Bloomberg"); IndividualCycleOptions cycleOptions = IndividualCycleOptions.builder() .valuationTime(valuationTime) .marketDataSpecs(ImmutableList.of(bloomberg)) // .marketDataSpec(new FixedHistoricalMarketDataSpecification(LocalDate.now().minusDays(2))) .build(); ImmutableList<Object> inputs = ImmutableList.<Object>of( createRandomFxForwardSecurity(), createRandomFxForwardSecurity(), createRandomFxForwardSecurity(), createEquitySecurity1(), createEquitySecurity2(), createEquitySecurity3(), createEquitySecurity4(), createEquitySecurity5()); FunctionServerRequest<IndividualCycleOptions> request = FunctionServerRequest.<IndividualCycleOptions>builder() .viewConfig(viewConfig) .inputs(inputs) .cycleOptions(cycleOptions) .build(); return request; } private static FXForwardSecurity createRandomFxForwardSecurity() { ExternalId regionId = ExternalId.of(ExternalSchemes.FINANCIAL, "US"); double usdAmount = 10_000_000 * Math.random(); double eurAmount = usdAmount * (1.31 + 0.04 * Math.random()); Currency payCcy; Currency recCcy; double payAmount; double recAmount; if (Math.random() < 0.5) { payAmount = usdAmount; payCcy = USD; recAmount = eurAmount; recCcy = EUR; } else { payAmount = eurAmount; payCcy = EUR; recAmount = usdAmount; recCcy = USD; } ZonedDateTime forwardDate = ZonedDateTime.now().plusWeeks((long) (104 * Math.random())); FXForwardSecurity security = new FXForwardSecurity(payCcy, payAmount, recCcy, recAmount, forwardDate, regionId); String id = Long.toString(s_nextId.getAndIncrement()); security.setUniqueId(UniqueId.of("fxFwdSec", id)); security.setName("FX forward " + id); return security; } private static EquitySecurity createEquitySecurity1() { ExternalId securityId = ExternalId.of(ExternalSchemes.BLOOMBERG_TICKER, "ACE US Equity"); EquitySecurity sec = new EquitySecurity("NEW YORK STOCK EXCHANGE INC.", "XNYS", "ACE LTD", Currency.USD); sec.setExternalIdBundle(securityId.toBundle()); sec.setShortName("ACE"); sec.setName("ACE US Equity"); return sec; } private static EquitySecurity createEquitySecurity2() { ExternalId securityId = ExternalId.of(ExternalSchemes.BLOOMBERG_TICKER, "IBM US Equity"); EquitySecurity sec = new EquitySecurity("NEW YORK STOCK EXCHANGE INC.", "XNYS", "INTL BUSINESS MACHINES CORP", Currency.USD); sec.setExternalIdBundle(securityId.toBundle()); sec.setShortName("IBM"); sec.setName("IBM US Equity"); return sec; } private static EquitySecurity createEquitySecurity3() { ExternalId securityId = ExternalId.of(ExternalSchemes.BLOOMBERG_TICKER, "AAPL US Equity"); EquitySecurity sec = new EquitySecurity("NASDAQ/NGS (GLOBAL SELECT MARKET)", "XNGS", "APPLE", Currency.USD); sec.setExternalIdBundle(securityId.toBundle()); sec.setShortName("AAPL"); sec.setName("AAPL US Equity"); return sec; } private static EquitySecurity createEquitySecurity4() { ExternalId securityId = ExternalId.of(ExternalSchemes.BLOOMBERG_TICKER, "VOD LN Equity"); EquitySecurity sec = new EquitySecurity("LONDON STOCK EXCHANGE", "XLON", "VODAFONE GROUP PLC", Currency.USD); sec.setExternalIdBundle(securityId.toBundle()); sec.setShortName("VOD"); sec.setName("VOD LN Equity"); return sec; } private static EquitySecurity createEquitySecurity5() { ExternalId securityId = ExternalId.of(ExternalSchemes.BLOOMBERG_TICKER, "HSBA LN Equity"); EquitySecurity sec = new EquitySecurity("LONDON STOCK EXCHANGE", "XLON", "HSBC HOLDINGS PLC", Currency.USD); sec.setExternalIdBundle(securityId.toBundle()); sec.setShortName("HSBA"); sec.setName("HSBA LN Equity"); return sec; } private static FunctionModelConfig createFunctionConfig() { ConfigLink<CurrencyMatrix> currencyMatrixLink = ConfigLink.resolvable("BloombergLiveData", CurrencyMatrix.class); ConfigLink<ExposureFunctions> exposureFunctionsLink = ConfigLink.resolvable("Exposure Config", ExposureFunctions.class); return config( arguments( function( ConfigDbMarketExposureSelectorFn.class, argument("exposureConfig", exposureFunctionsLink)), function( RootFinderConfiguration.class, argument("rootFinderAbsoluteTolerance", 1e-9), argument("rootFinderRelativeTolerance", 1e-9), argument("rootFinderMaxIterations", 1000)), function( DefaultCurrencyPairsFn.class, argument( "currencyPairs", ImmutableSet.of( CurrencyPair.of(USD, JPY), CurrencyPair.of(EUR, USD), CurrencyPair.of(GBP, USD)))), function(DefaultCurveNodeConverterFn.class, argument("timeSeriesDuration", RetrievalPeriod.of(Period.ofYears(1)))), function( DefaultDiscountingMulticurveBundleFn.class, argument("impliedCurveNames", StringSet.of("Implied Deposit Curve EUR"))), function(DefaultHistoricalPnLFXConverterFn.class, argument("periodBound", PnLPeriodBound.START))), implementations( CurrencyPairsFn.class, DefaultCurrencyPairsFn.class, CurveConstructionConfigurationSource.class, ConfigDBCurveConstructionConfigurationSource.class, CurveDefinitionFn.class, DefaultCurveDefinitionFn.class, CurveNodeConverterFn.class, DefaultCurveNodeConverterFn.class, CurveSpecificationFn.class, DefaultCurveSpecificationFn.class, CurveSpecificationMarketDataFn.class, DefaultCurveSpecificationMarketDataFn.class, DiscountingMulticurveBundleFn.class, DefaultDiscountingMulticurveBundleFn.class, DiscountingMulticurveCombinerFn.class, ExposureFunctionsDiscountingMulticurveCombinerFn.class, FinancialSecurityVisitor.class, FXForwardSecurityConverter.class, FXForwardCalculatorFn.class, FXForwardDiscountingCalculatorFn.class, FXForwardPVFn.class, DiscountingFXForwardPVFn.class, FXMatrixFn.class, DefaultFXMatrixFn.class, HistoricalMarketDataFn.class, DefaultHistoricalMarketDataFn.class, InstrumentExposuresProvider.class, ConfigDBInstrumentExposuresProvider.class, MarketExposureSelectorFn.class, ConfigDbMarketExposureSelectorFn.class)); } }