/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model.curve.future;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Clock;
import org.threeten.bp.Instant;
import org.threeten.bp.LocalDate;
import org.threeten.bp.LocalTime;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.ZonedDateTime;
import com.google.common.collect.Sets;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.math.curve.NodalDoublesCurve;
import com.opengamma.analytics.util.time.TimeCalculator;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.core.security.Security;
import com.opengamma.core.security.SecuritySource;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.function.CompiledFunctionDefinition;
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.target.PrimitiveComputationTargetType;
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.financial.OpenGammaExecutionContext;
import com.opengamma.financial.analytics.model.InstrumentTypeProperties;
import com.opengamma.financial.analytics.volatility.surface.ConfigDBFuturePriceCurveDefinitionSource;
import com.opengamma.financial.analytics.volatility.surface.ConfigDBFuturePriceCurveSpecificationSource;
import com.opengamma.financial.analytics.volatility.surface.FuturePriceCurveDefinition;
import com.opengamma.financial.analytics.volatility.surface.FuturePriceCurveInstrumentProvider;
import com.opengamma.financial.analytics.volatility.surface.FuturePriceCurveSpecification;
import com.opengamma.financial.convention.HolidaySourceCalendarAdapter;
import com.opengamma.financial.convention.calendar.Calendar;
import com.opengamma.financial.convention.expirycalc.ExchangeTradedInstrumentExpiryCalculator;
import com.opengamma.financial.security.future.InterestRateFutureSecurity;
import com.opengamma.id.ExternalId;
import com.opengamma.id.VersionCorrection;
import com.opengamma.util.money.Currency;
/**
*
*/
public abstract class FuturePriceCurveFunction extends AbstractFunction {
private static final Logger s_logger = LoggerFactory.getLogger(FuturePriceCurveFunction.class);
private ConfigDBFuturePriceCurveDefinitionSource _futurePriceCurveDefinitionSource;
private ConfigDBFuturePriceCurveSpecificationSource _futurePriceCurveSpecificationSource;
@Override
public void init(final FunctionCompilationContext context) {
_futurePriceCurveDefinitionSource = ConfigDBFuturePriceCurveDefinitionSource.init(context, this);
_futurePriceCurveSpecificationSource = ConfigDBFuturePriceCurveSpecificationSource.init(context, this);
}
protected ConfigDBFuturePriceCurveDefinitionSource getFuturePriceCurveDefinitionSource() {
return _futurePriceCurveDefinitionSource;
}
protected ConfigDBFuturePriceCurveSpecificationSource getFuturePriceCurveSpecificationSource() {
return _futurePriceCurveSpecificationSource;
}
protected abstract String getInstrumentType();
@SuppressWarnings("unchecked")
private FuturePriceCurveDefinition<Object> getCurveDefinition(final ComputationTarget target, final String definitionName, final VersionCorrection versionCorrection) {
final String fullDefinitionName = definitionName + "_" + target.getUniqueId().getValue();
return (FuturePriceCurveDefinition<Object>) getFuturePriceCurveDefinitionSource().getDefinition(fullDefinitionName, getInstrumentType(), versionCorrection);
}
private FuturePriceCurveSpecification getCurveSpecification(final ComputationTarget target, final String specificationName, final VersionCorrection versionCorrection) {
final String fullSpecificationName = specificationName + "_" + target.getUniqueId().getValue();
return getFuturePriceCurveSpecificationSource().getSpecification(fullSpecificationName, getInstrumentType(), versionCorrection);
}
@SuppressWarnings("unchecked")
public static Set<ValueRequirement> buildRequirements(final FuturePriceCurveSpecification futurePriceCurveSpecification,
final FuturePriceCurveDefinition<Object> futurePriceCurveDefinition, final ZonedDateTime atInstant) {
final Set<ValueRequirement> result = new HashSet<ValueRequirement>();
final FuturePriceCurveInstrumentProvider<Object> futurePriceCurveProvider = (FuturePriceCurveInstrumentProvider<Object>) futurePriceCurveSpecification.getCurveInstrumentProvider();
for (final Object x : futurePriceCurveDefinition.getXs()) {
final ExternalId identifier = futurePriceCurveProvider.getInstrument(x, atInstant.toLocalDate());
result.add(new ValueRequirement(futurePriceCurveProvider.getDataFieldName(), ComputationTargetType.PRIMITIVE, identifier));
}
return result;
}
protected abstract Double getTimeToMaturity(int n, LocalDate date, Calendar calendar);
@Override
public CompiledFunctionDefinition compile(final FunctionCompilationContext context, final Instant atInstant) {
final ZonedDateTime atZDT = ZonedDateTime.ofInstant(atInstant, ZoneOffset.UTC);
return new AbstractInvokingCompiledFunction(atZDT.with(LocalTime.MIDNIGHT), atZDT.plusDays(1).with(LocalTime.MIDNIGHT).minusNanos(1000000)) {
@Override
public ComputationTargetType getTargetType() {
return ComputationTargetType.CURRENCY;
}
@SuppressWarnings("synthetic-access")
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext myContext, final ComputationTarget target) {
final ValueProperties curveProperties = createValueProperties().withAny(ValuePropertyNames.CURVE)
.with(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE, getInstrumentType()).get();
final ValueSpecification futurePriceCurveResult = new ValueSpecification(ValueRequirementNames.FUTURE_PRICE_CURVE_DATA, target.toSpecification(), curveProperties);
return Collections.singleton(futurePriceCurveResult);
}
@SuppressWarnings("synthetic-access")
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext myContext, final ComputationTarget target, final ValueRequirement desiredValue) {
final ValueProperties constraints = desiredValue.getConstraints();
final String curveName = constraints.getStrictValue(ValuePropertyNames.CURVE);
if (curveName == null) {
return null;
}
//TODO use separate definition and specification names?
final String curveDefinitionName = curveName;
final String curveSpecificationName = curveName;
final VersionCorrection versionCorrection = myContext.getComputationTargetResolver().getVersionCorrection();
final FuturePriceCurveDefinition<Object> priceCurveDefinition = getCurveDefinition(target, curveDefinitionName, versionCorrection);
if (priceCurveDefinition == null) {
s_logger.error("Price curve definition for target {} with curve name {} and instrument type {} was null", new Object[] {target, curveDefinitionName, getInstrumentType() });
return null;
}
final FuturePriceCurveSpecification priceCurveSpecification = getCurveSpecification(target, curveSpecificationName, versionCorrection);
if (priceCurveSpecification == null) {
s_logger.error("Price curve specification for target {} with curve name {} and instrument type {} was null", new Object[] {target, curveSpecificationName, getInstrumentType() });
return null;
}
final Set<ValueRequirement> requirements = Collections.unmodifiableSet(buildRequirements(priceCurveSpecification, priceCurveDefinition, atZDT));
return requirements;
}
@Override
public boolean canHandleMissingInputs() {
return true;
}
@Override
public boolean canHandleMissingRequirements() {
return true;
}
@SuppressWarnings({"synthetic-access", "unchecked" })
@Override
public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target,
final Set<ValueRequirement> desiredValues) {
final ValueRequirement desiredValue = desiredValues.iterator().next();
final String curveName = desiredValue.getConstraint(ValuePropertyNames.CURVE);
final Currency currency = target.getValue(PrimitiveComputationTargetType.CURRENCY);
final Calendar calendar = new HolidaySourceCalendarAdapter(OpenGammaExecutionContext.getHolidaySource(executionContext), currency);
//TODO use separate definition and specification names?
final String curveDefinitionName = curveName;
final String curveSpecificationName = curveName;
final VersionCorrection versionCorrection = executionContext.getComputationTargetResolver().getVersionCorrection();
final FuturePriceCurveDefinition<Object> priceCurveDefinition = getCurveDefinition(target, curveDefinitionName, versionCorrection);
final FuturePriceCurveSpecification priceCurveSpecification = getCurveSpecification(target, curveSpecificationName, versionCorrection);
final Clock snapshotClock = executionContext.getValuationClock();
final ZonedDateTime now = ZonedDateTime.now(snapshotClock);
final DoubleArrayList xList = new DoubleArrayList();
final DoubleArrayList prices = new DoubleArrayList();
final FuturePriceCurveInstrumentProvider<Number> futurePriceCurveProvider = (FuturePriceCurveInstrumentProvider<Number>) priceCurveSpecification.getCurveInstrumentProvider();
final LocalDate valDate = now.toLocalDate();
if (inputs.getAllValues().isEmpty()) {
throw new OpenGammaRuntimeException("Could not get any data for future price curve called " + curveSpecificationName);
}
for (final Object x : priceCurveDefinition.getXs()) {
final Number xNum = (Number) x;
ExternalId identifier = futurePriceCurveProvider.getInstrument(xNum, valDate);
final ValueRequirement requirement = new ValueRequirement(futurePriceCurveProvider.getDataFieldName(), ComputationTargetType.PRIMITIVE, identifier);
Double futurePrice = null;
if (inputs.getValue(requirement) != null) {
futurePrice = (Double) inputs.getValue(requirement);
if (futurePrice != null) {
if (priceCurveSpecification.isUseUnderlyingSecurityForExpiry()) {
// directly getting the expiry of the underliers
if (identifier.getScheme().equals(ExternalSchemes.BLOOMBERG_TICKER_WEAK)) {
identifier = ExternalSchemes.bloombergTickerSecurityId(identifier.getValue());
}
final SecuritySource securitySource = OpenGammaExecutionContext.getSecuritySource(executionContext);
final Security security = securitySource.getSingle(identifier.toBundle());
if (security != null) {
// check if the security is IRFutures here
final InterestRateFutureSecurity irFuture = (InterestRateFutureSecurity) security;
final LocalDate expiry = irFuture.getExpiry().getExpiry().toLocalDate();
final Double ttm = TimeCalculator.getTimeBetween(valDate, expiry);
xList.add(ttm);
prices.add(futurePrice);
}
} else {
final ExchangeTradedInstrumentExpiryCalculator expiryCalc = futurePriceCurveProvider.getExpiryRuleCalculator();
final LocalDate expiry = expiryCalc.getExpiryDate(xNum.intValue(), valDate, calendar);
final Double ttm = TimeCalculator.getTimeBetween(valDate, expiry);
xList.add(ttm);
prices.add(futurePrice);
}
}
}
}
final ValueSpecification futurePriceCurveResult = new ValueSpecification(ValueRequirementNames.FUTURE_PRICE_CURVE_DATA, target.toSpecification(), createValueProperties()
.with(ValuePropertyNames.CURVE, curveName).with(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE, getInstrumentType()).get());
final NodalDoublesCurve curve = NodalDoublesCurve.from(xList.toDoubleArray(), prices.toDoubleArray());
final ComputedValue futurePriceCurveResultValue = new ComputedValue(futurePriceCurveResult, curve);
return Sets.newHashSet(futurePriceCurveResultValue);
}
};
}
}