/**
* 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));
}
}