/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.curve; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.Instant; import org.threeten.bp.LocalTime; import org.threeten.bp.ZoneOffset; import org.threeten.bp.ZonedDateTime; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.core.marketdatasnapshot.SnapshotDataBundle; import com.opengamma.core.value.MarketDataRequirementNames; import com.opengamma.engine.ComputationTarget; import com.opengamma.engine.ComputationTargetSpecification; 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.marketdata.ExternalIdBundleResolver; import com.opengamma.engine.target.ComputationTargetType; 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.analytics.curve.credit.ConfigDBCurveDefinitionSource; import com.opengamma.financial.analytics.curve.credit.CurveDefinitionSource; import com.opengamma.financial.analytics.curve.credit.CurveSpecificationBuilder; import com.opengamma.financial.analytics.ircurve.strips.BillNode; import com.opengamma.financial.analytics.ircurve.strips.BondNode; import com.opengamma.financial.analytics.ircurve.strips.CurveNodeWithIdentifier; import com.opengamma.financial.analytics.ircurve.strips.PointsCurveNodeWithIdentifier; import com.opengamma.id.ExternalIdBundle; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.async.AsynchronousExecution; /** * For a given curve name, returns a {@link SnapshotDataBundle} containing the market data for the nodes of that curve. This function does not require that any or all of the market data is available * for it to return the snapshot. */ public class CurveMarketDataFunction extends AbstractFunction { /** The logger */ private static final Logger s_logger = LoggerFactory.getLogger(CurveMarketDataFunction.class); /** The curve name */ private final String _curveName; /** The curve definition source */ private CurveDefinitionSource _curveDefinitionSource; /** The curve specification builder */ private CurveSpecificationBuilder _curveSpecificationBuilder; /** * @param curveName The curve name, not null */ public CurveMarketDataFunction(final String curveName) { ArgumentChecker.notNull(curveName, "curve name"); _curveName = curveName; } /** * Gets the curve name. * * @return The curve name */ public String getCurveName() { return _curveName; } @Override public void init(final FunctionCompilationContext context) { _curveDefinitionSource = ConfigDBCurveDefinitionSource.init(context, this); _curveSpecificationBuilder = ConfigDBCurveSpecificationBuilder.init(context, this); } @Override public CompiledFunctionDefinition compile(final FunctionCompilationContext context, final Instant atInstant) { final ZonedDateTime atZDT = ZonedDateTime.ofInstant(atInstant, ZoneOffset.UTC); final ValueProperties properties = createValueProperties().with(ValuePropertyNames.CURVE, _curveName).get(); final ValueSpecification spec = new ValueSpecification(ValueRequirementNames.CURVE_MARKET_DATA, ComputationTargetSpecification.NULL, properties); try { final AbstractCurveSpecification specification = CurveUtils.getSpecification(atInstant, _curveDefinitionSource, _curveSpecificationBuilder, atZDT.toLocalDate(), _curveName); return new MyCompiledFunction(atZDT.with(LocalTime.MIDNIGHT), atZDT.plusDays(1).with(LocalTime.MIDNIGHT).minusNanos(1000000), specification, spec); } catch (final Exception e) { throw new OpenGammaRuntimeException(e.getMessage() + ": problem in CurveDefinition called " + _curveName); } } /** * Function that gets market data for a curve. */ protected class MyCompiledFunction extends AbstractInvokingCompiledFunction { /** The curve specification */ private final AbstractCurveSpecification _specification; /** The result specification */ private final ValueSpecification _spec; /** The market data requirements */ private final Set<ValueRequirement> _requirements; /** * @param earliestInvocation The earliest time at which this function can be invoked * @param latestInvocation The latest time at which this function can be invoked * @param specification The curve specification * @param spec The result specification */ @SuppressWarnings("synthetic-access") public MyCompiledFunction(final ZonedDateTime earliestInvocation, final ZonedDateTime latestInvocation, final AbstractCurveSpecification specification, final ValueSpecification spec) { super(earliestInvocation, latestInvocation); _specification = specification; _spec = spec; _requirements = CurveMarketDataFunction.getRequirements(_specification, _curveName); } @Override public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) throws AsynchronousExecution { final ExternalIdBundleResolver resolver = new ExternalIdBundleResolver(executionContext.getComputationTargetResolver()); final SnapshotDataBundle marketData = new SnapshotDataBundle(); populateSnapshot(_specification, inputs, marketData, resolver); return Collections.singleton(new ComputedValue(_spec, marketData)); } @Override public ComputationTargetType getTargetType() { return ComputationTargetType.NULL; } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext compilationContext, final ComputationTarget target) { return Collections.singleton(_spec); } @Override public Set<ValueRequirement> getRequirements(final FunctionCompilationContext compilationContext, final ComputationTarget target, final ValueRequirement desiredValue) { if (_requirements == null) { return null; } return _requirements; } @Override public boolean canHandleMissingRequirements() { return true; } @Override public boolean canHandleMissingInputs() { return true; } } /** * Gets the market data requirements from a curve specification. * @param abstractSpecification The curve specification * @param curveName The curve name * @return The set of requirements */ /* package */static Set<ValueRequirement> getRequirements(final AbstractCurveSpecification abstractSpecification, final String curveName) { final Set<ValueRequirement> requirements = new HashSet<>(); if (abstractSpecification instanceof ConstantCurveSpecification) { final ConstantCurveSpecification constant = (ConstantCurveSpecification) abstractSpecification; return Collections.singleton(new ValueRequirement(constant.getDataField(), ComputationTargetType.PRIMITIVE, constant.getIdentifier())); } else if (abstractSpecification instanceof CurveSpecification) { final CurveSpecification specification = (CurveSpecification) abstractSpecification; final Collection<CurveNodeWithIdentifier> nodes = specification.getNodes(); for (final CurveNodeWithIdentifier id : nodes) { try { if (id.getDataField() != null) { if ((id.getCurveNode() instanceof BondNode) || (id.getCurveNode() instanceof BillNode)) { requirements.add(new ValueRequirement(id.getDataField(), ComputationTargetType.SECURITY, id.getIdentifier())); } else { requirements.add(new ValueRequirement(id.getDataField(), ComputationTargetType.PRIMITIVE, id.getIdentifier())); if (id instanceof PointsCurveNodeWithIdentifier) { final PointsCurveNodeWithIdentifier node = (PointsCurveNodeWithIdentifier) id; requirements.add(new ValueRequirement(node.getUnderlyingDataField(), ComputationTargetType.PRIMITIVE, node.getUnderlyingIdentifier())); } } } else { requirements.add(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, id.getIdentifier())); } } catch (final OpenGammaRuntimeException e) { s_logger.error(curveName + " " + e.getMessage()); return null; } } } else if (abstractSpecification instanceof SpreadCurveSpecification) { final SpreadCurveSpecification spread = (SpreadCurveSpecification) abstractSpecification; final Set<ValueRequirement> firstRequirements = getRequirements(spread.getFirstCurve(), curveName); if (firstRequirements == null) { return null; } requirements.addAll(firstRequirements); } return requirements; } /** * Populates a market data snapshot for a curve specification. * @param abstractSpecification The specification * @param inputs The function inputs * @param marketData The market data snapshot bundle * @param resolver The external id bundle resolver * @return A populated snapshot */ /* package */static SnapshotDataBundle populateSnapshot(final AbstractCurveSpecification abstractSpecification, final FunctionInputs inputs, final SnapshotDataBundle marketData, final ExternalIdBundleResolver resolver) { if (abstractSpecification instanceof ConstantCurveSpecification) { final ConstantCurveSpecification constant = (ConstantCurveSpecification) abstractSpecification; final ComputedValue value = inputs.getComputedValue(new ValueRequirement(constant.getDataField(), ComputationTargetType.PRIMITIVE, constant.getIdentifier())); if (value != null) { final ExternalIdBundle identifiers = value.getSpecification().getTargetSpecification().accept(resolver); final Object valueObject = value.getValue(); if (!(valueObject instanceof Double)) { throw new OpenGammaRuntimeException("Value for " + identifiers + " was not a number; have " + valueObject); } marketData.setDataPoint(identifiers, (Double) valueObject); } else { s_logger.info("Could not get market data for {} with field {}", constant.getIdentifier(), constant.getDataField()); } } else if (abstractSpecification instanceof CurveSpecification) { final CurveSpecification specification = (CurveSpecification) abstractSpecification; for (final CurveNodeWithIdentifier id : specification.getNodes()) { if (id.getDataField() != null) { ComputedValue value; if ((id.getCurveNode() instanceof BondNode) || (id.getCurveNode() instanceof BillNode)) { try { value = inputs.getComputedValue(new ValueRequirement(id.getDataField(), ComputationTargetType.SECURITY, id.getIdentifier())); } catch (final NullPointerException e) { // happens when the target cannot be resolved value = null; } } else { value = inputs.getComputedValue(new ValueRequirement(id.getDataField(), ComputationTargetType.PRIMITIVE, id.getIdentifier())); } if (value != null) { final Object valueObject = value.getValue(); if (!(valueObject instanceof Double)) { throw new OpenGammaRuntimeException("Value for " + id.getIdentifier() + " was not a number; have " + valueObject); } final ExternalIdBundle identifiers = value.getSpecification().getTargetSpecification().accept(resolver); if (id instanceof PointsCurveNodeWithIdentifier) { final PointsCurveNodeWithIdentifier pointsId = (PointsCurveNodeWithIdentifier) id; final ComputedValue base = inputs .getComputedValue(new ValueRequirement(pointsId.getUnderlyingDataField(), ComputationTargetType.PRIMITIVE, pointsId.getUnderlyingIdentifier())); if (base != null) { final Object baseObject = base.getValue(); if (!(baseObject instanceof Double)) { throw new OpenGammaRuntimeException("Value for " + pointsId.getUnderlyingIdentifier() + " was not a number; have " + valueObject); } final ExternalIdBundle spreadIdentifiers = value.getSpecification().getTargetSpecification().accept(resolver); if (value.getValue() == null || base.getValue() == null) { marketData.setDataPoint(spreadIdentifiers, null); } else { marketData.setDataPoint(spreadIdentifiers, (Double) valueObject + (Double) baseObject); } } else { s_logger.info("Could not get market data for {}", pointsId.getUnderlyingIdentifier()); } } else { marketData.setDataPoint(identifiers, (Double) valueObject); } } else { s_logger.info("Could not get market data for {}", id.getIdentifier()); } } else { final ComputedValue value = inputs.getComputedValue(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, id.getIdentifier())); if (value != null) { final ExternalIdBundle identifiers = value.getSpecification().getTargetSpecification().accept(resolver); marketData.setDataPoint(identifiers, (Double) value.getValue()); } else { s_logger.info("Could not get market data for {}", id.getIdentifier()); } } } } else if (abstractSpecification instanceof SpreadCurveSpecification) { final SpreadCurveSpecification spread = (SpreadCurveSpecification) abstractSpecification; populateSnapshot(spread.getFirstCurve(), inputs, marketData, resolver); } else { throw new OpenGammaRuntimeException("Cannot handle specifications of type " + abstractSpecification.getClass()); } return marketData; } }