@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; }