/** * 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.HashSet; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries; import com.opengamma.engine.function.FunctionCompilationContext; import com.opengamma.engine.function.FunctionExecutionContext; import com.opengamma.engine.function.FunctionInputs; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.value.ValueRequirementNames; import com.opengamma.financial.currency.CurrencyMatrixValue.CurrencyMatrixCross; import com.opengamma.financial.currency.CurrencyMatrixValue.CurrencyMatrixFixed; import com.opengamma.financial.currency.CurrencyMatrixValue.CurrencyMatrixValueRequirement; import com.opengamma.timeseries.DoubleTimeSeries; import com.opengamma.util.money.Currency; import com.opengamma.util.tuple.Pair; import com.opengamma.util.tuple.Pairs; /** * Injects a value from a {@link CurrencyMatrix} into a dependency graph to satisfy the currency requirements generated by {@link CurrencyConversionFunction}. */ public class CurrencyMatrixSpotSourcingFunction extends AbstractCurrencyMatrixSourcingFunction { private static final Logger s_logger = LoggerFactory.getLogger(CurrencyMatrixSpotSourcingFunction.class); public CurrencyMatrixSpotSourcingFunction() { super(ValueRequirementNames.SPOT_RATE); } private boolean getRequirements(final CurrencyMatrix matrix, final Set<ValueRequirement> requirements, final Set<Pair<Currency, Currency>> visited, final Pair<Currency, Currency> currencies) { if (!visited.add(currencies)) { // Gone round in a loop if we've already seen this pair throw new IllegalStateException(); } final CurrencyMatrixValue value = matrix.getConversion(currencies.getFirst(), currencies.getSecond()); if (value != null) { return value.accept(new CurrencyMatrixValueVisitor<Boolean>() { @Override public Boolean visitCross(final CurrencyMatrixCross cross) { return getRequirements(matrix, requirements, visited, Pairs.of(currencies.getFirst(), cross.getCrossCurrency())) && getRequirements(matrix, requirements, visited, Pairs.of(cross.getCrossCurrency(), currencies.getSecond())); } @Override public Boolean visitFixed(final CurrencyMatrixFixed fixedValue) { // Literal value - nothing required return Boolean.TRUE; } @Override public Boolean visitValueRequirement(final CurrencyMatrixValueRequirement valueRequirement) { requirements.add(tagInput(valueRequirement.getValueRequirement(), currencies.getFirst(), currencies.getSecond())); return Boolean.TRUE; } }); } else { return false; } } @Override protected boolean getRequirements(final FunctionCompilationContext context, final ValueRequirement desiredValue, final CurrencyMatrix matrix, final Set<ValueRequirement> requirements, final Currency source, final Currency target) { return getRequirements(matrix, requirements, new HashSet<Pair<Currency, Currency>>(), Pairs.of(source, target)); } private Object getRate(final CurrencyMatrix matrix, final FunctionInputs inputs, final Currency source, final Currency target) { final CurrencyMatrixValue value = matrix.getConversion(source, target); final Object rate = value.accept(new CurrencyMatrixValueVisitor<Object>() { @Override public Object visitCross(final CurrencyMatrixCross cross) { final Object r1 = getRate(matrix, inputs, source, cross.getCrossCurrency()); final Object r2 = getRate(matrix, inputs, cross.getCrossCurrency(), target); return createCrossRate(r1, r2); } @Override public Object visitFixed(final CurrencyMatrixFixed fixedValue) { return fixedValue.getFixedValue(); } @Override public Object visitValueRequirement(final CurrencyMatrixValueRequirement valueRequirement) { final Object marketValue = inputs.getValue(valueRequirement.getValueRequirement()); if (marketValue instanceof Number) { double rate = ((Number) marketValue).doubleValue(); if (valueRequirement.isReciprocal()) { rate = 1.0 / rate; } return rate; } else if (marketValue instanceof DoubleTimeSeries) { DoubleTimeSeries<?> rate = (DoubleTimeSeries<?>) marketValue; if (valueRequirement.isReciprocal()) { rate = rate.reciprocal(); } return rate; } else if (marketValue instanceof HistoricalTimeSeries) { DoubleTimeSeries<?> rate = ((HistoricalTimeSeries) marketValue).getTimeSeries(); if (valueRequirement.isReciprocal()) { rate = rate.reciprocal(); } return rate; } else { if (marketValue == null) { // Missing input case; reported elsewhere return null; } throw new IllegalArgumentException(valueRequirement.toString()); } } }); s_logger.debug("{} to {} = {}", new Object[] {source, target, rate }); return rate; } @Override protected Object getRate(final CurrencyMatrix matrix, final ValueRequirement desiredValue, final FunctionExecutionContext executionContext, final FunctionInputs inputs, final Currency source, final Currency target) { return getRate(matrix, inputs, source, target); } /** * Creates a requirement that will supply a value which gives the number of units of the source currency for each unit of the target currency. * * @param source the source currency to convert from * @param target the target currency to convert to * @return the requirement, not null */ public static ValueRequirement getConversionRequirement(final Currency source, final Currency target) { //TODO is the reversal of the inputs intentional? // Yes - pending a thorough change across all currency matrix based code. Everything that refers to "source" and "target" is unfortunately back to front. // "Source" should really have been "counter", and "target" should really have been "base". The ordering would then be reversed. [PLAT-3453] return new ValueRequirement(ValueRequirementNames.SPOT_RATE, CurrencyPair.TYPE.specification(CurrencyPair.of(target, source))); } /** * Creates a requirement that will supply a value which gives the number of units of the source currency for each unit of the target currency. * * @param source the source currency to convert from * @param target the target currency to convert to * @return the requirement, not null */ public static Set<ValueRequirement> getConversionRequirements(final Currency source, final Currency target) { //TODO is the reversal of the inputs intentional? final Set<ValueRequirement> requirements = new HashSet<>(); requirements.add(new ValueRequirement(ValueRequirementNames.SPOT_RATE, CurrencyPair.TYPE.specification(CurrencyPair.of(target, source)))); requirements.add(new ValueRequirement(ValueRequirementNames.SPOT_RATE, CurrencyPair.TYPE.specification(CurrencyPair.of(source, target)))); return requirements; } /** * Creates a requirement that will supply a value which gives the number of units of the source currency for each unit of the target currency. * * @param source the source currency to convert from * @param target the target currency to convert to * @return the requirement, not null */ public static ValueRequirement getConversionRequirement(final String source, final String target) { return getConversionRequirement(Currency.of(source), Currency.of(target)); } }