@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<ValueSpecification> getResults(
     final FunctionCompilationContext context,
     final ComputationTarget target,
     final Map<ValueSpecification, ValueRequirement> inputs) {
   if (inputs.size() == 1) {
     final ValueSpecification input = Iterables.getOnlyElement(inputs.keySet());
     if (ValueRequirementNames.PNL_SERIES.equals(input.getValueName())) {
       return Collections.singleton(input);
     }
   }
   final FXForwardSecurity security = (FXForwardSecurity) target.getPosition().getSecurity();
   final CurrencyPair currencyPair =
       _currencyPairs.getCurrencyPair(security.getPayCurrency(), security.getReceiveCurrency());
   if (currencyPair == null) {
     return null;
   }
   final Currency currencyBase = currencyPair.getBase();
   String resultCurrency = null;
   final ValueProperties.Builder builder = createValueProperties();
   for (final Map.Entry<ValueSpecification, ValueRequirement> entry : inputs.entrySet()) {
     final ValueSpecification inputSpec = entry.getKey();
     final ValueRequirement inputReq = entry.getValue();
     if (inputReq.getValueName().equals(RETURN_SERIES)) {
       final Set<String> resultCurrencies = inputReq.getConstraints().getValues(CURRENCY);
       if (resultCurrencies != null && resultCurrencies.size() == 1) {
         resultCurrency = inputReq.getConstraint(CURRENCY);
       } else {
         resultCurrency = currencyBase.getCode();
       }
     }
     for (final String propertyName : inputSpec.getProperties().getProperties()) {
       if (ValuePropertyNames.FUNCTION.equals(propertyName)) {
         continue;
       }
       final Set<String> values = inputSpec.getProperties().getValues(propertyName);
       if (values == null || values.isEmpty()) {
         builder.withAny(propertyName);
       } else {
         builder.with(propertyName, values);
       }
     }
   }
   if (resultCurrency == null) {
     return null;
   }
   builder
       .with(ValuePropertyNames.CURRENCY, resultCurrency)
       .with(
           ValuePropertyNames.PROPERTY_PNL_CONTRIBUTIONS,
           ValueRequirementNames.FX_CURRENCY_EXPOSURE);
   return ImmutableSet.of(
       new ValueSpecification(
           ValueRequirementNames.PNL_SERIES, target.toSpecification(), builder.get()));
 }
 @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 daysForward = desiredValue.getConstraint(PROPERTY_DAYS_TO_MOVE_FORWARD);
   final String fullPayCurveName = payCurveName + "_" + payCurrency.getCode();
   final String fullReceiveCurveName = receiveCurveName + "_" + receiveCurrency.getCode();
   final YieldAndDiscountCurve payFundingCurve =
       getPayCurve(inputs, payCurrency, payCurveName, payCurveConfig);
   final YieldAndDiscountCurve receiveFundingCurve =
       getReceiveCurve(inputs, receiveCurrency, receiveCurveName, receiveCurveConfig);
   final YieldAndDiscountCurve[] curves;
   final Map<String, Currency> curveCurrency = new HashMap<>();
   curveCurrency.put(fullPayCurveName, payCurrency);
   curveCurrency.put(fullReceiveCurveName, receiveCurrency);
   final String[] allCurveNames;
   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 CurrencyPair baseQuotePair = baseQuotePairs.getCurrencyPair(payCurrency, receiveCurrency);
   if (baseQuotePair == null) {
     throw new OpenGammaRuntimeException(
         "Could not get base/quote pair for currency pair ("
             + payCurrency
             + ", "
             + receiveCurrency
             + ")");
   }
   if (baseQuotePair
       .getBase()
       .equals(payCurrency)) { // To get Base/quote in market standard order.
     curves = new YieldAndDiscountCurve[] {payFundingCurve, receiveFundingCurve};
     allCurveNames = new String[] {fullPayCurveName, fullReceiveCurveName};
   } else {
     curves = new YieldAndDiscountCurve[] {receiveFundingCurve, payFundingCurve};
     allCurveNames = new String[] {fullReceiveCurveName, fullPayCurveName};
   }
   final ForexSecurityConverter converter = new ForexSecurityConverter(baseQuotePairs);
   final ForexDefinition definition = (ForexDefinition) security.accept(converter);
   final YieldCurveBundle yieldCurves = new YieldCurveBundle(allCurveNames, curves);
   final ValueProperties.Builder properties = getResultProperties(target, desiredValue);
   final ValueSpecification spec =
       new ValueSpecification(
           getValueRequirementName(), target.toSpecification(), properties.get());
   final ConstantSpreadHorizonThetaCalculator calculator =
       ConstantSpreadHorizonThetaCalculator.getInstance();
   final MultipleCurrencyAmount theta =
       calculator.getTheta(
           definition, now, allCurveNames, yieldCurves, Integer.parseInt(daysForward));
   return Collections.singleton(new ComputedValue(spec, theta));
 }
 @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;
   String daysForward = 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);
       daysForward = requirement.getConstraint(PROPERTY_DAYS_TO_MOVE_FORWARD);
     } 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));
       }
     }
   }
   assert currencyPairConfigName != 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,
                   daysForward)
               .get());
   return Collections.singleton(resultSpec);
 }
 @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 = snapshotClock.zonedDateTime();
   final FinancialSecurity security = (FinancialSecurity) target.getSecurity();
   final Currency putCurrency = security.accept(ForexVisitors.getPutCurrencyVisitor());
   final Currency callCurrency = security.accept(ForexVisitors.getCallCurrencyVisitor());
   final ValueRequirement desiredValue = desiredValues.iterator().next();
   final String putCurveName = desiredValue.getConstraint(PUT_CURVE);
   final String callCurveName = desiredValue.getConstraint(CALL_CURVE);
   final String surfaceName = desiredValue.getConstraint(ValuePropertyNames.SURFACE);
   final String putCurveConfig = desiredValue.getConstraint(PUT_CURVE_CALC_CONFIG);
   final String callCurveConfig = desiredValue.getConstraint(CALL_CURVE_CALC_CONFIG);
   final String interpolatorName =
       desiredValue.getConstraint(InterpolatedDataProperties.X_INTERPOLATOR_NAME);
   final String leftExtrapolatorName =
       desiredValue.getConstraint(InterpolatedDataProperties.LEFT_X_EXTRAPOLATOR_NAME);
   final String rightExtrapolatorName =
       desiredValue.getConstraint(InterpolatedDataProperties.RIGHT_X_EXTRAPOLATOR_NAME);
   final String daysForward = desiredValue.getConstraint(PROPERTY_DAYS_TO_MOVE_FORWARD);
   final String fullPutCurveName = putCurveName + "_" + putCurrency.getCode();
   final String fullCallCurveName = callCurveName + "_" + callCurrency.getCode();
   final YieldAndDiscountCurve putFundingCurve =
       FXOptionFunctionUtils.getCurveForCurrency(
           inputs, putCurrency, putCurveName, putCurveConfig);
   final YieldAndDiscountCurve callFundingCurve =
       FXOptionFunctionUtils.getCurveForCurrency(
           inputs, callCurrency, callCurveName, callCurveConfig);
   final YieldAndDiscountCurve[] curves;
   final Map<String, Currency> curveCurrency = new HashMap<String, Currency>();
   curveCurrency.put(fullPutCurveName, putCurrency);
   curveCurrency.put(fullCallCurveName, callCurrency);
   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 CurrencyPair baseQuotePair = baseQuotePairs.getCurrencyPair(putCurrency, callCurrency);
   if (baseQuotePair == null) {
     throw new OpenGammaRuntimeException(
         "Could not get base/quote pair for currency pair ("
             + putCurrency
             + ", "
             + callCurrency
             + ")");
   }
   final String[] allCurveNames;
   final Currency ccy1;
   final Currency ccy2;
   if (baseQuotePair
       .getBase()
       .equals(putCurrency)) { // To get Base/quote in market standard order.
     ccy1 = putCurrency;
     ccy2 = callCurrency;
     curves = new YieldAndDiscountCurve[] {putFundingCurve, callFundingCurve};
     allCurveNames = new String[] {fullPutCurveName, fullCallCurveName};
   } else {
     curves = new YieldAndDiscountCurve[] {callFundingCurve, putFundingCurve};
     allCurveNames = new String[] {fullCallCurveName, fullPutCurveName};
     ccy1 = callCurrency;
     ccy2 = putCurrency;
   }
   final YieldCurveBundle yieldCurves = new YieldCurveBundle(allCurveNames, curves);
   final Object spotObject = inputs.getValue(ValueRequirementNames.SPOT_RATE);
   if (spotObject == null) {
     throw new OpenGammaRuntimeException("Could not get spot requirement");
   }
   final double spot = (Double) spotObject;
   final ValueRequirement fxVolatilitySurfaceRequirement =
       getSurfaceRequirement(
           surfaceName,
           putCurrency,
           callCurrency,
           interpolatorName,
           leftExtrapolatorName,
           rightExtrapolatorName);
   final Object volatilitySurfaceObject = inputs.getValue(fxVolatilitySurfaceRequirement);
   if (volatilitySurfaceObject == null) {
     throw new OpenGammaRuntimeException("Could not get " + fxVolatilitySurfaceRequirement);
   }
   final SmileDeltaTermStructureParametersStrikeInterpolation smiles =
       (SmileDeltaTermStructureParametersStrikeInterpolation) volatilitySurfaceObject;
   final FXMatrix fxMatrix = new FXMatrix(ccy1, ccy2, spot);
   final ValueProperties.Builder properties =
       getResultProperties(target, desiredValue, baseQuotePair);
   final ValueSpecification spec =
       new ValueSpecification(
           ValueRequirementNames.VALUE_THETA, target.toSpecification(), properties.get());
   final YieldCurveBundle curvesWithFX =
       new YieldCurveBundle(fxMatrix, curveCurrency, yieldCurves.getCurvesMap());
   final SmileDeltaTermStructureDataBundle smileBundle =
       new SmileDeltaTermStructureDataBundle(curvesWithFX, smiles, Pair.of(ccy1, ccy2));
   final ForexSecurityConverter converter = new ForexSecurityConverter(baseQuotePairs);
   final ForexOptionVanillaDefinition definition =
       (ForexOptionVanillaDefinition) security.accept(converter);
   final MultipleCurrencyAmount theta =
       CALCULATOR.getTheta(
           definition, now, allCurveNames, smileBundle, Integer.parseInt(daysForward));
   return Collections.singleton(new ComputedValue(spec, HorizonUtils.getNonZeroValue(theta)));
 }
    @Override
    public Set<ComputedValue> execute(
        final FunctionExecutionContext executionContext,
        final FunctionInputs inputs,
        final ComputationTarget target,
        final Set<ValueRequirement> desiredValues) {
      final Position position = target.getPosition();
      final ValueRequirement desiredValue = desiredValues.iterator().next();
      final ValueProperties constraints = desiredValue.getConstraints();
      final Set<String> resultCurrencies = constraints.getValues(CURRENCY);
      final FXForwardSecurity security = (FXForwardSecurity) position.getSecurity();
      final MultipleCurrencyAmount mca =
          (MultipleCurrencyAmount) inputs.getValue(ValueRequirementNames.FX_CURRENCY_EXPOSURE);
      final Currency payCurrency = security.getPayCurrency();
      final Currency receiveCurrency = security.getReceiveCurrency();
      final CurrencyPair currencyPair =
          _currencyPairs.getCurrencyPair(payCurrency, receiveCurrency);
      final Currency baseCurrency = currencyPair.getBase();
      final Currency currencyNonBase = currencyPair.getCounter(); // The non-base currency
      final double exposure = mca.getAmount(currencyNonBase);

      final ValueSpecification spec =
          new ValueSpecification(
              ValueRequirementNames.PNL_SERIES,
              target.toSpecification(),
              desiredValue.getConstraints());
      if (resultCurrencies == null || resultCurrencies.size() != 1) {
        s_logger.warn("No Currency property - returning result in base currency");
        final LocalDateDoubleTimeSeries fxSpotReturnSeries =
            (LocalDateDoubleTimeSeries) inputs.getValue(ValueRequirementNames.RETURN_SERIES);
        final LocalDateDoubleTimeSeries pnlSeries =
            fxSpotReturnSeries.multiply(
                position.getQuantity().doubleValue()
                    * exposure); // The P/L time series is in the base currency
        return Collections.singleton(new ComputedValue(spec, pnlSeries));
      }
      final Currency resultCurrency = Currency.of(Iterables.getOnlyElement(resultCurrencies));
      final LocalDateDoubleTimeSeries conversionTS =
          (LocalDateDoubleTimeSeries) inputs.getValue(HISTORICAL_FX_TIME_SERIES);
      if (conversionTS == null) {
        throw new OpenGammaRuntimeException(
            "Asked for result in "
                + resultCurrency
                + " but could not get "
                + baseCurrency
                + "/"
                + resultCurrency
                + " conversion series");
      }
      if (resultCurrency.equals(baseCurrency)) {
        final LocalDateDoubleTimeSeries fxSpotReturnSeries =
            (LocalDateDoubleTimeSeries) inputs.getValue(ValueRequirementNames.RETURN_SERIES);
        final LocalDateDoubleTimeSeries convertedSeries =
            conversionTS
                .reciprocal()
                .multiply(
                    position.getQuantity().doubleValue()
                        * exposure); // The P/L time series is in the base currency
        final LocalDateDoubleTimeSeries pnlSeries =
            fxSpotReturnSeries.multiply(
                convertedSeries); // The P/L time series is in the base currency
        return Collections.singleton(new ComputedValue(spec, pnlSeries));
      }
      final LocalDateDoubleTimeSeries fxSpotReturnSeries =
          (LocalDateDoubleTimeSeries) inputs.getValue(ValueRequirementNames.RETURN_SERIES);
      final LocalDateDoubleTimeSeries convertedSeries =
          conversionTS.multiply(
              position.getQuantity().doubleValue()
                  * exposure); // The P/L time series is in the base currency
      final LocalDateDoubleTimeSeries pnlSeries = convertedSeries.multiply(fxSpotReturnSeries);
      return Collections.singleton(new ComputedValue(spec, pnlSeries));
    }
 @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;
 }
 public static ForexOptionDataBundle<?> buildMarketBundle(
     final ZonedDateTime now,
     final FunctionInputs inputs,
     final ComputationTarget target,
     final Set<ValueRequirement> desiredValues) {
   final FinancialSecurity security = (FinancialSecurity) target.getSecurity();
   final Currency putCurrency = security.accept(ForexVisitors.getPutCurrencyVisitor());
   final Currency callCurrency = security.accept(ForexVisitors.getCallCurrencyVisitor());
   if (now.isAfter(security.accept(ForexVisitors.getExpiryVisitor()))) {
     throw new OpenGammaRuntimeException(
         "FX option " + putCurrency.getCode() + "/" + callCurrency + " has expired");
   }
   final ValueRequirement desiredValue = desiredValues.iterator().next();
   final String putCurveName = desiredValue.getConstraint(PUT_CURVE);
   final String callCurveName = desiredValue.getConstraint(CALL_CURVE);
   final String putCurveConfig = desiredValue.getConstraint(PUT_CURVE_CALC_CONFIG);
   final String callCurveConfig = desiredValue.getConstraint(CALL_CURVE_CALC_CONFIG);
   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 String fullPutCurveName = putCurveName + "_" + putCurrency.getCode();
   final String fullCallCurveName = callCurveName + "_" + callCurrency.getCode();
   final YieldAndDiscountCurve putFundingCurve =
       getCurveForCurrency(inputs, putCurrency, putCurveName, putCurveConfig);
   final YieldAndDiscountCurve callFundingCurve =
       getCurveForCurrency(inputs, callCurrency, callCurveName, callCurveConfig);
   final YieldAndDiscountCurve[] curves;
   final Map<String, Currency> curveCurrency = new HashMap<>();
   curveCurrency.put(fullPutCurveName, putCurrency);
   curveCurrency.put(fullCallCurveName, callCurrency);
   final String[] allCurveNames;
   final Currency ccy1;
   final Currency ccy2;
   final Object spotObject = inputs.getValue(ValueRequirementNames.SPOT_RATE);
   if (spotObject == null) {
     throw new OpenGammaRuntimeException("Could not get spot requirement");
   }
   double spot = (Double) spotObject;
   final CurrencyPair baseQuotePair = baseQuotePairs.getCurrencyPair(putCurrency, callCurrency);
   if (baseQuotePair == null) {
     throw new OpenGammaRuntimeException(
         "Could not get base/quote pair for currency pair ("
             + putCurrency
             + ", "
             + callCurrency
             + ")");
   }
   if (baseQuotePair
       .getBase()
       .equals(putCurrency)) { // To get Base/quote in market standard order.
     ccy1 = putCurrency;
     ccy2 = callCurrency;
     curves = new YieldAndDiscountCurve[] {putFundingCurve, callFundingCurve};
     allCurveNames = new String[] {fullPutCurveName, fullCallCurveName};
   } else {
     curves = new YieldAndDiscountCurve[] {callFundingCurve, putFundingCurve};
     allCurveNames = new String[] {fullCallCurveName, fullPutCurveName};
     ccy1 = callCurrency;
     ccy2 = putCurrency;
     spot = 1. / spot;
   }
   final YieldCurveBundle yieldCurves = new YieldCurveBundle(allCurveNames, curves);
   final Object volatilitySurfaceObject =
       inputs.getValue(ValueRequirementNames.STANDARD_VOLATILITY_SURFACE_DATA);
   if (volatilitySurfaceObject == null) {
     throw new OpenGammaRuntimeException("Could not get volatility surface data");
   }
   final FXMatrix fxMatrix = new FXMatrix(ccy1, ccy2, spot);
   final YieldCurveBundle curvesWithFX =
       new YieldCurveBundle(fxMatrix, curveCurrency, yieldCurves.getCurvesMap());
   final Pair<Currency, Currency> currencyPair = Pair.of(ccy1, ccy2);
   if (volatilitySurfaceObject instanceof SmileDeltaTermStructureParametersStrikeInterpolation) {
     final SmileDeltaTermStructureParametersStrikeInterpolation smiles =
         (SmileDeltaTermStructureParametersStrikeInterpolation) volatilitySurfaceObject;
     final SmileDeltaTermStructureDataBundle smileBundle =
         new SmileDeltaTermStructureDataBundle(curvesWithFX, smiles, currencyPair);
     return smileBundle;
   }
   final BlackForexTermStructureParameters termStructure =
       (BlackForexTermStructureParameters) volatilitySurfaceObject;
   final YieldCurveWithBlackForexTermStructureBundle flatData =
       new YieldCurveWithBlackForexTermStructureBundle(curvesWithFX, termStructure, currencyPair);
   return flatData;
 }