/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.model.forex; import java.util.Collections; import java.util.Map; import java.util.Set; import org.threeten.bp.Instant; 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.CompiledFunctionDefinition; 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.financial.OpenGammaCompilationContext; import com.opengamma.financial.analytics.timeseries.DateConstraint; import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesFunctionUtils; import com.opengamma.financial.currency.CurrencyPair; import com.opengamma.financial.currency.CurrencyPairs; import com.opengamma.util.money.Currency; import com.opengamma.util.money.UnorderedCurrencyPair; /** * Function to source FX rates (live, historical, latest historical) based on a quoting convention. It is up to the consumer of the value to recognize the convention and interpret the value * accordingly. * <p> * This function is implemented by querying a value on an ordered currency pair, which will typically be handled by the CurrencyMatrix based functions. */ public class ConventionBasedFXRateFunction extends AbstractFunction { /** * The property name that indicates the convention document that was used. */ private static final String CONVENTION_NAME_PROPERTY = "ConventionConfig"; /** * The property name that indicates the quoting convention, for example "GBP/USD". */ private static final String QUOTING_CONVENTION_PROPERTY = "ConventionQuoting"; private final String _convention; public ConventionBasedFXRateFunction() { this(CurrencyPairs.DEFAULT_CURRENCY_PAIRS); } public ConventionBasedFXRateFunction(final String currencyPairsConvention) { _convention = currencyPairsConvention; } @Override public CompiledFunctionDefinition compile(final FunctionCompilationContext context, final Instant atInstant) { final CurrencyPairs currencyPairs = OpenGammaCompilationContext.getCurrencyPairsSource(context).getCurrencyPairs(_convention); if (currencyPairs == null) { throw new UnsupportedOperationException("No convention called " + _convention + " found"); } return new Compiled(currencyPairs); } /** * The compiled form. */ protected class Compiled extends AbstractInvokingCompiledFunction { private final CurrencyPairs _currencyPairs; public Compiled(final CurrencyPairs currencyPairs) { _currencyPairs = currencyPairs; } @SuppressWarnings("synthetic-access") protected ValueProperties.Builder createValueProperties(final ComputationTarget target) { final ValueProperties.Builder properties = ConventionBasedFXRateFunction.this.createValueProperties(); properties.with(CONVENTION_NAME_PROPERTY, _convention); final UnorderedCurrencyPair unordered = UnorderedCurrencyPair.of(target.getUniqueId()); if (unordered.getFirstCurrency().equals(unordered.getSecondCurrency())) { return null; } final CurrencyPair ordered = _currencyPairs.getCurrencyPair(unordered.getFirstCurrency(), unordered.getSecondCurrency()); if (ordered == null) { return null; } properties.with(QUOTING_CONVENTION_PROPERTY, ordered.toString()); return properties; } private ValueSpecification createSpotRateResult(final ComputationTargetSpecification targetSpec, final ValueProperties properties) { return new ValueSpecification(ValueRequirementNames.SPOT_RATE, targetSpec, properties); } private ValueSpecification createHistoricalTimeSeriesResult(final 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(final ComputationTargetSpecification targetSpec, final ValueProperties properties) { return new ValueSpecification(ValueRequirementNames.HISTORICAL_TIME_SERIES_LATEST, targetSpec, properties); } // CompiledFunctionDefinition @Override public ComputationTargetType getTargetType() { return ComputationTargetType.UNORDERED_CURRENCY_PAIR; } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) { final ValueProperties.Builder propertiesBuilder = createValueProperties(target); if (propertiesBuilder == null) { return null; } final ValueProperties properties = propertiesBuilder.get(); final ComputationTargetSpecification targetSpec = target.toSpecification(); 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 ValueProperties constraints; if (desiredValue.getConstraints().getProperties() != null) { final ValueProperties.Builder constraintsBuilder = ValueProperties.builder(); for (final String constraintName : desiredValue.getConstraints().getProperties()) { if (ValuePropertyNames.FUNCTION.equals(constraintName) || constraintName.startsWith(ValuePropertyNames.OUTPUT_RESERVED_PREFIX) || QUOTING_CONVENTION_PROPERTY.equals(constraintName)) { continue; } final Set<String> values = desiredValue.getConstraints().getValues(constraintName); if (values.isEmpty()) { constraintsBuilder.withAny(constraintName); } else { constraintsBuilder.with(constraintName, values); } if (desiredValue.getConstraints().isOptional(constraintName)) { constraintsBuilder.withOptional(constraintName); } } constraints = constraintsBuilder.get(); } else { constraints = ValueProperties.none(); } final UnorderedCurrencyPair unordered = UnorderedCurrencyPair.of(target.getUniqueId()); final CurrencyPair ordered = _currencyPairs.getCurrencyPair(unordered.getFirstCurrency(), unordered.getSecondCurrency()); return Collections.singleton(new ValueRequirement(desiredValue.getValueName(), CurrencyPair.TYPE.specification(ordered), constraints)); } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) { final Set<ValueSpecification> results = Sets.newHashSetWithExpectedSize(inputs.size()); final ComputationTargetSpecification targetSpec = target.toSpecification(); for (final ValueSpecification input : inputs.keySet()) { final ValueProperties.Builder properties = createValueProperties(target); final ValueProperties inputProperties = input.getProperties(); for (final String propertyName : inputProperties.getProperties()) { if (!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(input.getValueName(), targetSpec, properties.get())); } return results; } // FunctionInvoker @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 (final 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 static ValueRequirement getSpotRateRequirement(final UnorderedCurrencyPair currencies) { return new ValueRequirement(ValueRequirementNames.SPOT_RATE, ComputationTargetType.UNORDERED_CURRENCY_PAIR.specification(currencies)); } public static ValueRequirement getSpotRateRequirement(final Currency currency1, final Currency currency2) { return getSpotRateRequirement(UnorderedCurrencyPair.of(currency1, currency2)); } public static ValueRequirement getHistoricalTimeSeriesRequirement(final UnorderedCurrencyPair currencies) { return getHistoricalTimeSeriesRequirement(currencies, DateConstraint.NULL, true, DateConstraint.VALUATION_TIME, true); } public static ValueRequirement getHistoricalTimeSeriesRequirement(final Currency currency1, final Currency currency2) { return getHistoricalTimeSeriesRequirement(UnorderedCurrencyPair.of(currency1, currency2)); } public static ValueRequirement getHistoricalTimeSeriesRequirement(final UnorderedCurrencyPair currencies, final DateConstraint startDate, final boolean includeStart, final DateConstraint endDate, final boolean includeEnd) { return new ValueRequirement(ValueRequirementNames.HISTORICAL_FX_TIME_SERIES, ComputationTargetType.UNORDERED_CURRENCY_PAIR.specification(currencies), HistoricalTimeSeriesFunctionUtils .htsConstraints(ValueProperties.builder(), startDate, includeStart, endDate, includeEnd).get()); } public static ValueRequirement getHistoricalTimeSeriesRequirement(final Currency currency1, final Currency currency2, final DateConstraint startDate, final boolean includeStart, final DateConstraint endDate, final boolean includeEnd) { return getHistoricalTimeSeriesRequirement(UnorderedCurrencyPair.of(currency1, currency2), startDate, includeStart, endDate, includeEnd); } public static ValueRequirement getLatestHistoricalRequirement(final UnorderedCurrencyPair currencies) { return new ValueRequirement(ValueRequirementNames.HISTORICAL_TIME_SERIES_LATEST, ComputationTargetType.UNORDERED_CURRENCY_PAIR.specification(currencies)); } public static ValueRequirement getLatestHistoricalRequirement(final Currency currency1, final Currency currency2) { return getLatestHistoricalRequirement(UnorderedCurrencyPair.of(currency1, currency2)); } }