com.opengamma.financial.analytics.timeseries.HistoricalValuationFunction.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.financial.analytics.timeseries.HistoricalValuationFunction.java

Source

/**
 * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.financial.analytics.timeseries;

import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Period;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.opengamma.core.security.Security;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetResolver;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.engine.function.FunctionInputs;
import com.opengamma.engine.target.ComputationTargetReference;
import com.opengamma.engine.target.ComputationTargetReferenceVisitor;
import com.opengamma.engine.target.ComputationTargetRequirement;
import com.opengamma.engine.target.ComputationTargetResolverUtils;
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.engine.view.ViewCalculationConfiguration;
import com.opengamma.engine.view.ViewDefinition;
import com.opengamma.financial.OpenGammaCompilationContext;
import com.opengamma.financial.security.FinancialSecurityUtils;
import com.opengamma.financial.temptarget.TempTarget;
import com.opengamma.financial.temptarget.TempTargetRepository;
import com.opengamma.financial.view.HistoricalViewEvaluationMarketDataMode;
import com.opengamma.financial.view.HistoricalViewEvaluationResult;
import com.opengamma.financial.view.HistoricalViewEvaluationTarget;
import com.opengamma.financial.view.ViewEvaluationFunction;
import com.opengamma.financial.view.ViewEvaluationTarget;
import com.opengamma.id.ExternalBundleIdentifiable;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.ExternalIdentifiable;
import com.opengamma.id.UniqueId;
import com.opengamma.timeseries.TimeSeries;
import com.opengamma.util.money.Currency;

/**
 * Iterates a view client over historical data to produce a historical valuation of a target. The view client iteration is performed by a helper function on a {@link ViewEvaluationTarget} created by
 * this function. The time series appropriate to this function's target are then extracted from the overall evaluation results.
 */
public class HistoricalValuationFunction extends AbstractFunction.NonCompiledInvoker {

    private static final Logger s_logger = LoggerFactory.getLogger(HistoricalValuationFunction.class);

    /**
     * Property naming the value produced on the target to generate the time series. For example {@code Historical Series[Value=FairValue]} will produce a time series based on evaluating
     * {@code FairValue[]}.
     */
    public static final String VALUE_PROPERTY = "Value";

    /**
     * Prefix on properties corresponding the the underlying production on the target. For example {@code Historical Series[Value=FairValue, Value_Foo=Bar]} will produce a time series based on
     * evaluating {@code FairValue[Foo=Bar]}.
     */
    public static final String PASSTHROUGH_PREFIX = VALUE_PROPERTY + "_";

    /**
     * Property naming how the target is specified, for example by unique identifier, object identifier or external identifier.
     */
    public static final String TARGET_SPECIFICATION_PROPERTY = "Target";

    /**
     * Value of the {@link #TARGET_SPECIFICATION_PROPERTY} property indicating the target is specified by its unique identifier and is independent of the resolver version/correction time on the spawned
     * view cycles.
     */
    public static final String TARGET_SPECIFICATION_UNIQUE = "Unique";

    /**
     * Value of the {@link #TARGET_SPECIFICATION_PROPERTY} property indicating the target is specified by its object identifier and is dependent of the resolver version/correction time on the spawned
     * view cycles.
     */
    public static final String TARGET_SPECIFICATION_OBJECT = "Object";

    /**
     * Value of the {@link #TARGET_SPECIFICATION_PROPERTY} property indicating the target is specified by its external identifier bundle and is dependent of the resolver version/correction time on the
     * spawned view cycles.
     */
    public static final String TARGET_SPECIFICATION_EXTERNAL = "External";

    /**
     * Value of the market data mode property.
     */
    public static final String MARKET_DATA_MODE_PROPERTY = "MarketDataMode";

    private static final Set<String> s_ignoreConstraints = ImmutableSet.of(
            HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY,
            HistoricalTimeSeriesFunctionUtils.END_DATE_PROPERTY,
            HistoricalTimeSeriesFunctionUtils.INCLUDE_START_PROPERTY,
            HistoricalTimeSeriesFunctionUtils.INCLUDE_END_PROPERTY, MARKET_DATA_MODE_PROPERTY,
            ValuePropertyNames.FUNCTION);

    protected ValueRequirement getNestedRequirement(final ComputationTargetResolver.AtVersionCorrection resolver,
            final ComputationTarget target, final ValueProperties constraints) {
        String valueName = ValueRequirementNames.VALUE;
        ComputationTargetReference requirementTarget = null;
        final ValueProperties.Builder requirementConstraints = ValueProperties.builder();
        if (constraints.getProperties() != null) {
            for (final String constraintName : constraints.getProperties()) {
                final Set<String> constraintValues = constraints.getValues(constraintName);
                if (VALUE_PROPERTY.equals(constraintName)) {
                    if (constraintValues.isEmpty()) {
                        valueName = ValueRequirementNames.VALUE;
                    } else if (constraintValues.size() > 1) {
                        return null;
                    } else {
                        valueName = constraintValues.iterator().next();
                    }
                } else if (TARGET_SPECIFICATION_PROPERTY.equals(constraintName)) {
                    if (constraintValues.isEmpty() || constraintValues.contains(TARGET_SPECIFICATION_UNIQUE)) {
                        requirementTarget = target.toSpecification();
                    } else if (constraintValues.contains(TARGET_SPECIFICATION_OBJECT)) {
                        final ComputationTargetSpecification targetSpec = target.toSpecification();
                        if (targetSpec.getUniqueId() != null) {
                            requirementTarget = ComputationTargetResolverUtils.simplifyType(
                                    targetSpec.replaceIdentifier(target.getUniqueId().toLatest()), resolver);
                        } else {
                            // Null - special case
                            requirementTarget = targetSpec;
                        }
                    } else if (constraintValues.contains(TARGET_SPECIFICATION_EXTERNAL)) {
                        final ExternalIdBundle identifiers;
                        if (target.getValue() instanceof ExternalIdentifiable) {
                            final ExternalId identifier = ((ExternalIdentifiable) target.getValue())
                                    .getExternalId();
                            if (identifier == null) {
                                identifiers = ExternalIdBundle.EMPTY;
                            } else {
                                identifiers = identifier.toBundle();
                            }
                        } else if (target.getValue() instanceof ExternalBundleIdentifiable) {
                            identifiers = ((ExternalBundleIdentifiable) target.getValue()).getExternalIdBundle();
                        } else if (target.getValue() == null) {
                            // Null - special case
                            identifiers = ExternalIdBundle.EMPTY;
                        } else {
                            return null;
                        }
                        final ComputationTargetReference context = target.getContextSpecification();
                        if (context == null) {
                            requirementTarget = new ComputationTargetRequirement(
                                    resolver.simplifyType(target.getType()), identifiers);
                        } else {
                            requirementTarget = context.containing(
                                    resolver.simplifyType(target.getLeafSpecification().getType()), identifiers);
                        }
                    }
                } else if (constraintName.startsWith(PASSTHROUGH_PREFIX)) {
                    final String name = constraintName.substring(PASSTHROUGH_PREFIX.length());
                    if (constraintValues.isEmpty()) {
                        requirementConstraints.withAny(name);
                    } else {
                        requirementConstraints.with(name, constraintValues);
                    }
                    if (constraints.isOptional(constraintName)) {
                        requirementConstraints.withOptional(name);
                    }
                } else if (!constraints.isOptional(constraintName)
                        && !s_ignoreConstraints.contains(constraintName)) {
                    // Not an optional constraint, not one recognized here, and not one ignored by the main getRequirements method
                    return null;
                }
            }
        }
        if (requirementTarget == null) {
            requirementTarget = ComputationTargetResolverUtils.simplifyType(target.toSpecification(), resolver);
        }
        return new ValueRequirement(valueName, requirementTarget, requirementConstraints.get());
    }

    // FunctionDefinition

    @Override
    public void init(final FunctionCompilationContext context) {
        if (OpenGammaCompilationContext.getTempTargets(context) == null) {
            throw new IllegalStateException("Function compilation context does not contain "
                    + OpenGammaCompilationContext.TEMPORARY_TARGETS_NAME);
        }
    }

    // CompiledFunctionDefinition

    @Override
    public ComputationTargetType getTargetType() {
        return ComputationTargetType.ANYTHING;
    }

    @Override
    public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
        return !(target.getValue() instanceof HistoricalViewEvaluationTarget)
                && context.getViewCalculationConfiguration() != null;
    }

    @Override
    public Set<ValueSpecification> getResults(final FunctionCompilationContext context,
            final ComputationTarget target) {
        return Collections.singleton(new ValueSpecification(ValueRequirementNames.HISTORICAL_TIME_SERIES,
                target.toSpecification(), ValueProperties.all()));
    }

    private String anyConstraintOrNull(final ValueProperties constraints, final String name) {
        final Set<String> values = constraints.getValues(name);
        if ((values == null) || values.isEmpty()) {
            return null;
        } else {
            return values.iterator().next();
        }
    }

    @Override
    public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context,
            final ComputationTarget target, final ValueRequirement desiredValue) {
        ValueProperties constraints = desiredValue.getConstraints();
        String startDateConstraint = anyConstraintOrNull(constraints,
                HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY);
        String includeStartConstraintString = anyConstraintOrNull(constraints,
                HistoricalTimeSeriesFunctionUtils.INCLUDE_START_PROPERTY);
        boolean includeStartConstraint = true;
        String endDateConstraint = anyConstraintOrNull(constraints,
                HistoricalTimeSeriesFunctionUtils.END_DATE_PROPERTY);
        String includeEndConstraintString = anyConstraintOrNull(constraints,
                HistoricalTimeSeriesFunctionUtils.INCLUDE_END_PROPERTY);
        boolean includeEndConstraint = false;
        if (includeStartConstraintString != null) {
            includeStartConstraint = HistoricalTimeSeriesFunctionUtils.YES_VALUE
                    .equals(includeStartConstraintString);
        }
        if (includeEndConstraintString != null) {
            includeEndConstraint = HistoricalTimeSeriesFunctionUtils.YES_VALUE.equals(includeEndConstraintString);
        }
        if (endDateConstraint == null) {
            endDateConstraint = DateConstraint.VALUATION_TIME.toString();
        }
        if (startDateConstraint == null) {
            if (includeEndConstraint) {
                if (includeStartConstraint) {
                    startDateConstraint = endDateConstraint;
                } else {
                    startDateConstraint = DateConstraint.parse(endDateConstraint).minus(Period.ofDays(1))
                            .toString();
                }
            } else {
                if (includeStartConstraint) {
                    startDateConstraint = DateConstraint.parse(endDateConstraint).minus(Period.ofDays(1))
                            .toString();
                } else {
                    startDateConstraint = DateConstraint.parse(endDateConstraint).minus(Period.ofDays(2))
                            .toString();
                }
            }
        }
        String marketDataModeConstraint = anyConstraintOrNull(constraints, MARKET_DATA_MODE_PROPERTY);
        HistoricalViewEvaluationMarketDataMode marketDataMode = marketDataModeConstraint != null
                ? HistoricalViewEvaluationMarketDataMode.parse(marketDataModeConstraint)
                : HistoricalViewEvaluationMarketDataMode.HISTORICAL;
        Security security = null;
        if (ComputationTargetType.SECURITY.equals(target.getType())) {
            security = target.getSecurity();
        } else if (ComputationTargetType.POSITION.equals(target.getType())) {
            security = target.getPosition().getSecurityLink().resolve(context.getSecuritySource());
        }
        Set<Currency> targetCurrencies = security != null
                ? ImmutableSet.copyOf(FinancialSecurityUtils.getCurrencies(security, context.getSecuritySource()))
                : null;

        ViewDefinition viewDefinition = context.getViewCalculationConfiguration().getViewDefinition();
        final HistoricalViewEvaluationTarget tempTarget = new HistoricalViewEvaluationTarget(
                viewDefinition.getMarketDataUser(), startDateConstraint, includeStartConstraint, endDateConstraint,
                includeEndConstraint, targetCurrencies, marketDataMode);
        final ValueRequirement requirement = getNestedRequirement(context.getComputationTargetResolver(), target,
                desiredValue.getConstraints());
        if (requirement == null) {
            return null;
        }
        final ViewCalculationConfiguration calcConfig = new ViewCalculationConfiguration(
                tempTarget.getViewDefinition(), context.getViewCalculationConfiguration().getName());
        calcConfig.addSpecificRequirement(requirement);
        tempTarget.getViewDefinition().addViewCalculationConfiguration(calcConfig);
        final TempTargetRepository targets = OpenGammaCompilationContext.getTempTargets(context);
        final UniqueId tempTargetId = targets.locateOrStore(tempTarget);
        return Collections.singleton(new ValueRequirement(ValueRequirementNames.HISTORICAL_TIME_SERIES,
                new ComputationTargetSpecification(TempTarget.TYPE, tempTargetId),
                ValueProperties.withAny(ViewEvaluationFunction.PROPERTY_CALC_CONFIG).get()));
    }

    protected ValueProperties.Builder createValueProperties(final HistoricalViewEvaluationTarget target) {
        final ValueProperties.Builder builder = createValueProperties();
        builder.with(HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY, target.getStartDate());
        builder.with(HistoricalTimeSeriesFunctionUtils.INCLUDE_START_PROPERTY,
                target.isIncludeStart() ? HistoricalTimeSeriesFunctionUtils.YES_VALUE
                        : HistoricalTimeSeriesFunctionUtils.NO_VALUE);
        builder.with(HistoricalTimeSeriesFunctionUtils.END_DATE_PROPERTY, target.getEndDate());
        builder.with(HistoricalTimeSeriesFunctionUtils.INCLUDE_END_PROPERTY,
                target.isIncludeEnd() ? HistoricalTimeSeriesFunctionUtils.YES_VALUE
                        : HistoricalTimeSeriesFunctionUtils.NO_VALUE);
        builder.with(MARKET_DATA_MODE_PROPERTY, target.getMarketDataMode().getConstraintName());
        return builder;
    }

    // TODO: Our declared type of anything means there will never be a parent context, this will probably need fixing

    @Override
    public Set<ValueSpecification> getResults(final FunctionCompilationContext context,
            final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) {
        final TempTarget tempTargetObject = OpenGammaCompilationContext.getTempTargets(context)
                .get(inputs.keySet().iterator().next().getTargetSpecification().getUniqueId());
        if (tempTargetObject instanceof HistoricalViewEvaluationTarget) {
            final HistoricalViewEvaluationTarget historicalTarget = (HistoricalViewEvaluationTarget) tempTargetObject;
            final ViewCalculationConfiguration calcConfig = historicalTarget.getViewDefinition()
                    .getCalculationConfiguration(context.getViewCalculationConfiguration().getName());
            final ExternalIdBundle targetEids;
            if (target.getValue() instanceof ExternalIdentifiable) {
                targetEids = ((ExternalIdentifiable) target.getValue()).getExternalId().toBundle();
            } else if (target.getValue() instanceof ExternalBundleIdentifiable) {
                targetEids = ((ExternalBundleIdentifiable) target.getValue()).getExternalIdBundle();
            } else {
                targetEids = null;
            }
            final ComputationTargetSpecification targetSpec = target.toSpecification();
            final ComputationTargetReference targetContextSpec = target.getContextSpecification();
            final ComputationTargetReferenceVisitor<Set<String>> getTargetType = new ComputationTargetReferenceVisitor<Set<String>>() {

                @Override
                public Set<String> visitComputationTargetRequirement(
                        final ComputationTargetRequirement requirement) {
                    if (target.getUniqueId() == null) {
                        if (requirement.getIdentifiers().isEmpty()) {
                            if (ObjectUtils.equals(requirement.getParent(), targetContextSpec)) {
                                // Null target can be referenced by anything
                                return ImmutableSet.of(TARGET_SPECIFICATION_OBJECT, TARGET_SPECIFICATION_UNIQUE,
                                        TARGET_SPECIFICATION_EXTERNAL);
                            }
                        }
                    } else {
                        if ((targetEids != null) && targetEids.equals(requirement.getIdentifiers())) {
                            if (ObjectUtils.equals(requirement.getParent(), targetContextSpec)) {
                                // Our target
                                return Collections.singleton(TARGET_SPECIFICATION_EXTERNAL);
                            }
                        }
                    }
                    // Not our target
                    return null;
                }

                @Override
                public Set<String> visitComputationTargetSpecification(
                        final ComputationTargetSpecification specification) {
                    if (target.getUniqueId() == null) {
                        if (specification.getUniqueId() == null) {
                            // Null target can be referenced by anything
                            return ImmutableSet.of(TARGET_SPECIFICATION_OBJECT, TARGET_SPECIFICATION_UNIQUE,
                                    TARGET_SPECIFICATION_EXTERNAL);
                        }
                    } else if (target.getUniqueId().isLatest()) {
                        // The target is a primitive - unique and object are the same
                        if (target.getUniqueId().equals(specification.getUniqueId())) {
                            if (ObjectUtils.equals(specification.getParent(), targetContextSpec)) {
                                // Our target
                                return ImmutableSet.of(TARGET_SPECIFICATION_OBJECT, TARGET_SPECIFICATION_UNIQUE);
                            }
                        }
                    } else {
                        if (specification.getUniqueId() != null) {
                            if (specification.getUniqueId().isLatest()) {
                                if (target.getUniqueId().equalObjectId(specification.getUniqueId())) {
                                    if (ObjectUtils.equals(specification.getParent(), targetContextSpec)) {
                                        // Our target at object specification
                                        return Collections.singleton(TARGET_SPECIFICATION_OBJECT);
                                    }
                                }
                            } else {
                                if (target.getUniqueId().equals(specification.getUniqueId())) {
                                    if (ObjectUtils.equals(specification.getParent(), targetContextSpec)) {
                                        // Our target at unique specification
                                        return Collections.singleton(TARGET_SPECIFICATION_UNIQUE);
                                    }
                                }
                            }
                        }
                    }
                    // Not our target
                    return null;
                }

            };
            final Set<ValueSpecification> results = new HashSet<ValueSpecification>();
            for (final ValueRequirement nestedRequirement : calcConfig.getSpecificRequirements()) {
                final Set<String> targetType = nestedRequirement.getTargetReference().accept(getTargetType);
                if (targetType != null) {
                    // The properties on the outputs are based directly on the constraints used to specify the nested view definition. We can't
                    // get the strict value specifications because we don't know how those requirements will compile because we don't know the
                    // valuation date and the graph building behavior of the functions involved might be valuation date dependent - what if the
                    // pricing currency changes over time for example; which do we use for the time series. This isn't always the case, but we'll
                    // ignore the forms where we should know the outcomes to avoid complicating matters. A higher priority function should be
                    // used to enforce any necessary constraints based on the target's properties.
                    final ValueProperties.Builder properties = createValueProperties(historicalTarget);
                    properties.with(VALUE_PROPERTY, nestedRequirement.getValueName());
                    final ValueProperties nestedConstraints = nestedRequirement.getConstraints();
                    if (nestedConstraints.getProperties() != null) {
                        for (final String propertyName : nestedConstraints.getProperties()) {
                            final Set<String> propertyValues = nestedConstraints.getValues(propertyName);
                            final String passthroughName = PASSTHROUGH_PREFIX + propertyName;
                            if (propertyValues == null) {
                                properties.withAny(passthroughName);
                            } else {
                                properties.with(passthroughName, propertyValues);
                            }
                            if (nestedConstraints.isOptional(propertyName)) {
                                properties.withOptional(passthroughName);
                            }
                        }
                    }
                    results.add(new ValueSpecification(ValueRequirementNames.HISTORICAL_TIME_SERIES, targetSpec,
                            properties.get()));
                }
            }
            return results;
        } else {
            return null;
        }
    }

    // FunctionInvoker

    @Override
    public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs,
            final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
        final HistoricalViewEvaluationResult evaluationResult = (HistoricalViewEvaluationResult) inputs
                .getValue(ValueRequirementNames.HISTORICAL_TIME_SERIES);
        final Set<ComputedValue> results = Sets.newHashSetWithExpectedSize(desiredValues.size());
        final ComputationTargetSpecification targetSpec = target.toSpecification();
        for (final ValueRequirement desiredValue : desiredValues) {
            final ValueRequirement requirement = getNestedRequirement(
                    executionContext.getComputationTargetResolver(), target, desiredValue.getConstraints());
            if (requirement != null) {
                @SuppressWarnings("rawtypes")
                final TimeSeries ts = evaluationResult.getTimeSeries(requirement);
                if (ts != null) {
                    results.add(new ComputedValue(new ValueSpecification(desiredValue.getValueName(), targetSpec,
                            desiredValue.getConstraints()), ts));
                } else {
                    s_logger.warn("Nested requirement {} did not produce a time series for {}", requirement,
                            desiredValue);
                }
            } else {
                s_logger.error("Couldn't produce nested requirement for {}", desiredValue);
            }
        }
        return results;
    }

}