/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.currency; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; 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.ComputationTargetRequirement; 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.timeseries.HistoricalTimeSeriesFunctionUtils; import com.opengamma.id.ExternalId; import com.opengamma.id.ExternalIdBundle; /** * Rewrites a requirement to satisfy the currency requirements generated by {@link CurrencyConversionFunction} into one that will query a {@link CurrencyMatrix}. */ public class CurrencyMatrixLookupFunction extends AbstractFunction.NonCompiledInvoker { /** * Property name when applied to a {@link CurrencyPair} target to allow the matrix to be selected. */ protected static final String CURRENCY_MATRIX_NAME_PROPERTY = "CurrencyMatrix"; private final String _defaultCurrencyMatrixName; private final String[] _additionalProperties; public CurrencyMatrixLookupFunction(final String defaultCurrencyMatrixName) { _defaultCurrencyMatrixName = defaultCurrencyMatrixName; _additionalProperties = null; } public CurrencyMatrixLookupFunction(final String[] params) { _defaultCurrencyMatrixName = params[0]; _additionalProperties = new String[params.length - 1]; System.arraycopy(params, 1, _additionalProperties, 0, _additionalProperties.length); } @Override protected ValueProperties.Builder createValueProperties() { final ValueProperties.Builder properties = super.createValueProperties(); if (_additionalProperties != null) { for (int i = 0; i < _additionalProperties.length; i += 2) { properties.with(_additionalProperties[i], _additionalProperties[i + 1]); } } return properties; } protected String getDefaultCurrencyMatrixName() { return _defaultCurrencyMatrixName; } @Override public ComputationTargetType getTargetType() { return CurrencyPair.TYPE; } private ValueSpecification createSpotRateResult(ComputationTargetSpecification targetSpec, ValueProperties properties) { return new ValueSpecification(ValueRequirementNames.SPOT_RATE, targetSpec, properties); } private ValueSpecification createHistoricalTimeSeriesResult(ComputationTargetSpecification targetSpec, ValueProperties properties) { properties = properties.copy() .withAny(HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY) .with(HistoricalTimeSeriesFunctionUtils.INCLUDE_START_PROPERTY, HistoricalTimeSeriesFunctionUtils.NO_VALUE, HistoricalTimeSeriesFunctionUtils.YES_VALUE) .withAny(HistoricalTimeSeriesFunctionUtils.END_DATE_PROPERTY) .with(HistoricalTimeSeriesFunctionUtils.INCLUDE_END_PROPERTY, HistoricalTimeSeriesFunctionUtils.NO_VALUE, HistoricalTimeSeriesFunctionUtils.YES_VALUE).get(); return new ValueSpecification(ValueRequirementNames.HISTORICAL_FX_TIME_SERIES, targetSpec, properties); } private ValueSpecification createTimeSeriesLatestResult(ComputationTargetSpecification targetSpec, ValueProperties properties) { return new ValueSpecification(ValueRequirementNames.HISTORICAL_TIME_SERIES_LATEST, targetSpec, properties); } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) { final ComputationTargetSpecification targetSpec = target.toSpecification(); final ValueProperties properties = createValueProperties().withAny(CURRENCY_MATRIX_NAME_PROPERTY).get(); return ImmutableSet.of(createSpotRateResult(targetSpec, properties), createHistoricalTimeSeriesResult(targetSpec, properties), createTimeSeriesLatestResult(targetSpec, properties)); } @Override public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) { final CurrencyPair currencies = (CurrencyPair) target.getValue(); final ValueProperties.Builder constraints = ValueProperties .with(AbstractCurrencyMatrixSourcingFunction.SOURCE_CURRENCY_PROPERTY, currencies.getCounter().getCode()) .with(AbstractCurrencyMatrixSourcingFunction.TARGET_CURRENCY_PROPERTY, currencies.getBase().getCode()); ExternalIdBundle matrixIdentifiers = null; if (desiredValue.getConstraints().getProperties() != null) { for (String constraintName : desiredValue.getConstraints().getProperties()) { if (ValuePropertyNames.FUNCTION.equals(constraintName) || constraintName.startsWith(ValuePropertyNames.OUTPUT_RESERVED_PREFIX)) { continue; } final Set<String> values = desiredValue.getConstraints().getValues(constraintName); if (CURRENCY_MATRIX_NAME_PROPERTY.equals(constraintName)) { if (values.isEmpty()) { matrixIdentifiers = ExternalId.of(CurrencyMatrixResolver.IDENTIFIER_SCHEME, getDefaultCurrencyMatrixName()).toBundle(); } else { if (values.size() == 1) { matrixIdentifiers = ExternalId.of(CurrencyMatrixResolver.IDENTIFIER_SCHEME, values.iterator().next()).toBundle(); } else { final Collection<ExternalId> identifiers = new ArrayList<ExternalId>(values.size()); for (String matrixName : values) { identifiers.add(ExternalId.of(CurrencyMatrixResolver.IDENTIFIER_SCHEME, matrixName)); } matrixIdentifiers = ExternalIdBundle.of(identifiers); } } } else { if (values.isEmpty()) { constraints.withAny(constraintName); } else { constraints.with(constraintName, values); } if (desiredValue.getConstraints().isOptional(constraintName)) { constraints.withOptional(constraintName); } } } } if (matrixIdentifiers == null) { matrixIdentifiers = ExternalId.of(CurrencyMatrixResolver.IDENTIFIER_SCHEME, getDefaultCurrencyMatrixName()).toBundle(); } return Collections.singleton(new ValueRequirement(desiredValue.getValueName(), new ComputationTargetRequirement(CurrencyMatrixResolver.TYPE, matrixIdentifiers), constraints.get())); } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) { final Set<ValueSpecification> results = Sets.newHashSetWithExpectedSize(inputs.size()); for (Map.Entry<ValueSpecification, ValueRequirement> inputEntry : inputs.entrySet()) { ValueProperties.Builder properties = createValueProperties(); properties.with(CURRENCY_MATRIX_NAME_PROPERTY, inputEntry.getValue().getTargetReference().getRequirement().getIdentifiers().getValue(CurrencyMatrixResolver.IDENTIFIER_SCHEME)); final ValueProperties inputProperties = inputEntry.getKey().getProperties(); for (String propertyName : inputProperties.getProperties()) { if (!AbstractCurrencyMatrixSourcingFunction.SOURCE_CURRENCY_PROPERTY.equals(propertyName) && !AbstractCurrencyMatrixSourcingFunction.TARGET_CURRENCY_PROPERTY.equals(propertyName) && !ValuePropertyNames.FUNCTION.equals(propertyName)) { final Set<String> values = inputProperties.getValues(propertyName); if (values.isEmpty()) { properties.withAny(propertyName); } else { properties.with(propertyName, values); } } } results.add(new ValueSpecification(inputEntry.getKey().getValueName(), target.toSpecification(), properties.get())); } return results; } @Override public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) { final Set<ComputedValue> results = Sets.newHashSetWithExpectedSize(desiredValues.size()); for (ValueRequirement desiredValue : desiredValues) { final Object input = inputs.getValue(desiredValue.getValueName()); results.add(new ComputedValue(new ValueSpecification(desiredValue.getValueName(), target.toSpecification(), desiredValue.getConstraints()), input)); } return results; } public int getPriority() { if (getDefaultCurrencyMatrixName().contains(CurrencyMatrixConfigPopulator.SYNTHETIC_LIVE_DATA)) { return -1; } return 0; } }