/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.view;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.core.position.PortfolioNode;
import com.opengamma.core.position.Position;
import com.opengamma.engine.ComputationTarget;
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.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.ViewCalculationConfiguration.MergedOutput;
import com.opengamma.financial.analytics.DoubleCurrencyLabelledMatrix2D;
import com.opengamma.financial.analytics.DoubleLabelledMatrix1D;
import com.opengamma.financial.analytics.SumUtils;
import com.opengamma.util.async.AsynchronousExecution;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.CurrencyAmount;
/**
* Calculates simple, linear aggregates at the portfolio node level over merged outputs.
*/
public class MergedOutputLinearAggregationPortfolioNodeFunction extends AbstractFunction.NonCompiledInvoker {
@Override
public ComputationTargetType getTargetType() {
return ComputationTargetType.PORTFOLIO_NODE;
}
@Override
public Set<ValueSpecification> getResults(FunctionCompilationContext context, ComputationTarget target) {
return ImmutableSet.of(ValueSpecification.of(ValueRequirementNames.MERGED_OUTPUT, target.toSpecification(), ValueProperties.all()));
}
@Override
public Set<ValueRequirement> getRequirements(FunctionCompilationContext context, ComputationTarget target, ValueRequirement desiredValue) {
String mergedOutputName = desiredValue.getConstraint(ValuePropertyNames.NAME);
ViewCalculationConfiguration calcConfig = context.getViewCalculationConfiguration();
MergedOutput mergedOutput = calcConfig.getMergedOutput(mergedOutputName);
if (mergedOutput == null) {
return null;
}
final Set<ValueRequirement> requirements = new HashSet<ValueRequirement>();
final PortfolioNode node = target.getPortfolioNode();
for (final Position position : node.getPositions()) {
requirements.add(new ValueRequirement(ValueRequirementNames.MERGED_OUTPUT, ComputationTargetType.POSITION, position.getUniqueId(), desiredValue.getConstraints()));
}
for (final PortfolioNode childNode : node.getChildNodes()) {
requirements.add(new ValueRequirement(ValueRequirementNames.MERGED_OUTPUT, ComputationTargetType.PORTFOLIO_NODE, childNode.getUniqueId(), desiredValue.getConstraints()));
}
return requirements;
}
@Override
public Set<ValueSpecification> getResults(FunctionCompilationContext context, ComputationTarget target, Map<ValueSpecification, ValueRequirement> inputs) {
ValueRequirement exampleInput = Iterables.getFirst(inputs.values(), null);
if (exampleInput == null) {
return null;
}
String mergedOutputName = exampleInput.getConstraint(ValuePropertyNames.NAME);
ValueProperties properties = createValueProperties().with(ValuePropertyNames.NAME, mergedOutputName).get();
return ImmutableSet.of(ValueSpecification.of(ValueRequirementNames.MERGED_OUTPUT, target.toSpecification(), properties));
}
@Override
public Set<ComputedValue> execute(FunctionExecutionContext executionContext, FunctionInputs inputs, ComputationTarget target, Set<ValueRequirement> desiredValues) throws AsynchronousExecution {
// TODO jonathan 2014-01-13 -- as a proof-of-concept this supports aggregating very specific types. It should be
// extended as required.
ValueRequirement desiredValue = Iterables.getOnlyElement(desiredValues);
String mergedOutputName = desiredValue.getConstraint(ValuePropertyNames.NAME);
Object value = null;
for (final ComputedValue input : inputs.getAllValues()) {
Object inputValue = input.getValue();
if (inputValue instanceof String) {
if (((String) inputValue).length() == 0) {
continue;
}
}
String ccyCode = input.getSpecification().getProperty(ValuePropertyNames.CURRENCY);
Currency ccy = ccyCode != null ? Currency.parse(ccyCode) : null;
if (ccy != null) {
// When merging outputs we want to allow aggregation across currencies, so we add another dimension to known
// data structures to include the currency
if (inputValue instanceof Double) {
inputValue = CurrencyAmount.of(ccy, (double) inputValue);
} else if (inputValue instanceof DoubleLabelledMatrix1D) {
DoubleLabelledMatrix1D inputMatrix = (DoubleLabelledMatrix1D) inputValue;
// TODO jonathan 2014-01-08 -- an optional constraint should control whether we aggregate by key or label
// We default to this since we usually want to see the matrix aggregated across currencies by label (e.g. 7D)
// rather than repeating labels where the keys are slightly different (e.g. due to holiday differences)
if (value != null && !(value instanceof DoubleCurrencyLabelledMatrix2D)) {
throw new OpenGammaRuntimeException("Unable to aggregate " + value.getClass() + " with " + DoubleCurrencyLabelledMatrix2D.class);
}
inputValue = new DoubleCurrencyLabelledMatrix2D(
inputMatrix.getKeys(), inputMatrix.getLabels(), inputMatrix.getLabelsTitle(),
new Currency[] {ccy}, new Currency[] {ccy}, ValuePropertyNames.CURRENCY,
new double[][] {inputMatrix.getValues()}, inputMatrix.getValuesTitle());
}
}
String requirementDisplayName = ValueRequirementNames.MERGED_OUTPUT + " (" + mergedOutputName + ")";
value = addValue(value, inputValue, requirementDisplayName);
}
if (value == null) {
value = "";
}
return Collections.singleton(new ComputedValue(new ValueSpecification(ValueRequirementNames.MERGED_OUTPUT, target.toSpecification(), desiredValue.getConstraints()), value));
}
protected Object addValue(Object previousSum, Object currentValue, String requirementDisplayName) {
if (previousSum == null) {
return currentValue;
}
if (previousSum instanceof DoubleCurrencyLabelledMatrix2D && currentValue instanceof DoubleCurrencyLabelledMatrix2D) {
DoubleCurrencyLabelledMatrix2D previousSumMatrix = (DoubleCurrencyLabelledMatrix2D) previousSum;
DoubleCurrencyLabelledMatrix2D currentValueMatrix = (DoubleCurrencyLabelledMatrix2D) currentValue;
return previousSumMatrix.addUsingDoubleLabels(currentValueMatrix);
}
return SumUtils.addValue(previousSum, currentValue, requirementDisplayName);
}
@Override
public boolean canHandleMissingInputs() {
return true;
}
}