/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.function.scenarios.marketdata; import java.util.List; import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.LocalDate; import org.threeten.bp.ZonedDateTime; import com.opengamma.core.security.Security; import com.opengamma.financial.analytics.ircurve.strips.CurveNodeWithIdentifier; import com.opengamma.financial.analytics.ircurve.strips.PointsCurveNodeWithIdentifier; import com.opengamma.financial.currency.CurrencyPair; import com.opengamma.id.ExternalIdBundle; import com.opengamma.sesame.Environment; import com.opengamma.sesame.function.scenarios.ScenarioFunction; import com.opengamma.sesame.marketdata.FieldName; import com.opengamma.sesame.marketdata.MarketDataBundle; import com.opengamma.sesame.marketdata.MarketDataFn; import com.opengamma.sesame.marketdata.MarketDataId; import com.opengamma.sesame.marketdata.RawId; import com.opengamma.sesame.marketdata.SecurityId; import com.opengamma.timeseries.date.DateTimeSeries; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.result.FailureStatus; import com.opengamma.util.result.Result; import com.opengamma.util.time.LocalDateRange; /** * Function that decorates {@link MarketDataFn} and applies shocks to the underlying market data. */ public class MarketDataShockDecorator implements MarketDataFn, ScenarioFunction<MarketDataShock, MarketDataShockDecorator> { private static final Logger s_logger = LoggerFactory.getLogger(MarketDataShockDecorator.class); /** The underlying market data function that this function decorates. */ private final MarketDataFn _delegate; /** * @param delegate the function to decorate */ public MarketDataShockDecorator(MarketDataFn delegate) { _delegate = ArgumentChecker.notNull(delegate, "delegate"); } @Override public Result<Double> getFxRate(Environment env, CurrencyPair currencyPair) { return _delegate.getFxRate(decorateDataSource(env), currencyPair); } @Override public Result<Double> getCurveNodeValue(Environment env, CurveNodeWithIdentifier node) { return _delegate.getCurveNodeValue(decorateDataSource(env), node); } @Override public Result<Double> getCurveNodeUnderlyingValue(Environment env, PointsCurveNodeWithIdentifier node) { return _delegate.getCurveNodeUnderlyingValue(decorateDataSource(env), node); } @Override public Result<Double> getMarketValue(Environment env, Security security) { return _delegate.getMarketValue(decorateDataSource(env), security); } @Override public Result<Double> getMarketValue(Environment env, ExternalIdBundle id) { return _delegate.getMarketValue(decorateDataSource(env), id); } @Override public <T> Result<T> getValue(Environment env, Security security, FieldName fieldName, Class<T> valueType) { return _delegate.getValue(env, security, fieldName, valueType); } private Environment decorateDataSource(Environment env) { // this will probably become obsolete with the new scenario API List<MarketDataShock> shocks = env.getScenarioArguments(this); if (shocks.isEmpty()) { s_logger.debug("No shocks in the environment"); return env; } return env.withMarketData(new BundleDecorator(env.getMarketDataBundle(), shocks)); } @Override public Class<MarketDataShock> getArgumentType() { return MarketDataShock.class; } // TODO this will have to be migrated to use MarketDataEnvironment if we still need it at all private class BundleDecorator implements MarketDataBundle { /** The decorated data source. */ private final MarketDataBundle _delegate; /** The shocks to apply to the market data. */ private final List<MarketDataShock> _shocks; private BundleDecorator(MarketDataBundle delegate, List<MarketDataShock> shocks) { _delegate = delegate; _shocks = shocks; } @SuppressWarnings("unchecked") @Override public <T, I extends MarketDataId<T>> Result<T> get(I id, Class<T> dataType) { Result<T> result = _delegate.get(id, dataType); if (!result.isSuccess()) { return result; } Object value = result.getValue(); if (!(value instanceof Double)) { return Result.failure(FailureStatus.ERROR, "Market data shocks can only be applied to double values. Value " + "for {} is of type {}, value {}", id, value.getClass().getName(), value); } double shockedValue = (double) value; for (MarketDataShock shock : _shocks) { // TODO this will only work for MarketDataIds with an external ID - raw and security ExternalIdBundle idBundle = getIdBundle(id); if (idBundle != null) { shockedValue = shock.apply(idBundle, shockedValue); } } return (Result<T>) Result.success(shockedValue); } @Override public <T, I extends MarketDataId<T>> Result<DateTimeSeries<LocalDate, T>> get( I id, Class<T> dataType, LocalDateRange dateRange) { return _delegate.get(id, dataType, dateRange); } @Override public MarketDataBundle withTime(ZonedDateTime time) { return new BundleDecorator(_delegate.withTime(time), _shocks); } @Override public MarketDataBundle withDate(LocalDate date) { return new BundleDecorator(_delegate.withDate(date), _shocks); } } @Nullable private static ExternalIdBundle getIdBundle(MarketDataId id) { if (id instanceof RawId) { return ((RawId) id).getId(); } else if (id instanceof SecurityId) { return ((SecurityId) id).getId(); } else { return null; } } }