/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.model.forex.forward; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.Clock; import org.threeten.bp.ZonedDateTime; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.analytics.financial.forex.derivative.Forex; import com.opengamma.analytics.financial.instrument.InstrumentDefinition; import com.opengamma.analytics.financial.interestrate.YieldCurveBundle; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; 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.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.CurrencyPairsFunction; import com.opengamma.financial.analytics.conversion.ForexSecurityConverter; import com.opengamma.financial.analytics.model.YieldCurveFunctionUtils; import com.opengamma.financial.analytics.model.discounting.DiscountingFunction; import com.opengamma.financial.analytics.model.forex.ForexVisitors; import com.opengamma.financial.currency.CurrencyPair; import com.opengamma.financial.currency.CurrencyPairs; import com.opengamma.financial.security.FinancialSecurity; import com.opengamma.financial.security.FinancialSecurityTypes; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; /** * Base class for FX forward functions * @deprecated Use {@link DiscountingFunction} */ @Deprecated public abstract class FXForwardFunction extends AbstractFunction.NonCompiledInvoker { /** The logger */ private static final Logger s_logger = LoggerFactory.getLogger(FXForwardFunction.class); /** * @deprecated Deprecated value property name - has been moved to {@link ValuePropertyNames#PAY_CURVE_CALCULATION_CONFIG} */ @Deprecated public static final String PAY_CURVE_CALC_CONFIG = ValuePropertyNames.PAY_CURVE_CALCULATION_CONFIG; /** * @deprecated Deprecated value property name - has been moved to {@link ValuePropertyNames#RECEIVE_CURVE_CALCULATION_CONFIG} */ @Deprecated public static final String RECEIVE_CURVE_CALC_CONFIG = ValuePropertyNames.RECEIVE_CURVE_CALCULATION_CONFIG; /** The computation target types for this function */ private static final ComputationTargetType TYPE = FinancialSecurityTypes.FX_FORWARD_SECURITY.or(FinancialSecurityTypes.NON_DELIVERABLE_FX_FORWARD_SECURITY); /** The value requirement produced by this function */ private final String _valueRequirementName; /** * @param valueRequirementName The value requirement name, not null */ public FXForwardFunction(final String valueRequirementName) { ArgumentChecker.notNull(valueRequirementName, "value requirement name"); _valueRequirementName = valueRequirementName; } @Override public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) { final Clock snapshotClock = executionContext.getValuationClock(); final ZonedDateTime now = ZonedDateTime.now(snapshotClock); final FinancialSecurity security = (FinancialSecurity) target.getSecurity(); final Currency payCurrency = security.accept(ForexVisitors.getPayCurrencyVisitor()); final Currency receiveCurrency = security.accept(ForexVisitors.getReceiveCurrencyVisitor()); if (now.isAfter(security.accept(ForexVisitors.getExpiryVisitor()))) { throw new OpenGammaRuntimeException("FX forward " + payCurrency.getCode() + "/" + receiveCurrency + " has expired"); } final ValueRequirement desiredValue = desiredValues.iterator().next(); final String payCurveName = desiredValue.getConstraint(ValuePropertyNames.PAY_CURVE); final String receiveCurveName = desiredValue.getConstraint(ValuePropertyNames.RECEIVE_CURVE); final String payCurveConfig = desiredValue.getConstraint(ValuePropertyNames.PAY_CURVE_CALCULATION_CONFIG); final String receiveCurveConfig = desiredValue.getConstraint(ValuePropertyNames.RECEIVE_CURVE_CALCULATION_CONFIG); final String fullPayCurveName = payCurveName + "_" + payCurrency.getCode(); final String fullReceiveCurveName = receiveCurveName + "_" + receiveCurrency.getCode(); final YieldAndDiscountCurve payCurve = getPayCurve(inputs, payCurrency, payCurveName, payCurveConfig); final YieldAndDiscountCurve receiveCurve = getReceiveCurve(inputs, receiveCurrency, receiveCurveName, receiveCurveConfig); final Map<String, Currency> curveCurrency = new HashMap<>(); curveCurrency.put(fullPayCurveName, payCurrency); curveCurrency.put(fullReceiveCurveName, receiveCurrency); final Object baseQuotePairsObject = inputs.getValue(ValueRequirementNames.CURRENCY_PAIRS); if (baseQuotePairsObject == null) { throw new OpenGammaRuntimeException("Could not get base/quote pair data"); } final CurrencyPairs baseQuotePairs = (CurrencyPairs) baseQuotePairsObject; final YieldAndDiscountCurve[] curves; final String[] allCurveNames; curves = new YieldAndDiscountCurve[] {payCurve, receiveCurve }; allCurveNames = new String[] {fullPayCurveName, fullReceiveCurveName }; // Implementation note: The ForexSecurityConverter create the Forex with currency order pay/receive. The curve are passed in the same order. final ForexSecurityConverter converter = new ForexSecurityConverter(baseQuotePairs); final InstrumentDefinition<?> definition = security.accept(converter); final Forex forex = (Forex) definition.toDerivative(now); final YieldCurveBundle yieldCurves = new YieldCurveBundle(allCurveNames, curves); final ValueProperties.Builder properties = getResultProperties(target, desiredValue); final ValueSpecification spec = new ValueSpecification(_valueRequirementName, target.toSpecification(), properties.get()); return getResult(forex, yieldCurves, target, desiredValues, inputs, spec, executionContext); } //TODO clumsy. Push the execute() method down into the functions and have getForward() and getData() methods /** * Performs the calculation. * * @param fxForward The FX forward * @param data The yield curve data * @param target The computation target * @param desiredValues The desired values * @param inputs The function inputs * @param spec The specification of the result * @param executionContext The execution context * @return A set of computed values */ protected abstract Set<ComputedValue> getResult(final Forex fxForward, final YieldCurveBundle data, final ComputationTarget target, final Set<ValueRequirement> desiredValues, final FunctionInputs inputs, final ValueSpecification spec, final FunctionExecutionContext executionContext); @Override public ComputationTargetType getTargetType() { return TYPE; } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) { final ValueProperties properties = getResultProperties(target).get(); return Collections.singleton(new ValueSpecification(_valueRequirementName, target.toSpecification(), properties)); } @Override public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) { final ValueProperties constraints = desiredValue.getConstraints(); final Set<String> payCurveNames = constraints.getValues(ValuePropertyNames.PAY_CURVE); if (payCurveNames == null || payCurveNames.size() != 1) { return null; } final Set<String> receiveCurveNames = constraints.getValues(ValuePropertyNames.RECEIVE_CURVE); if (receiveCurveNames == null || receiveCurveNames.size() != 1) { return null; } final Set<String> payCurveConfigNames = constraints.getValues(ValuePropertyNames.PAY_CURVE_CALCULATION_CONFIG); if (payCurveConfigNames == null || payCurveConfigNames.size() != 1) { return null; } final Set<String> receiveCurveConfigNames = constraints.getValues(ValuePropertyNames.RECEIVE_CURVE_CALCULATION_CONFIG); if (receiveCurveConfigNames == null || receiveCurveConfigNames.size() != 1) { return null; } final String payCurveName = payCurveNames.iterator().next(); final String receiveCurveName = receiveCurveNames.iterator().next(); final String payCurveCalculationConfig = payCurveConfigNames.iterator().next(); final String receiveCurveCalculationConfig = receiveCurveConfigNames.iterator().next(); final FinancialSecurity security = (FinancialSecurity) target.getSecurity(); final Currency payCurrency = security.accept(ForexVisitors.getPayCurrencyVisitor()); final Currency receiveCurrency = security.accept(ForexVisitors.getReceiveCurrencyVisitor()); final ValueRequirement payFundingCurve = YieldCurveFunctionUtils.getCurveRequirementForFXForward(ComputationTargetSpecification.of(payCurrency), payCurveName, payCurveCalculationConfig, true); final ValueRequirement receiveFundingCurve = YieldCurveFunctionUtils.getCurveRequirementForFXForward(ComputationTargetSpecification.of(receiveCurrency), receiveCurveName, receiveCurveCalculationConfig, false); final ValueRequirement pairQuoteRequirement = new ValueRequirement(ValueRequirementNames.CURRENCY_PAIRS, ComputationTargetSpecification.NULL); return Sets.newHashSet(pairQuoteRequirement, payFundingCurve, receiveFundingCurve); } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) { String currencyPairConfigName = null; String payCurveName = null; String payCurveCalculationConfig = null; String receiveCurveName = null; String receiveCurveCalculationConfig = null; for (final Map.Entry<ValueSpecification, ValueRequirement> entry : inputs.entrySet()) { final ValueSpecification specification = entry.getKey(); final ValueRequirement requirement = entry.getValue(); if (specification.getValueName().equals(ValueRequirementNames.CURRENCY_PAIRS)) { currencyPairConfigName = specification.getProperty(CurrencyPairsFunction.CURRENCY_PAIRS_NAME); } else if (requirement.getValueName().equals(ValueRequirementNames.YIELD_CURVE)) { final ValueProperties constraints = requirement.getConstraints(); if (constraints.getProperties().contains(ValuePropertyNames.PAY_CURVE)) { payCurveName = Iterables.getOnlyElement(constraints.getValues(ValuePropertyNames.CURVE)); payCurveCalculationConfig = Iterables.getOnlyElement(constraints.getValues(ValuePropertyNames.CURVE_CALCULATION_CONFIG)); } else if (constraints.getProperties().contains(ValuePropertyNames.RECEIVE_CURVE)) { receiveCurveName = Iterables.getOnlyElement(constraints.getValues(ValuePropertyNames.CURVE)); receiveCurveCalculationConfig = Iterables.getOnlyElement(constraints.getValues(ValuePropertyNames.CURVE_CALCULATION_CONFIG)); } } } if (currencyPairConfigName == null || payCurveName == null || receiveCurveName == null) { return null; } final CurrencyPairs baseQuotePairs = OpenGammaCompilationContext.getCurrencyPairsSource(context).getCurrencyPairs(currencyPairConfigName); final FinancialSecurity security = (FinancialSecurity) target.getSecurity(); final Currency payCurrency = security.accept(ForexVisitors.getPayCurrencyVisitor()); final Currency receiveCurrency = security.accept(ForexVisitors.getReceiveCurrencyVisitor()); final CurrencyPair baseQuotePair = baseQuotePairs.getCurrencyPair(payCurrency, receiveCurrency); if (baseQuotePair == null) { s_logger.error("Could not get base/quote pair for currency pair (" + payCurrency + ", " + receiveCurrency + ")"); return null; } final ValueSpecification resultSpec = new ValueSpecification(getValueRequirementName(), target.toSpecification(), getResultProperties(target, payCurveName, receiveCurveName, payCurveCalculationConfig, receiveCurveCalculationConfig, baseQuotePair).get()); return Collections.singleton(resultSpec); } /** * Gets the general result properties. * * @param target The target * @return The result properties */ protected abstract ValueProperties.Builder getResultProperties(final ComputationTarget target); /** * Gets the result properties with curve information set. * * @param target The target * @param payCurve The name of the pay curve * @param payCurveCalculationConfig The name of the pay curve calculation configuration * @param receiveCurve The name of the receive curve * @param receiveCurveCalculationConfig The name of the receive curve calculation configuration * @param baseQuotePair The base / counter information for the currency pair * @return The result properties */ protected abstract ValueProperties.Builder getResultProperties(final ComputationTarget target, final String payCurve, final String payCurveCalculationConfig, final String receiveCurve, final String receiveCurveCalculationConfig, final CurrencyPair baseQuotePair); /** * Gets the result properties. * * @param target The target * @param desiredValue The desired value * @return The result properties */ protected abstract ValueProperties.Builder getResultProperties(final ComputationTarget target, final ValueRequirement desiredValue); /** * Gets the value requirement name. * * @return The value requirement name */ protected String getValueRequirementName() { return _valueRequirementName; } /** * Gets the pay curve. * * @param inputs The function inputs * @param currency The pay currency * @param curveName The pay curve name * @param curveCalculationConfig The pay curve calculation configuration name * @return The pay curve */ protected static YieldAndDiscountCurve getPayCurve(final FunctionInputs inputs, final Currency currency, final String curveName, final String curveCalculationConfig) { final Object curveObject = inputs.getValue(YieldCurveFunctionUtils.getCurveRequirementForFXForward(ComputationTargetSpecification.of(currency), curveName, curveCalculationConfig, true)); if (curveObject == null) { throw new OpenGammaRuntimeException("Could not get " + curveName + " curve"); } final YieldAndDiscountCurve curve = (YieldAndDiscountCurve) curveObject; return curve; } /** * Gets the receive curve. * * @param inputs The function inputs * @param currency The receive currency * @param curveName The receive curve name * @param curveCalculationConfig The receive curve calculation configuration name * @return The receive curve */ protected static YieldAndDiscountCurve getReceiveCurve(final FunctionInputs inputs, final Currency currency, final String curveName, final String curveCalculationConfig) { final Object curveObject = inputs.getValue(YieldCurveFunctionUtils.getCurveRequirementForFXForward(ComputationTargetSpecification.of(currency), curveName, curveCalculationConfig, false)); if (curveObject == null) { throw new OpenGammaRuntimeException("Could not get " + curveName + " curve"); } final YieldAndDiscountCurve curve = (YieldAndDiscountCurve) curveObject; return curve; } }