/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.marketdata.snapshot;
import static java.lang.String.format;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.threeten.bp.Instant;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.core.marketdatasnapshot.CurveKey;
import com.opengamma.core.marketdatasnapshot.CurveSnapshot;
import com.opengamma.core.marketdatasnapshot.SnapshotDataBundle;
import com.opengamma.core.marketdatasnapshot.StructuredMarketDataSnapshot;
import com.opengamma.core.marketdatasnapshot.SurfaceData;
import com.opengamma.core.marketdatasnapshot.SurfaceKey;
import com.opengamma.core.marketdatasnapshot.SurfaceSnapshot;
import com.opengamma.core.marketdatasnapshot.UnstructuredMarketDataSnapshot;
import com.opengamma.core.marketdatasnapshot.ValueSnapshot;
import com.opengamma.core.marketdatasnapshot.VolatilityCubeData;
import com.opengamma.core.marketdatasnapshot.VolatilityCubeKey;
import com.opengamma.core.marketdatasnapshot.VolatilityCubeSnapshot;
import com.opengamma.core.marketdatasnapshot.VolatilitySurfaceData;
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.value.MarketDataRequirementNames;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.marketdata.AbstractMarketDataSnapshot;
import com.opengamma.engine.marketdata.InMemoryLKVMarketDataProvider;
import com.opengamma.engine.marketdata.availability.DefaultMarketDataAvailabilityProvider;
import com.opengamma.engine.marketdata.availability.MarketDataAvailabilityFilter;
import com.opengamma.engine.marketdata.availability.MarketDataAvailabilityProvider;
import com.opengamma.engine.marketdata.availability.ProviderMarketDataAvailabilityFilter;
import com.opengamma.engine.marketdata.spec.MarketData;
import com.opengamma.engine.target.ComputationTargetReference;
import com.opengamma.engine.target.ComputationTargetRequirement;
import com.opengamma.engine.target.ComputationTargetType;
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.id.ExternalIdBundle;
import com.opengamma.id.UniqueId;
import com.opengamma.id.UniqueIdentifiable;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.tuple.Pair;
import com.opengamma.util.tuple.Triple;
// REVIEW jonathan 2011-06-29 -- The user market data provider classes, including this, no longer need to be in the
// engine and they simply introduce dependencies on the MarketDataSnapshotSource and specific StructuredMarketDataKeys.
// They are a perfect example of adding a custom market data source and should be moved elsewhere.
/**
* Represents a market data snapshot from a {@link com.opengamma.core.marketdatasnapshot.MarketDataSnapshotSource}.
*/
public class UserMarketDataSnapshot extends AbstractMarketDataSnapshot {
/** The instrument type property */
private static final String INSTRUMENT_TYPE_PROPERTY = "InstrumentType";
/** The surface quote type property */
private static final String SURFACE_QUOTE_TYPE_PROPERTY = "SurfaceQuoteType";
/** The surface quote units property */
private static final String SURFACE_QUOTE_UNITS_PROPERTY = "SurfaceUnits";
/** The cube quote type property */
private static final String CUBE_QUOTE_TYPE_PROPERTY = "CubeQuoteType";
/** The cube quote units property */
private static final String CUBE_QUOTE_UNITS_PROPERTY = "CubeUnits";
/** The cube definition property */
private static final String CUBE_DEFINITION_PROPERTY = "VolatilityCubeDefinition";
/** The cube specification property */
private static final String CUBE_SPECIFICATION_PROPERTY = "VolatiltyCubeSpecification";
private static final Map<String, StructuredMarketDataHandler> s_structuredDataHandlers =
ImmutableMap.of(ValueRequirementNames.YIELD_CURVE_MARKET_DATA, new YieldCurveDataHandler(),
ValueRequirementNames.CURVE_MARKET_DATA, new CurveDataHandler(),
ValueRequirementNames.VOLATILITY_SURFACE_DATA, new SurfaceDataHandler(),
ValueRequirementNames.VOLATILITY_CUBE_MARKET_DATA, new CubeDataHandler());
private InMemoryLKVMarketDataProvider _unstructured;
private final StructuredMarketDataSnapshot _snapshot;
public UserMarketDataSnapshot(final StructuredMarketDataSnapshot snapshot) {
ArgumentChecker.notNull(snapshot, "snapshot");
_snapshot = snapshot;
}
private static Object query(final ValueSnapshot valueSnapshot) {
if (valueSnapshot == null) {
return null;
}
// TODO: If there is a use case to run a snapshot with the original values then we might want a mode to use the original values
// instead of the overrides. The alternative is to create a new snapshot programmatically (or revert to an earlier version) which
// does not have the override values.
if (valueSnapshot.getOverrideValue() != null) {
return valueSnapshot.getOverrideValue();
} else {
return valueSnapshot.getMarketValue();
}
}
private static Double queryDouble(final ValueSnapshot valueSnapshot) {
final Object objResult = query(valueSnapshot);
if (objResult == null //original query() would return null for Doubles so do same here
||
objResult instanceof Double) {
return (Double) objResult;
} else {
throw new OpenGammaRuntimeException(format(
"Double was expected in snapshot but Object instance of type %s found instead.",
objResult.getClass()));
}
}
private static SnapshotDataBundle createSnapshotDataBundle(final UnstructuredMarketDataSnapshot values) {
final SnapshotDataBundle ret = new SnapshotDataBundle();
for (final ExternalIdBundle target : values.getTargets()) {
final Double value = queryDouble(values.getValue(target, MarketDataRequirementNames.MARKET_VALUE));
ret.setDataPoint(target, value);
}
return ret;
}
private static SnapshotDataBundle convertYieldCurveMarketData(final YieldCurveSnapshot yieldCurveSnapshot) {
return createSnapshotDataBundle(yieldCurveSnapshot.getValues());
}
private static SnapshotDataBundle convertCurveMarketData(final CurveSnapshot curveSnapshot) {
return createSnapshotDataBundle(curveSnapshot.getValues());
}
private static VolatilitySurfaceData<Object, Object> createVolatilitySurfaceData(final VolatilitySurfaceSnapshot volSurfaceSnapshot,
final VolatilitySurfaceKey marketDataKey) {
final Set<Object> xs = Sets.newHashSet();
final Set<Object> ys = Sets.newHashSet();
final Map<Pair<Object, Object>, Double> values = Maps.newHashMap();
final Map<Pair<Object, Object>, ValueSnapshot> snapValues = volSurfaceSnapshot.getValues();
for (final Entry<Pair<Object, Object>, ValueSnapshot> entry : snapValues.entrySet()) {
values.put(entry.getKey(), queryDouble(entry.getValue()));
xs.add(entry.getKey().getFirst());
ys.add(entry.getKey().getSecond());
}
return new VolatilitySurfaceData<>(marketDataKey.getName(), "UNKNOWN", marketDataKey.getTarget(),
xs.toArray(), ys.toArray(), values);
}
private static SurfaceData<Object, Object> createSurfaceData(final SurfaceSnapshot surfaceSnapshot, final SurfaceKey marketDataKey) {
final Set<Object> xs = new HashSet<>();
final Set<Object> ys = new HashSet<>();
final Map<Pair<Object, Object>, Double> values = new HashMap<>();
final Map<Pair<Object, Object>, ValueSnapshot> snapValues = surfaceSnapshot.getValues();
for (final Entry<Pair<Object, Object>, ValueSnapshot> entry : snapValues.entrySet()) {
values.put(entry.getKey(), queryDouble(entry.getValue()));
xs.add(entry.getKey().getFirst());
ys.add(entry.getKey().getSecond());
}
return new SurfaceData<>(marketDataKey.getName(), values);
}
private static VolatilityCubeData<Object, Object, Object> createVolatilityCubeData(final VolatilityCubeSnapshot volCubeSnapshot, final VolatilityCubeKey marketDataKey) {
final Set<Object> xs = Sets.newHashSet();
final Set<Object> ys = Sets.newHashSet();
final Set<Object> zs = Sets.newHashSet();
final Map<Triple<Object, Object, Object>, Double> values = Maps.newHashMap();
final Map<Triple<Object, Object, Object>, ValueSnapshot> snapValues = volCubeSnapshot.getValues();
for (final Entry<Triple<Object, Object, Object>, ValueSnapshot> entry : snapValues.entrySet()) {
values.put(entry.getKey(), queryDouble(entry.getValue()));
xs.add(entry.getKey().getFirst());
ys.add(entry.getKey().getSecond());
zs.add(entry.getKey().getThird());
}
return new VolatilityCubeData<>(marketDataKey.getDefinitionName(), marketDataKey.getSpecificationName(), values);
}
// AbstractMarketDataSnapshot
@Override
public UniqueId getUniqueId() {
return _snapshot.getUniqueId();
}
@Override
public Instant getSnapshotTimeIndication() {
return getSnapshotTime();
}
@Override
public synchronized void init() {
if (!isInitialized()) {
_unstructured = new InMemoryLKVMarketDataProvider();
final UnstructuredMarketDataSnapshot globalValues = _snapshot.getGlobalValues();
if (globalValues != null) {
for (final ExternalIdBundle target : globalValues.getTargets()) {
final ComputationTargetReference targetRef = new ComputationTargetRequirement(ComputationTargetType.PRIMITIVE, target);
for (final Map.Entry<String, ValueSnapshot> valuePair : globalValues.getTargetValues(target).entrySet()) {
final ValueRequirement valueRequirement = new ValueRequirement(valuePair.getKey(), targetRef);
_unstructured.addValue(valueRequirement, query(valuePair.getValue()));
}
}
}
}
}
@Override
public void init(final Set<ValueSpecification> valuesRequired, final long timeout, final TimeUnit unit) {
init();
}
@Override
public synchronized boolean isInitialized() {
return _unstructured != null;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public Instant getSnapshotTime() {
Instant snapshotTime = _snapshot.getValuationTime();
if (snapshotTime == null) {
//older snapshots do not always contain valuation times,
//so default to now if none can be inferred.
snapshotTime = Instant.now();
}
return snapshotTime;
}
@Override
public Object query(final ValueSpecification valueSpecification) {
final StructuredMarketDataHandler handler = s_structuredDataHandlers.get(valueSpecification.getValueName());
if (handler == null) {
return _unstructured.getCurrentValue(valueSpecification);
} else {
return handler.query(valueSpecification, _snapshot);
}
}
// MarketDataProvider
public MarketDataAvailabilityProvider getAvailabilityProvider() {
assertInitialized();
final MarketDataAvailabilityProvider unstructured = _unstructured.getAvailabilityProvider(MarketData.live());
return new MarketDataAvailabilityProvider() {
@Override
public ValueSpecification getAvailability(final ComputationTargetSpecification targetSpec,
final Object target,
final ValueRequirement desiredValue) {
final StructuredMarketDataHandler handler = s_structuredDataHandlers.get(desiredValue.getValueName());
if (handler == null) {
return unstructured.getAvailability(targetSpec, target, desiredValue);
} else {
return handler.resolve(targetSpec, target, desiredValue, _snapshot);
}
}
@Override
public MarketDataAvailabilityFilter getAvailabilityFilter() {
return new ProviderMarketDataAvailabilityFilter(this);
}
@Override
public Serializable getAvailabilityHintKey() {
final ArrayList<Serializable> key = Lists.newArrayList();
key.add(getClass().getName());
key.add(getUniqueId());
return key;
}
};
}
/**
* Handler for a type of structured market data. Converts the data stored in the database into an object that
* can be consumed by the engine.
*/
private abstract static class StructuredMarketDataHandler {
protected ValueProperties.Builder createValueProperties() {
return ValueProperties.with(ValuePropertyNames.FUNCTION, "StructuredMarketData");
}
/**
* @param snapshot A snapshot of market data
* @return Whether the snapshot contains data that this handler can convert.
*/
protected abstract boolean isValidSnapshot(StructuredMarketDataSnapshot snapshot);
protected abstract boolean isValidTarget(Object target);
protected abstract ValueProperties resolve(Object target,
ValueProperties constraints,
StructuredMarketDataSnapshot snapshot);
protected ValueProperties resolve(final Object target,
final ValueRequirement desiredValue,
final StructuredMarketDataSnapshot snapshot) {
return resolve(target, desiredValue.getConstraints(), snapshot);
}
public ValueSpecification resolve(ComputationTargetSpecification targetSpec,
final Object target,
final ValueRequirement desiredValue,
final StructuredMarketDataSnapshot snapshot) {
if (isValidSnapshot(snapshot) && isValidTarget(target)) {
final ValueProperties properties = resolve(target, desiredValue, snapshot);
if (properties != null) {
if (desiredValue.getConstraints().isSatisfiedBy(properties)) {
if (targetSpec == null) {
targetSpec = DefaultMarketDataAvailabilityProvider.createPrimitiveComputationTargetSpecification(target);
}
return new ValueSpecification(desiredValue.getValueName(),
targetSpec,
properties.compose(desiredValue.getConstraints()));
}
}
}
return null;
}
protected abstract Object query(UniqueId target, ValueProperties properties, StructuredMarketDataSnapshot snapshot);
public Object query(final ValueSpecification valueSpecification, final StructuredMarketDataSnapshot snapshot) {
return query(valueSpecification.getTargetSpecification().getUniqueId(),
valueSpecification.getProperties(),
snapshot);
}
}
/**
* Converts a {@link VolatilitySurfaceSnapshot} into {@link VolatilitySurfaceData}.
*/
private static class SurfaceDataHandler extends StructuredMarketDataHandler {
@Override
protected boolean isValidTarget(final Object target) {
return target instanceof UniqueIdentifiable;
}
@Override
protected boolean isValidSnapshot(final StructuredMarketDataSnapshot snapshot) {
return (snapshot.getVolatilitySurfaces() != null) && !snapshot.getVolatilitySurfaces().isEmpty();
}
@Override
protected ValueProperties resolve(final Object targetObject,
final ValueProperties constraints,
final StructuredMarketDataSnapshot snapshot) {
final UniqueId target = ((UniqueIdentifiable) targetObject).getUniqueId();
final Set<String> names = constraints.getValues(ValuePropertyNames.SURFACE);
final Set<String> instrumentTypes = constraints.getValues(INSTRUMENT_TYPE_PROPERTY);
final Set<String> quoteTypes = constraints.getValues(SURFACE_QUOTE_TYPE_PROPERTY);
final Set<String> quoteUnits = constraints.getValues(SURFACE_QUOTE_UNITS_PROPERTY);
for (final VolatilitySurfaceKey surface : snapshot.getVolatilitySurfaces().keySet()) {
if (!target.equals(surface.getTarget())) {
continue;
}
if ((names != null) && !names.isEmpty() && !names.contains(surface.getName())) {
continue;
}
if ((instrumentTypes != null) && !instrumentTypes.isEmpty() && !instrumentTypes.contains(surface.getInstrumentType())) {
continue;
}
if ((quoteTypes != null) && !quoteTypes.isEmpty() && !quoteTypes.contains(surface.getQuoteType())) {
continue;
}
if ((quoteUnits != null) && !quoteUnits.isEmpty() && !quoteUnits.contains(surface.getQuoteUnits())) {
continue;
}
return createValueProperties().with(ValuePropertyNames.SURFACE,
surface.getName()).with(INSTRUMENT_TYPE_PROPERTY,
surface.getInstrumentType()).with(
SURFACE_QUOTE_TYPE_PROPERTY,
surface.getQuoteType())
.with(SURFACE_QUOTE_UNITS_PROPERTY, surface.getQuoteUnits()).get();
}
return null;
}
@Override
protected VolatilitySurfaceData<Object, Object> query(final UniqueId target,
final ValueProperties properties,
final StructuredMarketDataSnapshot snapshot) {
final String name = properties.getValues(ValuePropertyNames.SURFACE).iterator().next();
final String instrumentType = properties.getValues(INSTRUMENT_TYPE_PROPERTY).iterator().next();
final String quoteType = properties.getValues(SURFACE_QUOTE_TYPE_PROPERTY).iterator().next();
final String quoteUnits = properties.getValues(SURFACE_QUOTE_UNITS_PROPERTY).iterator().next();
if (snapshot.getVolatilitySurfaces() != null) {
final VolatilitySurfaceKey key = VolatilitySurfaceKey.of(target, name, instrumentType, quoteType, quoteUnits);
final VolatilitySurfaceSnapshot data = snapshot.getVolatilitySurfaces().get(key);
if (data != null) {
return createVolatilitySurfaceData(data, key);
}
}
return null;
}
}
/**
* Converts a {@link VolatilitySurfaceSnapshot} into {@link VolatilitySurfaceData}.
*/
private static class CubeDataHandler extends StructuredMarketDataHandler {
@Override
protected boolean isValidTarget(final Object target) {
return target instanceof UniqueIdentifiable;
}
@Override
protected boolean isValidSnapshot(final StructuredMarketDataSnapshot snapshot) {
return (snapshot.getVolatilityCubes() != null) && !snapshot.getVolatilityCubes().isEmpty();
}
@Override
protected ValueProperties resolve(final Object targetObject,
final ValueProperties constraints,
final StructuredMarketDataSnapshot snapshot) {
final UniqueId target = ((UniqueIdentifiable) targetObject).getUniqueId();
final Set<String> definitionNames = constraints.getValues(CUBE_DEFINITION_PROPERTY);
final Set<String> specificationNames = constraints.getValues(CUBE_SPECIFICATION_PROPERTY);
final Set<String> quoteTypes = constraints.getValues(CUBE_QUOTE_TYPE_PROPERTY);
final Set<String> quoteUnits = constraints.getValues(CUBE_QUOTE_UNITS_PROPERTY);
for (final VolatilityCubeKey cube : snapshot.getVolatilityCubes().keySet()) {
if (!target.equals(ComputationTarget.NULL)) {
continue;
}
if ((definitionNames != null) && !definitionNames.isEmpty() && !definitionNames.contains(cube.getDefinitionName())) {
continue;
}
if ((specificationNames != null) && !specificationNames.isEmpty() && !specificationNames.contains(cube.getSpecificationName())) {
continue;
}
if ((quoteTypes != null) && !quoteTypes.isEmpty() && !quoteTypes.contains(cube.getQuoteType())) {
continue;
}
if ((quoteUnits != null) && !quoteUnits.isEmpty() && !quoteUnits.contains(cube.getQuoteUnits())) {
continue;
}
return createValueProperties()
.with(CUBE_DEFINITION_PROPERTY, cube.getDefinitionName())
.with(CUBE_SPECIFICATION_PROPERTY, cube.getSpecificationName())
.with(CUBE_QUOTE_TYPE_PROPERTY, cube.getQuoteType())
.with(CUBE_QUOTE_UNITS_PROPERTY, cube.getQuoteUnits())
.get();
}
return null;
}
@Override
protected VolatilityCubeData<Object, Object, Object> query(final UniqueId target,
final ValueProperties properties,
final StructuredMarketDataSnapshot snapshot) {
final String definitionName = properties.getValues(CUBE_DEFINITION_PROPERTY).iterator().next();
final String specificationName = properties.getValues(CUBE_SPECIFICATION_PROPERTY).iterator().next();
final String quoteType = properties.getValues(CUBE_QUOTE_TYPE_PROPERTY).iterator().next();
final String quoteUnits = properties.getValues(CUBE_QUOTE_UNITS_PROPERTY).iterator().next();
if (snapshot.getVolatilityCubes() != null) {
final VolatilityCubeKey key = VolatilityCubeKey.of(definitionName, specificationName, quoteType, quoteUnits);
final VolatilityCubeSnapshot data = snapshot.getVolatilityCubes().get(key);
if (data != null) {
return createVolatilityCubeData(data, key);
}
}
return null;
}
}
/**
* Converts a {@link YieldCurveSnapshot} into a {@link SnapshotDataBundle}.
*/
private static class YieldCurveDataHandler extends StructuredMarketDataHandler {
@Override
protected boolean isValidTarget(final Object target) {
return target instanceof Currency;
}
@Override
protected boolean isValidSnapshot(final StructuredMarketDataSnapshot snapshot) {
return (snapshot.getYieldCurves() != null) && !snapshot.getYieldCurves().isEmpty();
}
@Override
protected ValueProperties resolve(final Object target,
final ValueProperties constraints,
final StructuredMarketDataSnapshot snapshot) {
ValueProperties.Builder properties = null;
for (final YieldCurveKey curve : snapshot.getYieldCurves().keySet()) {
if (target.equals(curve.getCurrency())) {
if (properties == null) {
properties = createValueProperties();
}
properties.with(ValuePropertyNames.CURVE, curve.getName());
}
}
if (properties != null) {
return properties.get();
} else {
return null;
}
}
@Override
protected SnapshotDataBundle query(final UniqueId target, final ValueProperties properties, final StructuredMarketDataSnapshot snapshot) {
final String name = properties.getValues(ValuePropertyNames.CURVE).iterator().next();
if (snapshot.getYieldCurves() != null) {
final YieldCurveKey key = YieldCurveKey.of(Currency.of(target.getValue()), name);
final YieldCurveSnapshot data = snapshot.getYieldCurves().get(key);
if (data != null) {
return convertYieldCurveMarketData(data);
}
}
return null;
}
}
/**
* Converts a {@link CurveSnapshot} into a {@link SnapshotDataBundle}.
*/
private static class CurveDataHandler extends StructuredMarketDataHandler {
@Override
protected boolean isValidTarget(final Object target) {
// TODO this *has* to be wrong. presumably this is never used?
return target == null;
}
@Override
protected boolean isValidSnapshot(final StructuredMarketDataSnapshot snapshot) {
return (snapshot.getCurves() != null) && !snapshot.getCurves().isEmpty();
}
@Override
protected ValueProperties resolve(final Object target,
final ValueProperties constraints,
final StructuredMarketDataSnapshot snapshot) {
ValueProperties.Builder properties = null;
for (final CurveKey curve : snapshot.getCurves().keySet()) {
if (curve.getName().equals(Iterables.getOnlyElement(constraints.getValues(ValuePropertyNames.CURVE)))) {
if (properties == null) {
properties = createValueProperties();
}
properties.with(ValuePropertyNames.CURVE, curve.getName());
}
}
if (properties != null) {
return properties.get();
} else {
return null;
}
}
@Override
protected SnapshotDataBundle query(final UniqueId target, final ValueProperties properties, final StructuredMarketDataSnapshot snapshot) {
final String name = properties.getValues(ValuePropertyNames.CURVE).iterator().next();
if (snapshot.getCurves() != null) {
final CurveKey key = CurveKey.of(name);
final CurveSnapshot data = snapshot.getCurves().get(key);
if (data != null) {
return convertCurveMarketData(data);
}
}
return null;
}
}
}