/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model.curve.forward;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.threeten.bp.Instant;
import com.google.common.collect.Iterables;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurveYieldImplied;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve;
import com.opengamma.engine.ComputationTarget;
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.model.forex.ConventionBasedFXRateFunction;
import com.opengamma.financial.currency.CurrencyPair;
import com.opengamma.financial.currency.CurrencyPairs;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.UnorderedCurrencyPair;
/**
*
*/
public class FXForwardCurveFromYieldCurvesFunction extends AbstractFunction {
@Override
public CompiledFunctionDefinition compile(final FunctionCompilationContext context, final Instant atInstant) {
final CurrencyPairs baseQuotePairs = OpenGammaCompilationContext.getCurrencyPairsSource(context).getCurrencyPairs(CurrencyPairs.DEFAULT_CURRENCY_PAIRS);
return new Compiled(baseQuotePairs);
}
/**
* Compiled form.
*/
protected class Compiled extends AbstractInvokingCompiledFunction {
private final CurrencyPairs _baseQuotePairs;
public Compiled(final CurrencyPairs baseQuotePairs) {
_baseQuotePairs = baseQuotePairs;
}
// CompiledFunctionDefinition
@Override
public ComputationTargetType getTargetType() {
return ComputationTargetType.UNORDERED_CURRENCY_PAIR;
}
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
final ValueProperties properties = createValueProperties()
.withAny(ValuePropertyNames.CURVE)
.with(ForwardCurveValuePropertyNames.PROPERTY_FORWARD_CURVE_CALCULATION_METHOD, ForwardCurveValuePropertyNames.PROPERTY_YIELD_CURVE_IMPLIED_METHOD)
.withAny(ValuePropertyNames.PAY_CURVE)
.withAny(ValuePropertyNames.PAY_CURVE_CALCULATION_CONFIG)
.withAny(ValuePropertyNames.RECEIVE_CURVE)
.withAny(ValuePropertyNames.RECEIVE_CURVE_CALCULATION_CONFIG)
.get();
final ValueSpecification spec = new ValueSpecification(ValueRequirementNames.FORWARD_CURVE, target.toSpecification(), properties);
return Collections.singleton(spec);
}
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
final ValueProperties constraints = desiredValue.getConstraints();
final Set<String> fxForwardCurveNames = constraints.getValues(ValuePropertyNames.CURVE);
if (fxForwardCurveNames == null || fxForwardCurveNames.size() != 1) {
return null;
}
final Set<String> payCurveNames = constraints.getValues(ValuePropertyNames.PAY_CURVE);
if (payCurveNames == null || payCurveNames.size() != 1) {
return null;
}
final Set<String> payCurveCalculationConfigs = constraints.getValues(ValuePropertyNames.PAY_CURVE_CALCULATION_CONFIG);
if (payCurveCalculationConfigs == null || payCurveCalculationConfigs.size() != 1) {
return null;
}
final Set<String> receiveCurveNames = constraints.getValues(ValuePropertyNames.RECEIVE_CURVE);
if (receiveCurveNames == null || receiveCurveNames.size() != 1) {
return null;
}
final Set<String> receiveCurveCalculationConfigs = constraints.getValues(ValuePropertyNames.RECEIVE_CURVE_CALCULATION_CONFIG);
if (receiveCurveCalculationConfigs == null || receiveCurveCalculationConfigs.size() != 1) {
return null;
}
final Set<ValueRequirement> result = new HashSet<ValueRequirement>();
final ValueProperties payCurveProperties = ValueProperties.builder()
.with(ValuePropertyNames.CURVE, Iterables.getOnlyElement(payCurveNames))
.with(ValuePropertyNames.CURVE_CALCULATION_CONFIG, Iterables.getOnlyElement(payCurveCalculationConfigs))
.withOptional(ValuePropertyNames.PAY_CURVE)
.get();
final ValueProperties receiveCurveProperties = ValueProperties.builder()
.with(ValuePropertyNames.CURVE, Iterables.getOnlyElement(receiveCurveNames))
.with(ValuePropertyNames.CURVE_CALCULATION_CONFIG, Iterables.getOnlyElement(receiveCurveCalculationConfigs))
.withOptional(ValuePropertyNames.RECEIVE_CURVE)
.get();
final UnorderedCurrencyPair ccyPair = target.getValue(ComputationTargetType.UNORDERED_CURRENCY_PAIR);
Currency payCurrency;
Currency receiveCurrency;
final CurrencyPair baseQuotePair = _baseQuotePairs.getCurrencyPair(ccyPair.getFirstCurrency(), ccyPair.getSecondCurrency());
if (baseQuotePair == null) {
throw new OpenGammaRuntimeException("Could not get base/quote pair for currency pair " + ccyPair);
}
if (baseQuotePair.getBase().equals(ccyPair.getFirstCurrency())) {
payCurrency = baseQuotePair.getBase();
receiveCurrency = baseQuotePair.getCounter();
} else {
payCurrency = baseQuotePair.getCounter();
receiveCurrency = baseQuotePair.getBase();
}
result.add(ConventionBasedFXRateFunction.getSpotRateRequirement(ccyPair));
result.add(new ValueRequirement(ValueRequirementNames.YIELD_CURVE, ComputationTargetType.CURRENCY.specification(payCurrency), payCurveProperties));
result.add(new ValueRequirement(ValueRequirementNames.YIELD_CURVE, ComputationTargetType.CURRENCY.specification(receiveCurrency), receiveCurveProperties));
return result;
}
// FunctionInvoker
@Override
public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
Object payCurveObject = null;
Object receiveCurveObject = null;
Set<String> payCurveNames = null;
Set<String> payCurveCalculationConfigs = null;
Set<String> receiveCurveNames = null;
Set<String> receiveCurveCalculationConfigs = null;
final UnorderedCurrencyPair ccyPair = UnorderedCurrencyPair.of(target.getUniqueId());
Currency payCurrency;
Currency receiveCurrency;
final CurrencyPair baseQuotePair = _baseQuotePairs.getCurrencyPair(ccyPair.getFirstCurrency(), ccyPair.getSecondCurrency());
if (baseQuotePair == null) {
throw new OpenGammaRuntimeException("Could not get base/quote pair for currency pair " + ccyPair);
}
if (baseQuotePair.getBase().equals(ccyPair.getFirstCurrency())) {
payCurrency = baseQuotePair.getBase();
receiveCurrency = baseQuotePair.getCounter();
} else {
payCurrency = baseQuotePair.getCounter();
receiveCurrency = baseQuotePair.getBase();
}
final Double spot = (Double) inputs.getValue(ValueRequirementNames.SPOT_RATE);
for (final ComputedValue input : inputs.getAllValues()) {
final ValueSpecification spec = input.getSpecification();
final ValueProperties properties = spec.getProperties();
if (spec.getTargetSpecification().getUniqueId().equals(payCurrency.getUniqueId())) {
payCurveObject = input.getValue();
payCurveNames = properties.getValues(ValuePropertyNames.CURVE);
payCurveCalculationConfigs = properties.getValues(ValuePropertyNames.CURVE_CALCULATION_CONFIG);
} else if (spec.getTargetSpecification().getUniqueId().equals(receiveCurrency.getUniqueId())) {
receiveCurveObject = input.getValue();
receiveCurveNames = properties.getValues(ValuePropertyNames.CURVE);
receiveCurveCalculationConfigs = properties.getValues(ValuePropertyNames.CURVE_CALCULATION_CONFIG);
}
}
if (payCurveObject == null) {
throw new OpenGammaRuntimeException("Could not get pay curve");
}
if (receiveCurveObject == null) {
throw new OpenGammaRuntimeException("Could not get receive curve");
}
if (payCurveNames == null || payCurveNames.size() != 1) {
throw new OpenGammaRuntimeException("Null or non-unique curve name: " + payCurveNames);
}
if (receiveCurveNames == null || receiveCurveNames.size() != 1) {
throw new OpenGammaRuntimeException("Null or non-unique curve name: " + receiveCurveNames);
}
final ValueRequirement desiredValue = desiredValues.iterator().next();
final Set<String> fxForwardCurveNames = desiredValue.getConstraints().getValues(ValuePropertyNames.CURVE);
if (fxForwardCurveNames == null || fxForwardCurveNames.size() != 1) {
throw new OpenGammaRuntimeException("Null or non-unique FX forward curve names: " + fxForwardCurveNames);
}
final YieldCurve payCurve = (YieldCurve) payCurveObject;
final YieldCurve receiveCurve = (YieldCurve) receiveCurveObject;
final String fxForwardCurveName = fxForwardCurveNames.iterator().next();
final ForwardCurve fxForwardCurve = new ForwardCurveYieldImplied(spot, payCurve, receiveCurve);
final ValueProperties properties = createValueProperties()
.with(ValuePropertyNames.CURVE, fxForwardCurveName)
.with(ForwardCurveValuePropertyNames.PROPERTY_FORWARD_CURVE_CALCULATION_METHOD, ForwardCurveValuePropertyNames.PROPERTY_YIELD_CURVE_IMPLIED_METHOD)
.with(ValuePropertyNames.PAY_CURVE, Iterables.getOnlyElement(payCurveNames))
.with(ValuePropertyNames.PAY_CURVE_CALCULATION_CONFIG, Iterables.getOnlyElement(payCurveCalculationConfigs))
.with(ValuePropertyNames.RECEIVE_CURVE, Iterables.getOnlyElement(receiveCurveNames))
.with(ValuePropertyNames.RECEIVE_CURVE_CALCULATION_CONFIG, Iterables.getOnlyElement(receiveCurveCalculationConfigs))
.get();
final ValueSpecification resultSpec = new ValueSpecification(ValueRequirementNames.FORWARD_CURVE, target.toSpecification(), properties);
return Collections.singleton(new ComputedValue(resultSpec, fxForwardCurve));
}
}
}