@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);
 }
 @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> 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<String> calculationMethods =
       constraints.getValues(ValuePropertyNames.CALCULATION_METHOD);
   if (calculationMethods == null || calculationMethods.size() != 1) {
     final ValueProperties newConstraints =
         constraints
             .copy()
             .withoutAny(ValuePropertyNames.CALCULATION_METHOD)
             .with(
                 ValuePropertyNames.CALCULATION_METHOD,
                 CalculationPropertyNamesAndValues.DISCOUNTING)
             .get();
     return Collections.singleton(
         new ValueRequirement(
             ValueRequirementNames.PNL_SERIES, target.toSpecification(), newConstraints));
   }
   final Set<ValueRequirement> requirements = new HashSet<>();
   final String calculationMethod = Iterables.getOnlyElement(calculationMethods);
   final FinancialSecurity security = (FinancialSecurity) target.getPosition().getSecurity();
   if (CalculationPropertyNamesAndValues.DISCOUNTING.equals(calculationMethod)) {
     requirements.add(
         new ValueRequirement(
             ValueRequirementNames.FX_CURRENCY_EXPOSURE,
             ComputationTargetSpecification.of(target.getPosition().getSecurity()),
             ValueProperties.builder()
                 .with(
                     ValuePropertyNames.CALCULATION_METHOD,
                     CalculationPropertyNamesAndValues.DISCOUNTING)
                 .with(ValuePropertyNames.PAY_CURVE, payCurveNames.iterator().next())
                 .with(
                     ValuePropertyNames.PAY_CURVE_CALCULATION_CONFIG,
                     payCurveCalculationConfigs.iterator().next())
                 .with(ValuePropertyNames.RECEIVE_CURVE, receiveCurveNames.iterator().next())
                 .with(
                     ValuePropertyNames.RECEIVE_CURVE_CALCULATION_CONFIG,
                     receiveCurveCalculationConfigs.iterator().next())
                 .get()));
   } else if (CalculationPropertyNamesAndValues.FORWARD_POINTS.equals(calculationMethod)) {
     final Set<String> forwardCurveNames =
         constraints.getValues(ValuePropertyNames.FORWARD_CURVE_NAME);
     if (forwardCurveNames == null || forwardCurveNames.size() != 1) {
       return null;
     }
     final String forwardCurveName = Iterables.getOnlyElement(forwardCurveNames);
     requirements.add(
         new ValueRequirement(
             ValueRequirementNames.FX_CURRENCY_EXPOSURE,
             ComputationTargetSpecification.of(target.getPosition().getSecurity()),
             ValueProperties.builder()
                 .with(
                     ValuePropertyNames.CALCULATION_METHOD,
                     CalculationPropertyNamesAndValues.FORWARD_POINTS)
                 .with(ValuePropertyNames.PAY_CURVE, payCurveNames.iterator().next())
                 .with(
                     ValuePropertyNames.PAY_CURVE_CALCULATION_CONFIG,
                     payCurveCalculationConfigs.iterator().next())
                 .with(ValuePropertyNames.RECEIVE_CURVE, receiveCurveNames.iterator().next())
                 .with(
                     ValuePropertyNames.RECEIVE_CURVE_CALCULATION_CONFIG,
                     receiveCurveCalculationConfigs.iterator().next())
                 .with(ValuePropertyNames.FORWARD_CURVE_NAME, forwardCurveName)
                 .get()));
   } else {
     return null;
   }
   final Set<String> resultCurrencies = constraints.getValues(CURRENCY);
   final Currency payCurrency = security.accept(ForexVisitors.getPayCurrencyVisitor());
   final Currency receiveCurrency = security.accept(ForexVisitors.getReceiveCurrencyVisitor());
   final String resultCurrency;
   final CurrencyPair baseQuotePair =
       _currencyPairs.getCurrencyPair(payCurrency, receiveCurrency);
   final Currency baseCurrency = baseQuotePair.getBase();
   final Currency nonBaseCurrency = baseQuotePair.getCounter();
   if (resultCurrencies != null && resultCurrencies.size() == 1) {
     final Currency ccy = Currency.of(Iterables.getOnlyElement(resultCurrencies));
     if (!(ccy.equals(payCurrency) || ccy.equals(receiveCurrency))) {
       requirements.add(
           ConventionBasedFXRateFunction.getHistoricalTimeSeriesRequirement(
               UnorderedCurrencyPair.of(baseCurrency, ccy)));
       resultCurrency = ccy.getCode();
     } else if (ccy.equals(nonBaseCurrency)) {
       requirements.add(
           ConventionBasedFXRateFunction.getHistoricalTimeSeriesRequirement(
               UnorderedCurrencyPair.of(nonBaseCurrency, baseCurrency)));
       resultCurrency = nonBaseCurrency.getCode();
     } else {
       requirements.add(
           ConventionBasedFXRateFunction.getHistoricalTimeSeriesRequirement(
               UnorderedCurrencyPair.of(baseCurrency, nonBaseCurrency)));
       resultCurrency = baseCurrency.getCode();
     }
   } else {
     resultCurrency = baseCurrency.getCode();
   }
   final ValueProperties fxSpotConstraints =
       desiredValue
           .getConstraints()
           .copy()
           .withoutAny(ValuePropertyNames.PAY_CURVE)
           .withoutAny(ValuePropertyNames.PAY_CURVE_CALCULATION_CONFIG)
           .withoutAny(ValuePropertyNames.RECEIVE_CURVE)
           .withoutAny(ValuePropertyNames.RECEIVE_CURVE_CALCULATION_CONFIG)
           .withoutAny(ValuePropertyNames.PROPERTY_PNL_CONTRIBUTIONS)
           .withoutAny(ValuePropertyNames.CURVE_CURRENCY)
           .withoutAny(ValuePropertyNames.CALCULATION_METHOD)
           .withoutAny(ValuePropertyNames.FORWARD_CURVE_NAME)
           .with(CURRENCY, resultCurrency)
           .withOptional(CURRENCY)
           .get();
   final ComputationTargetSpecification fxSpotReturnSeriesSpec =
       ComputationTargetType.UNORDERED_CURRENCY_PAIR.specification(
           UnorderedCurrencyPair.of(payCurrency, receiveCurrency));
   requirements.add(
       new ValueRequirement(
           ValueRequirementNames.RETURN_SERIES, fxSpotReturnSeriesSpec, fxSpotConstraints));
   return requirements;
 }