/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics; import java.util.Collections; import java.util.Map; import java.util.Set; import com.google.common.collect.ImmutableSet; import com.opengamma.engine.ComputationTarget; 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.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.property.UnitProperties; import com.opengamma.id.UniqueId; /** * */ public abstract class AbstractWeightFunction extends AbstractFunction.NonCompiledInvoker { /** * The property name that describes the value used to construct the weight. The full set of output properties will include those of the target and its parent node. */ public static final String VALUE_PROPERTY_NAME = "Value"; /** * Default to the generic value, for example FAIR_VALUE or PRESENT_VALUE depending on the asset classes. */ private static final String DEFAULT_VALUE_NAME = ValueRequirementNames.VALUE; @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) { return Collections.singleton(new ValueSpecification(ValueRequirementNames.WEIGHT, target.toSpecification(), ValueProperties.all())); } @Override public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) { final ValueProperties constraints = desiredValue.getConstraints(); Set<String> values = constraints.getValues(VALUE_PROPERTY_NAME); final String inputValue; if ((values == null) || values.isEmpty()) { inputValue = DEFAULT_VALUE_NAME; } else if (values.size() == 1) { inputValue = values.iterator().next(); } else { return null; } // Propogate the desired value constraints onto the requirements, removing those specific to this function and adding a unit homogeneity clause final ValueProperties.Builder requirementConstraintsBuilder = constraints.copy().withoutAny(VALUE_PROPERTY_NAME); for (String unit : UnitProperties.unitPropertyNames()) { values = constraints.getValues(unit); if (values == null) { // Unit was not specified on the output, but we specify it on the inputs so we can check homogeneity to ensure the division is valid requirementConstraintsBuilder.withOptional(unit); } } // Request value on the value and parent final ValueProperties requirementConstraints = requirementConstraintsBuilder.get(); return ImmutableSet.of(new ValueRequirement(inputValue, getValueTarget(target), requirementConstraints), new ValueRequirement(inputValue, getParentTarget(target), requirementConstraints)); } protected abstract ComputationTargetSpecification getValueTarget(final ComputationTarget target); protected abstract ComputationTargetReference getParentTarget(final ComputationTarget target); @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) { ValueSpecification inputValue = null; ValueSpecification inputParent = null; final UniqueId value = target.getUniqueId(); for (ValueSpecification input : inputs.keySet()) { if (value.equals(input.getTargetSpecification().getUniqueId())) { assert inputValue == null; inputValue = input; } else { assert inputParent == null; inputParent = input; } } final ValueProperties rawResultProperties = inputValue.getProperties().intersect(inputParent.getProperties()); final ValueProperties.Builder resultPropertiesBuilder = rawResultProperties.copy(); for (String unit : UnitProperties.unitPropertyNames()) { final Set<String> valueUnits = inputValue.getProperties().getValues(unit); final Set<String> parentUnits = inputParent.getProperties().getValues(unit); if (valueUnits != null) { if (parentUnits != null) { if (rawResultProperties.getValues(unit) != null) { // The operation is a division, so there are no units on the result resultPropertiesBuilder.withoutAny(unit); } else { // No common intersection between parent and value properties for this unit return null; } } else { // Parent did not include the same units as the value return null; } } else { if (parentUnits != null) { // Value did not include the same units as the parent return null; } } } resultPropertiesBuilder.withoutAny(VALUE_PROPERTY_NAME).with(VALUE_PROPERTY_NAME, inputValue.getValueName()); resultPropertiesBuilder.withoutAny(ValuePropertyNames.FUNCTION).with(ValuePropertyNames.FUNCTION, getUniqueId()); return Collections.singleton(new ValueSpecification(ValueRequirementNames.WEIGHT, target.toSpecification(), resultPropertiesBuilder.get())); } @Override public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) { double parentValue = 0; double targetValue = 0; final UniqueId targetIdentifier = target.getUniqueId(); for (final ComputedValue c : inputs.getAllValues()) { if (targetIdentifier.equals(c.getSpecification().getTargetSpecification().getUniqueId())) { targetValue = (Double) c.getValue(); } else { parentValue = (Double) c.getValue(); } } final ValueRequirement desiredValue = desiredValues.iterator().next(); return Collections.singleton(new ComputedValue(new ValueSpecification(ValueRequirementNames.WEIGHT, target.toSpecification(), desiredValue.getConstraints()), targetValue / parentValue)); } }