/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.marketdatasnapshot; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Maps; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeriesSource; import com.opengamma.core.marketdatasnapshot.CurveKey; import com.opengamma.core.marketdatasnapshot.CurveSnapshot; import com.opengamma.core.marketdatasnapshot.StructuredMarketDataSnapshot; import com.opengamma.core.marketdatasnapshot.ValueSnapshot; import com.opengamma.core.marketdatasnapshot.VolatilityCubeKey; import com.opengamma.core.marketdatasnapshot.VolatilityCubeSnapshot; import com.opengamma.core.marketdatasnapshot.VolatilitySurfaceKey; import com.opengamma.core.marketdatasnapshot.VolatilitySurfaceSnapshot; import com.opengamma.core.marketdatasnapshot.YieldCurveKey; import com.opengamma.core.marketdatasnapshot.YieldCurveSnapshot; import com.opengamma.core.marketdatasnapshot.impl.ManageableMarketDataSnapshot; import com.opengamma.core.marketdatasnapshot.impl.ManageableUnstructuredMarketDataSnapshot; import com.opengamma.engine.ComputationTargetResolver; import com.opengamma.engine.depgraph.DependencyGraph; import com.opengamma.engine.depgraph.DependencyNode; import com.opengamma.engine.depgraph.impl.DependencyNodeImpl; import com.opengamma.engine.function.MarketDataSourcingFunction; import com.opengamma.engine.marketdata.ExternalIdBundleResolver; import com.opengamma.engine.marketdata.snapshot.MarketDataSnapshotter; import com.opengamma.engine.value.ComputedValue; import com.opengamma.engine.value.ValueProperties; import com.opengamma.engine.value.ValuePropertyNames; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.value.ValueRequirementNames; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.engine.view.ViewComputationResultModel; import com.opengamma.engine.view.client.ViewClient; import com.opengamma.engine.view.compilation.CompiledViewCalculationConfiguration; import com.opengamma.engine.view.compilation.CompiledViewDefinitionWithGraphs; import com.opengamma.engine.view.cycle.ViewCycle; import com.opengamma.id.ExternalIdBundle; import com.opengamma.util.ArgumentChecker; /** * Default implementation of {@link MarketDataSnapshotter}. */ public class MarketDataSnapshotterImpl implements MarketDataSnapshotter { // TODO: reimplement this in a javalike way, transliterating LINQ is dirty. /** The logger */ private static final Logger s_logger = LoggerFactory.getLogger(MarketDataSnapshotterImpl.class); /** The computation target resolver */ private final ComputationTargetResolver _resolver; /** The historical time series source */ private final HistoricalTimeSeriesSource _htsSource; /** Snapshots yield curves */ private final YieldCurveSnapper _yieldCurveSnapper = new YieldCurveSnapper(); /** Snapshots curves */ private final CurveSnapper _curveSnapper = new CurveSnapper(); /** Snapshots volatility surfaces */ private final VolatilitySurfaceSnapper _volatilitySurfaceSnapper = new VolatilitySurfaceSnapper(); /** Snapshots volatility cubes */ private final VolatilityCubeSnapper _volatilityCubeSnapper = new VolatilityCubeSnapper(); @SuppressWarnings("rawtypes") /** Array of structured market data snappers */ private final StructuredSnapper[] _structuredSnappers; /** The snapshot mode */ private final Mode _mode; /** * Constructs a instance which produces structured market data snapshots. * * @param resolver the target resolver, not null * @param htsSource Must be specified if market data is inputted via HTS, may be null */ public MarketDataSnapshotterImpl(final ComputationTargetResolver resolver, final HistoricalTimeSeriesSource htsSource) { this(resolver, htsSource, Mode.STRUCTURED); } /** * @param resolver the target resolver, not null * @param htsSource Must be specified if market data is inputted via HTS, may be null * @param mode whether to create a structured or flattened snapshot */ public MarketDataSnapshotterImpl(final ComputationTargetResolver resolver, final HistoricalTimeSeriesSource htsSource, final Mode mode) { ArgumentChecker.notNull(resolver, "resolver"); _resolver = resolver; _htsSource = htsSource; _structuredSnappers = new StructuredSnapper[] {_yieldCurveSnapper, _curveSnapper, _volatilitySurfaceSnapper, _volatilityCubeSnapper }; _mode = mode; } @Override public StructuredMarketDataSnapshot createSnapshot(final ViewClient client, final ViewCycle cycle) { final CompiledViewDefinitionWithGraphs defn = cycle.getCompiledViewDefinition(); final ComputationTargetResolver.AtVersionCorrection resolver = _resolver.atVersionCorrection(cycle.getResultModel().getVersionCorrection()); return createSnapshot(new ExternalIdBundleResolver(resolver), cycle.getResultModel(), getGraphs(defn), cycle, defn.getViewDefinition().getName()); } private Map<String, DependencyGraph> getGraphs(final CompiledViewDefinitionWithGraphs defn) { final HashMap<String, DependencyGraph> ret = new HashMap<String, DependencyGraph>(); for (final CompiledViewCalculationConfiguration config : defn.getCompiledCalculationConfigurations()) { final String configName = config.getName(); final DependencyGraph graph = defn.getDependencyGraphExplorer(configName).getWholeGraph(); ret.put(configName, graph); } return ret; } public StructuredMarketDataSnapshot createSnapshot(final ExternalIdBundleResolver resolver, final ViewComputationResultModel results, final Map<String, DependencyGraph> graphs, final ViewCycle viewCycle, final String basisViewName) { final ManageableUnstructuredMarketDataSnapshot globalValues = getGlobalAndUnresolvedValues(resolver, results, graphs); final ManageableMarketDataSnapshot ret = new ManageableMarketDataSnapshot(); ret.setBasisViewName(basisViewName); ret.setGlobalValues(globalValues); ret.setValuationTime(viewCycle.getExecutionOptions().getValuationTime()); if (_mode == Mode.STRUCTURED) { final Map<YieldCurveKey, YieldCurveSnapshot> yieldCurves = _yieldCurveSnapper.getValues(results, graphs, viewCycle); final Map<CurveKey, CurveSnapshot> curves = _curveSnapper.getValues(results, graphs, viewCycle); final Map<VolatilitySurfaceKey, VolatilitySurfaceSnapshot> surfaces = _volatilitySurfaceSnapper.getValues(results, graphs, viewCycle); final Map<VolatilityCubeKey, VolatilityCubeSnapshot> cubes = _volatilityCubeSnapper.getValues(results, graphs, viewCycle); ret.setYieldCurves(yieldCurves); ret.setCurves(curves); ret.setVolatilitySurfaces(surfaces); ret.setVolatilityCubes(cubes); } else { ret.setYieldCurves(Collections.<YieldCurveKey, YieldCurveSnapshot>emptyMap()); ret.setCurves(Collections.<CurveKey, CurveSnapshot>emptyMap()); ret.setVolatilitySurfaces(Collections.<VolatilitySurfaceKey, VolatilitySurfaceSnapshot>emptyMap()); ret.setVolatilityCubes(Collections.<VolatilityCubeKey, VolatilityCubeSnapshot>emptyMap()); } return ret; } private ManageableUnstructuredMarketDataSnapshot getGlobalAndUnresolvedValues(final ExternalIdBundleResolver resolver, final ViewComputationResultModel results, final Map<String, DependencyGraph> graphs) { final ManageableUnstructuredMarketDataSnapshot snapshot = new ManageableUnstructuredMarketDataSnapshot(); for (final Entry<String, DependencyGraph> graphEntry : graphs.entrySet()) { final DependencyGraph graph = graphEntry.getValue(); final Collection<ComputedValue> marketData = results.getAllMarketData(); final Map<ValueSpecification, ComputedValue> resolvedValues = Maps.newHashMapWithExpectedSize(marketData.size()); for (final ComputedValue computedValue : marketData) { resolvedValues.put(computedValue.getSpecification(), computedValue); } final int roots = graph.getRootCount(); final Map<ValueSpecification, ?> terminalOutputs = graph.getTerminalOutputs(); for (int i = 0; i < roots; i++) { final DependencyNode root = graph.getRootNode(i); extractTerminalUnstructuredOutput(root, resolvedValues, resolver, true, terminalOutputs, snapshot); } } return snapshot; } private void extractTerminalUnstructuredOutput(final DependencyNode node, final Map<ValueSpecification, ComputedValue> resolvedValues, final ExternalIdBundleResolver resolver, boolean pathToRoot, final Map<ValueSpecification, ?> terminalOutputs, final ManageableUnstructuredMarketDataSnapshot snapshot) { final int inputs = node.getInputCount(); if (inputs == 0) { if (MarketDataSourcingFunction.UNIQUE_ID.equals(node.getFunction().getFunctionId())) { final int outputs = node.getOutputCount(); for (int i = 0; i < outputs; i++) { final ValueSpecification value = node.getOutputValue(i); final ComputedValue resolvedValue = resolvedValues.get(value); if (resolvedValue != null) { if (pathToRoot || terminalOutputs.containsKey(value)) { final ExternalIdBundle identifiers = resolveExternalIdBundle(resolver, value); if (identifiers != null) { snapshot.putValue(identifiers, value.getValueName(), ValueSnapshot.of(resolvedValue.getValue())); } } } else { // Missing market data final ExternalIdBundle identifiers = resolveExternalIdBundle(resolver, value); if (identifiers != null) { snapshot.putValue(identifiers, value.getValueName(), null); } } } } return; } if (pathToRoot && isStructuredNode(node) && _mode == Mode.STRUCTURED) { pathToRoot = false; } for (int i = 0; i < inputs; i++) { extractTerminalUnstructuredOutput(node.getInputNode(i), resolvedValues, resolver, pathToRoot, terminalOutputs, snapshot); } } private ExternalIdBundle resolveExternalIdBundle(final ExternalIdBundleResolver resolver, final ValueSpecification valueSpec) { ExternalIdBundle identifiers = resolver.visitComputationTargetSpecification(valueSpec.getTargetSpecification()); // if reading live data from hts, we need to lookup the externalIdBundle via the hts unique id if (identifiers == null && _htsSource != null && valueSpec.getTargetSpecification().getUniqueId() != null) { // try a lookup in hts identifiers = _htsSource.getExternalIdBundle(valueSpec.getTargetSpecification().getUniqueId()); } return identifiers; } @SuppressWarnings("rawtypes") private boolean isStructuredNode(final DependencyNode node) { final int outputs = node.getOutputCount(); for (int i = 0; i < outputs; i++) { final ValueSpecification output = node.getOutputValue(i); for (final StructuredSnapper snapper : _structuredSnappers) { if (output.getValueName() == snapper.getRequirementName()) { if (outputs != 1) { //TODO this is a bit fragile, but if this isn't true all sorts of things are broken s_logger.error("Structured market data node produced more than one output {} - {}", node, DependencyNodeImpl.getOutputValues(node)); throw new OpenGammaRuntimeException("Structured market data node produced more than one output"); } return true; } } } return false; } // TODO: snapshot should be holding value specifications not value requirements @Override public Map<YieldCurveKey, Map<String, ValueRequirement>> getYieldCurveSpecifications(final ViewClient client, final ViewCycle cycle) { final CompiledViewDefinitionWithGraphs defn = cycle.getCompiledViewDefinition(); final Map<String, DependencyGraph> graphs = getGraphs(defn); final Map<YieldCurveKey, Map<String, ValueRequirement>> ret = new HashMap<YieldCurveKey, Map<String, ValueRequirement>>(); for (final Entry<String, DependencyGraph> entry : graphs.entrySet()) { final DependencyGraph graph = entry.getValue(); final Iterator<DependencyNode> nodes = graph.nodeIterator(); while (nodes.hasNext()) { final DependencyNode node = nodes.next(); final int outputs = node.getOutputCount(); for (int i = 0; i < outputs; i++) { final ValueSpecification outputValue = node.getOutputValue(i); if (outputValue.getValueName().equals(ValueRequirementNames.YIELD_CURVE)) { addAll(ret, outputValue); } else if (outputValue.getValueName().equals(ValueRequirementNames.YIELD_CURVE_SPEC)) { final YieldCurveKey key = _yieldCurveSnapper.getKey(outputValue); add(ret, key, outputValue.toRequirementSpecification()); } } } } return ret; } private void addAll(final Map<YieldCurveKey, Map<String, ValueRequirement>> ret, final ValueSpecification yieldCurveSpec) { final YieldCurveKey key = _yieldCurveSnapper.getKey(yieldCurveSpec); add(ret, key, yieldCurveSpec.toRequirementSpecification()); //We know how the properties of this relate final ValueRequirement interpolatedSpec = new ValueRequirement( ValueRequirementNames.YIELD_CURVE_INTERPOLATED, yieldCurveSpec.getTargetSpecification(), getCurveProperties(yieldCurveSpec)); add(ret, key, interpolatedSpec); } private void add(final Map<YieldCurveKey, Map<String, ValueRequirement>> ret, final YieldCurveKey key, final ValueRequirement outputValue) { Map<String, ValueRequirement> ycMap = ret.get(key); if (ycMap == null) { ycMap = new HashMap<String, ValueRequirement>(); ret.put(key, ycMap); } ycMap.put(outputValue.getValueName(), outputValue); } private ValueProperties getCurveProperties(final ValueSpecification curveSpec) { return ValueProperties.builder().with(ValuePropertyNames.CURVE, curveSpec.getProperty(ValuePropertyNames.CURVE)).get(); } }