/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model.simpleinstrument;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Period;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivative;
import com.opengamma.analytics.financial.simpleinstruments.pricing.SimpleFutureDataBundle;
import com.opengamma.core.holiday.HolidaySource;
import com.opengamma.core.region.RegionSource;
import com.opengamma.core.security.Security;
import com.opengamma.core.security.SecuritySource;
import com.opengamma.core.value.MarketDataRequirementNames;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.function.AbstractFunction.NonCompiledInvoker;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.engine.function.FunctionInputs;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.value.ComputedValue;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValueProperties.Builder;
import com.opengamma.engine.value.ValuePropertyNames;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.financial.OpenGammaCompilationContext;
import com.opengamma.financial.analytics.conversion.BondFutureSecurityConverter;
import com.opengamma.financial.analytics.conversion.BondSecurityConverter;
import com.opengamma.financial.analytics.conversion.FutureSecurityConverterDeprecated;
import com.opengamma.financial.analytics.conversion.InterestRateFutureSecurityConverterDeprecated;
import com.opengamma.financial.analytics.timeseries.DateConstraint;
import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesBundle;
import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesFunctionUtils;
import com.opengamma.financial.convention.ConventionBundleSource;
import com.opengamma.financial.security.FinancialSecurityUtils;
import com.opengamma.financial.security.future.AgricultureFutureSecurity;
import com.opengamma.financial.security.future.EnergyFutureSecurity;
import com.opengamma.financial.security.future.FutureSecurity;
import com.opengamma.financial.security.future.MetalFutureSecurity;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesResolutionResult;
import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesResolver;
import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.async.AsynchronousExecution;
import com.opengamma.util.money.Currency;
/**
*
*/
public abstract class SimpleFutureFunction extends NonCompiledInvoker {
/** Calculation method name */
public static final String MARKET_METHOD = "Market";
private static final Logger s_logger = LoggerFactory.getLogger(SimpleFutureFunction.class);
private FutureSecurityConverterDeprecated _converter;
private final String _valueRequirementName;
public SimpleFutureFunction(final String valueRequirementName) {
ArgumentChecker.notNull(valueRequirementName, "valueRequirement name was null.");
_valueRequirementName = valueRequirementName;
}
@Override
public void init(final FunctionCompilationContext context) {
final HolidaySource holidaySource = OpenGammaCompilationContext.getHolidaySource(context);
final RegionSource regionSource = OpenGammaCompilationContext.getRegionSource(context);
final ConventionBundleSource conventionSource = OpenGammaCompilationContext.getConventionBundleSource(context);
final SecuritySource securitySource = OpenGammaCompilationContext.getSecuritySource(context);
final InterestRateFutureSecurityConverterDeprecated irFutureConverter = new InterestRateFutureSecurityConverterDeprecated(holidaySource, conventionSource, regionSource);
final BondSecurityConverter bondConverter = new BondSecurityConverter(holidaySource, conventionSource, regionSource);
final BondFutureSecurityConverter bondFutureConverter = new BondFutureSecurityConverter(securitySource, bondConverter);
_converter = new FutureSecurityConverterDeprecated(irFutureConverter, bondFutureConverter);
}
@Override
public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target,
final Set<ValueRequirement> desiredValues) throws AsynchronousExecution {
// 1. Build the analytic derivative to be priced
final ZonedDateTime now = ZonedDateTime.now(executionContext.getValuationClock());
final FutureSecurity security = (FutureSecurity) target.getTrade().getSecurity();
// Get reference price
final HistoricalTimeSeriesBundle timeSeriesBundle = HistoricalTimeSeriesFunctionUtils.getHistoricalTimeSeriesInputs(executionContext, inputs);
final LocalDateDoubleTimeSeries timeSeries = timeSeriesBundle.get(MarketDataRequirementNames.MARKET_VALUE, security.getExternalIdBundle()).getTimeSeries();
if (timeSeries == null || timeSeries.isEmpty()) {
throw new OpenGammaRuntimeException("Time Series is null or empty for " + security.getExternalIdBundle());
}
final Double lastMarginPrice = timeSeries.getLatestValue();
if (lastMarginPrice == null) {
throw new OpenGammaRuntimeException("Could not find latest value in time series.");
}
final InstrumentDerivative derivative = security.accept(_converter).toDerivative(now, lastMarginPrice, new String[0]);
// 2. Build up the (simple) market data bundle
final SimpleFutureDataBundle market = new SimpleFutureDataBundle(null, getMarketPrice(security, inputs), null, null, null);
// 3. The Calculation - what we came here to do
final Object results = computeValues(derivative, market);
// 4. Create result specification to match the properties promised and return
final ValueSpecification spec = new ValueSpecification(getValueRequirementName(), target.toSpecification(), createValueProperties(target).get());
return Collections.singleton(new ComputedValue(spec, results));
}
protected abstract Object computeValues(InstrumentDerivative derivative, SimpleFutureDataBundle market);
@Override
public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
final Security security = target.getTrade().getSecurity();
return security instanceof EnergyFutureSecurity
|| security instanceof MetalFutureSecurity
|| security instanceof AgricultureFutureSecurity;
}
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
return Collections.singleton(new ValueSpecification(_valueRequirementName, target.toSpecification(), createValueProperties(target).get()));
}
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
final Set<ValueRequirement> requirements = new HashSet<ValueRequirement>();
final FutureSecurity security = (FutureSecurity) target.getTrade().getSecurity();
// Live market price
requirements.add(getMarketPriceRequirement(security));
// Last day's closing price
final ValueRequirement refPriceReq = getReferencePriceRequirement(context, security);
if (refPriceReq == null) {
return null;
}
requirements.add(refPriceReq);
return requirements;
}
private ValueRequirement getMarketPriceRequirement(final Security security) {
return new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.SECURITY, security.getUniqueId());
}
protected Double getMarketPrice(final Security security, final FunctionInputs inputs) {
final ValueRequirement marketPriceRequirement = getMarketPriceRequirement(security);
final Object marketPriceObject = inputs.getValue(marketPriceRequirement);
if (marketPriceObject == null) {
throw new OpenGammaRuntimeException("Could not get " + marketPriceRequirement);
}
return (Double) marketPriceObject;
}
private ValueRequirement getReferencePriceRequirement(final FunctionCompilationContext context, final FutureSecurity security) {
final HistoricalTimeSeriesResolver resolver = OpenGammaCompilationContext.getHistoricalTimeSeriesResolver(context);
final ExternalIdBundle idBundle = security.getExternalIdBundle();
final HistoricalTimeSeriesResolutionResult timeSeries = resolver.resolve(security.getExternalIdBundle(), null, null, null, MarketDataRequirementNames.MARKET_VALUE, null);
if (timeSeries == null) {
s_logger.warn("Failed to find time series for: " + idBundle.toString());
return null;
}
return HistoricalTimeSeriesFunctionUtils.createHTSRequirement(timeSeries, MarketDataRequirementNames.MARKET_VALUE,
DateConstraint.VALUATION_TIME.minus(Period.ofDays(7)), true, DateConstraint.VALUATION_TIME, true);
}
@Override
public ComputationTargetType getTargetType() {
return ComputationTargetType.TRADE;
}
private String getValueRequirementName() {
return _valueRequirementName;
}
private Builder createValueProperties(final ComputationTarget target) {
final Currency ccy = FinancialSecurityUtils.getCurrency(target.getTrade().getSecurity());
final ValueProperties.Builder properties = createValueProperties()
.with(ValuePropertyNames.CURRENCY, ccy.getCode())
.with(ValuePropertyNames.CALCULATION_METHOD, MARKET_METHOD);
return properties;
}
}