/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.marketdata; import java.net.URI; import java.util.Collection; import java.util.List; import org.springframework.jms.core.JmsTemplate; import org.threeten.bp.LocalDate; import com.google.common.collect.ImmutableList; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.instrument.index.IndexON; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.core.value.MarketDataRequirementNames; import com.opengamma.livedata.LiveDataClient; import com.opengamma.livedata.client.JmsLiveDataClient; import com.opengamma.provider.livedata.LiveDataMetaData; import com.opengamma.provider.livedata.LiveDataMetaDataProvider; import com.opengamma.provider.livedata.LiveDataServerTypes; import com.opengamma.sesame.marketdata.scenarios.FilteredPerturbation; import com.opengamma.sesame.marketdata.scenarios.MatchDetails; import com.opengamma.sesame.marketdata.scenarios.Perturbation; import com.opengamma.timeseries.date.DateTimeSeries; import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries; import com.opengamma.transport.ByteArrayFudgeRequestSender; import com.opengamma.transport.jms.JmsByteArrayMessageSender; import com.opengamma.transport.jms.JmsByteArrayRequestSender; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.fudgemsg.OpenGammaFudgeContext; import com.opengamma.util.jms.JmsConnector; import com.opengamma.util.jms.JmsConnectorFactoryBean; import com.opengamma.util.metric.OpenGammaMetricRegistry; import com.opengamma.util.money.Currency; /** * Market data util methods. */ public class MarketDataUtils { /** The default field name used for looking up data in a market data record. */ static final FieldName MARKET_VALUE = FieldName.of(MarketDataRequirementNames.MARKET_VALUE); private MarketDataUtils() { } static LocalDateDoubleTimeSeries asLocalDateDoubleTimeSeries(DateTimeSeries<LocalDate, Double> timeSeries) { ArgumentChecker.notNull(timeSeries, "timeSeries"); if (timeSeries instanceof LocalDateDoubleTimeSeries) { return (LocalDateDoubleTimeSeries) timeSeries; } throw new IllegalArgumentException("Time series of type " + timeSeries.getClass().getName() + " cannot be " + "converted to a LocalDateDoubleTimeSeries"); } /** * Creates a live data client based on the information in the remote metadata. * <p> * This was copy-pasted from {@code LiveMarketDataProviderFactoryComponentFactory} because having application code * depending on configuration code seems particularly nasty. * * @param provider the metadata provider * @param jmsConnector the JMS connector * @return the client */ @SuppressWarnings("deprecation") static LiveDataClient createLiveDataClient(LiveDataMetaDataProvider provider, JmsConnector jmsConnector) { ArgumentChecker.notNull(jmsConnector, "jmsConnector"); ArgumentChecker.notNull(provider, "provider"); LiveDataMetaData metaData = provider.metaData(); URI jmsUri = metaData.getJmsBrokerUri(); if (metaData.getServerType() != LiveDataServerTypes.STANDARD || jmsUri == null) { throw new IllegalArgumentException("Unsupported live data server type " + metaData.getServerType() + " for " + metaData.getDescription() + " live data provider."); } if (!jmsConnector.getClientBrokerUri().equals(jmsUri)) { JmsConnectorFactoryBean jmsFactory = new JmsConnectorFactoryBean(jmsConnector); jmsFactory.setClientBrokerUri(jmsUri); jmsConnector = jmsFactory.getObjectCreating(); } JmsTemplate jmsTemplate = jmsConnector.getJmsTemplateTopic(); JmsByteArrayRequestSender jmsSubscriptionRequestSender; if (metaData.getJmsSubscriptionQueue() != null) { JmsTemplate subscriptionRequestTemplate = jmsConnector.getJmsTemplateQueue(); jmsSubscriptionRequestSender = new JmsByteArrayRequestSender(metaData.getJmsSubscriptionQueue(), subscriptionRequestTemplate); } else { jmsSubscriptionRequestSender = new JmsByteArrayRequestSender(metaData.getJmsSubscriptionTopic(), jmsTemplate); } ByteArrayFudgeRequestSender fudgeSubscriptionRequestSender = new ByteArrayFudgeRequestSender(jmsSubscriptionRequestSender); JmsByteArrayRequestSender jmsEntitlementRequestSender = new JmsByteArrayRequestSender(metaData.getJmsEntitlementTopic(), jmsTemplate); ByteArrayFudgeRequestSender fudgeEntitlementRequestSender = new ByteArrayFudgeRequestSender(jmsEntitlementRequestSender); JmsLiveDataClient liveDataClient = new JmsLiveDataClient(fudgeSubscriptionRequestSender, fudgeEntitlementRequestSender, jmsConnector, OpenGammaFudgeContext.getInstance(), JmsLiveDataClient.DEFAULT_NUM_SESSIONS); liveDataClient.setFudgeContext(OpenGammaFudgeContext.getInstance()); if (metaData.getJmsHeartbeatTopic() != null) { JmsByteArrayMessageSender jmsHeartbeatSender = new JmsByteArrayMessageSender(metaData.getJmsHeartbeatTopic(), jmsTemplate); liveDataClient.setHeartbeatMessageSender(jmsHeartbeatSender); } liveDataClient.start(); liveDataClient.registerMetrics(OpenGammaMetricRegistry.getSummaryInstance(), OpenGammaMetricRegistry.getDetailedInstance(), "LiveDataClient - " + provider.metaData().getDescription()); return liveDataClient; } // TODO Java 8 - replace with stream().filter() /** * Returns the perturbations whose {@link Perturbation#getMarketDataType() data type} equals {@code dataType} * and {@link Perturbation#getMatchDetailsType()} equals {@code matchDetailsType}. * * @param filteredPerturbations market data perturbations * @param dataType a type of market data or data used to build market data that the returned perturbations must handle * @param matchDetailsType a type of {@link MatchDetails} that the returned perturbations must handle * @return the perturbations that can act on the specified data type */ static <T> Collection<FilteredPerturbation> filterPerturbations( Collection<FilteredPerturbation> filteredPerturbations, Class<T> dataType, Class<? extends MatchDetails> matchDetailsType) { ArgumentChecker.notNull(filteredPerturbations, "perturbationMatches"); ArgumentChecker.notNull(dataType, "dataType"); ImmutableList.Builder<FilteredPerturbation> builder = ImmutableList.builder(); for (FilteredPerturbation filteredPerturbation : filteredPerturbations) { Perturbation perturbation = filteredPerturbation.getPerturbation(); if (perturbation.getMarketDataType().equals(dataType) && perturbation.getMatchDetailsType().equals(matchDetailsType)) { builder.add(filteredPerturbation); } } return builder.build(); } /** * Returns a new multicurve with one of its curves replaced. The new curve replaces any curves with the same name. * If the curve appears multiple times in the multicurve it is replaced everywhere. If there is no curve * in the multicurve with a matching name the returned multicurve is equal to the input. * * @param multicurve a multicurve * @param curve a curve * @return a new multicurve with a curve replaced */ public static MulticurveProviderDiscount replaceCurve(MulticurveProviderDiscount multicurve, YieldAndDiscountCurve curve) { ArgumentChecker.notNull(multicurve, "multicurve"); ArgumentChecker.notNull(curve, "curve"); MulticurveProviderDiscount updatedProvider = multicurve; List<Currency> currencies = multicurve.getCurrencyForName(curve.getName()); for (Currency currency : currencies) { updatedProvider = updatedProvider.withDiscountFactor(currency, curve); } List<IborIndex> iborIndices = multicurve.getIborIndexForName(curve.getName()); for (IborIndex index : iborIndices) { updatedProvider = updatedProvider.withForward(index, curve); } List<IndexON> onIndices = multicurve.getOvernightIndexForName(curve.getName()); for (IndexON index : onIndices) { updatedProvider = updatedProvider.withForward(index, curve); } return updatedProvider; } /** * Returns a list of lists that each contain one element from each of the input lists. There is one list in * the output for every combination of items in the inputs. The items in the output lists have the same order as * the input lists. * <p> * If the input consists of {@code n} lists with sizes {@code s1, s2, ..., sn}, the output list will have size * {@code (s1 x s2 x ... x sn)} and each of its lists will have size {@code n}. * <p> * For example: * <pre> * cartesianProduct([1, 2, 3], [a, b]) = [[1, a], [1, b], [2, a], [2, b], [3, a], [3, b]] * </pre> * <p> * If any of the lists are empty, the result is an empty list. * <p> * If there are no input lists, the result is [[]] which might be surprising but is mathematically correct. * <p> * Strictly speaking this isn't a cartesian product as that is a concept defined for sets, not lists. But * it's a close enough concept to justify using the name. * <p> * Guava has a package-private method {@code Lists.cartesianProduct()}. If that is ever made public this * can be deleted. * * @param inputs some lists * @return a list of lists containing the cartesian product of the input lists */ public static <T> List<List<T>> cartesianProduct(List<List<T>> inputs) { return cartesianProduct(ImmutableList.<T>of(), inputs); } /** * Returns a list of lists that each contain one element from each of the input lists. There is one list in * the output for every combination of items in the inputs. The items in the output lists have the same order as * the input lists. * <p> * If the input consists of {@code n} lists with sizes {@code s1, s2, ..., sn}, the output list will have size * {@code (s1 x s2 x ... x sn)} and each of its lists will have size {@code n}. * <p> * For example: * <pre> * cartesianProduct([1, 2, 3], [a, b]) = [[1, a], [1, b], [2, a], [2, b], [3, a], [3, b]] * </pre> * <p> * If any of the lists are empty, the result is an empty list. * <p> * If there are no input lists, the result is [[]] which might be surprising but is mathematically correct. * <p> * Strictly speaking this isn't a cartesian product as that is a concept defined for sets, not lists. But * it's a close enough concept to justify using the name. * <p> * Guava has a package-private method {@code Lists.cartesianProduct()}. If that is ever made public this * can be deleted. * * @param inputs some lists * @return a list of lists containing the cartesian product of the input lists */ @SafeVarargs public static <T> List<List<T>> cartesianProduct(List<T>... inputs) { return cartesianProduct(ImmutableList.<T>of(), ImmutableList.copyOf(inputs)); } /** * Recursively builds up the cartesian product. Each invocation of this method inserts the elements from * one input list. It then recurses, passing in the output so far and the remaining input lists. * * @param output the output list, possibly incomplete * @param inputs the remaining input lists * @return the complete output lists */ private static <T> List<List<T>> cartesianProduct(List<T> output, List<List<T>> inputs) { if (inputs.isEmpty()) { return ImmutableList.of(output); } List<T> firstList = inputs.get(0); ImmutableList.Builder<List<T>> builder = ImmutableList.builder(); List<List<T>> remainingInputs = inputs.subList(1, inputs.size()); for (T item : firstList) { builder.addAll(cartesianProduct(ImmutableList.<T>builder().addAll(output).add(item).build(), remainingInputs)); } return builder.build(); } }