@Override
 public Set<ComputedValue> execute(
     final FunctionExecutionContext executionContext,
     final FunctionInputs inputs,
     final ComputationTarget target,
     final Set<ValueRequirement> desiredValues) {
   final FXFutureSecurity security = (FXFutureSecurity) target.getSecurity();
   final Clock snapshotClock = executionContext.getValuationClock();
   final ZonedDateTime now = ZonedDateTime.now(snapshotClock);
   final Currency payCurrency = security.getNumerator();
   final Object payCurveObject =
       inputs.getValue(
           YieldCurveFunction.getCurveRequirement(payCurrency, _payCurveName, null, null));
   if (payCurveObject == null) {
     throw new OpenGammaRuntimeException("Could not get " + _payCurveName + " curve");
   }
   final Currency receiveCurrency = security.getDenominator();
   final Object receiveCurveObject =
       inputs.getValue(
           YieldCurveFunction.getCurveRequirement(receiveCurrency, _receiveCurveName, null, null));
   if (receiveCurveObject == null) {
     throw new OpenGammaRuntimeException("Could not get " + _receiveCurveName + " curve");
   }
   // TODO: The convention is only looked up here so that we can convert the spot rate; would be
   // better to request the spot rate using the correct currency pair in the first place
   final CurrencyPairs currencyPairs =
       OpenGammaExecutionContext.getCurrencyPairsSource(executionContext)
           .getCurrencyPairs(CurrencyPairs.DEFAULT_CURRENCY_PAIRS);
   final CurrencyPair currencyPair = currencyPairs.getCurrencyPair(payCurrency, receiveCurrency);
   final Currency currencyBase = currencyPair.getBase();
   final Object spotObject = inputs.getValue(ValueRequirementNames.SPOT_RATE);
   if (spotObject == null) {
     throw new OpenGammaRuntimeException("Could not get market data for spot rate");
   }
   double spot = (Double) spotObject;
   if (!receiveCurrency.equals(currencyBase) && receiveCurrency.equals(security.getCurrency())) {
     spot = 1. / spot;
   }
   final YieldAndDiscountCurve payCurve = (YieldAndDiscountCurve) payCurveObject;
   final YieldAndDiscountCurve receiveCurve = (YieldAndDiscountCurve) receiveCurveObject;
   final SimpleFXFutureDataBundle data =
       new SimpleFXFutureDataBundle(payCurve, receiveCurve, spot);
   final SimpleInstrument instrument = security.accept(CONVERTER).toDerivative(now);
   final CurrencyAmount pv = instrument.accept(CALCULATOR, data);
   final ValueProperties properties =
       createValueProperties()
           .with(ValuePropertyNames.PAY_CURVE, _payCurveName)
           .with(ValuePropertyNames.RECEIVE_CURVE, _receiveCurveName)
           .with(ValuePropertyNames.CURRENCY, pv.getCurrency().getCode())
           .get();
   final ValueSpecification spec =
       new ValueSpecification(
           ValueRequirementNames.PRESENT_VALUE, target.toSpecification(), properties);
   return Collections.singleton(new ComputedValue(spec, pv.getAmount()));
 }
 @Override
 public Set<ValueRequirement> getRequirements(
     final FunctionCompilationContext context,
     final ComputationTarget target,
     final ValueRequirement desiredValue) {
   final FXFutureSecurity future = (FXFutureSecurity) target.getSecurity();
   final ValueRequirement payYieldCurve =
       YieldCurveFunction.getCurveRequirement(future.getNumerator(), _payCurveName, null, null);
   final ValueRequirement receiveYieldCurve =
       YieldCurveFunction.getCurveRequirement(
           future.getDenominator(), _receiveCurveName, null, null);
   final ValueRequirement spot =
       ConventionBasedFXRateFunction.getSpotRateRequirement(
           future.getNumerator(), future.getDenominator());
   return Sets.newHashSet(payYieldCurve, receiveYieldCurve, spot);
 }