public void convertCurrencyAmount() { List<FxRate> rates = ImmutableList.of( FxRate.of(Currency.GBP, Currency.USD, 1.61), FxRate.of(Currency.GBP, Currency.USD, 1.62), FxRate.of(Currency.GBP, Currency.USD, 1.63)); CalculationEnvironment marketData = MarketEnvironment.builder() .valuationDate(date(2011, 3, 8)) .addValue(FxRateId.of(Currency.GBP, Currency.USD), rates) .build(); MarketDataMappings mappings = MarketDataMappings.of(MarketDataFeed.NONE); DefaultCalculationMarketData calculationMarketData = DefaultCalculationMarketData.of(marketData, mappings); SingleScenarioResult<CurrencyAmount> test = SingleScenarioResult.of(3, CurrencyAmount.of(Currency.GBP, 2)); ScenarioResult<?> convertedList = test.convertedTo(Currency.USD, calculationMarketData); List<CurrencyAmount> expectedValues = ImmutableList.of( CurrencyAmount.of(Currency.USD, 2 * 1.61), CurrencyAmount.of(Currency.USD, 2 * 1.62), CurrencyAmount.of(Currency.USD, 2 * 1.63)); DefaultScenarioResult<CurrencyAmount> expectedList = DefaultScenarioResult.of(expectedValues); assertThat(convertedList).isEqualTo(expectedList); }
@Test public class CalculationTaskTest { private static final MarketDataMappings MAPPINGS = MarketDataMappings.of(MarketDataFeed.NONE); private static final ReportingRules REPORTING_RULES = ReportingRules.fixedCurrency(Currency.USD); private static final TestTarget TARGET = new TestTarget(); public void requirements() { MarketDataFeed marketDataFeed = MarketDataFeed.of("MarketDataVendor"); MarketDataMappings marketDataMappings = DefaultMarketDataMappings.builder() .mappings(ImmutableMap.of(TestKey.class, new TestMapping("foo"))) .marketDataFeed(marketDataFeed) .build(); CalculationTask task = new CalculationTask( new TestTarget(), 0, 0, new TestFunction(), marketDataMappings, ReportingRules.empty()); MarketDataRequirements requirements = task.requirements(); Set<? extends MarketDataId<?>> nonObservables = requirements.getNonObservables(); ImmutableSet<? extends ObservableId> observables = requirements.getObservables(); ImmutableSet<ObservableId> timeSeries = requirements.getTimeSeries(); MarketDataId<?> timeSeriesId = TestObservableKey.of("3").toMarketDataId(marketDataFeed); assertThat(timeSeries).hasSize(1); assertThat(timeSeries.iterator().next()).isEqualTo(timeSeriesId); MarketDataId<?> nonObservableId = TestId.of("1"); assertThat(nonObservables).hasSize(1); assertThat(nonObservables.iterator().next()).isEqualTo(nonObservableId); MarketDataId<?> observableId = TestObservableKey.of("2").toMarketDataId(marketDataFeed); assertThat(observables).hasSize(1); assertThat(observables.iterator().next()).isEqualTo(observableId); } /** * Test that the result is converted to the reporting currency if it implements * CurrencyConvertible and the FX rates are available in the market data. The reporting currency * is taken from the reporting rules. */ public void convertResultCurrencyUsingReportingRules() { DoubleArray values = DoubleArray.of(1, 2, 3); List<FxRate> rates = ImmutableList.of(1.61, 1.62, 1.63) .stream() .map(rate -> FxRate.of(Currency.GBP, Currency.USD, rate)) .collect(toImmutableList()); CurrencyValuesArray list = CurrencyValuesArray.of(Currency.GBP, values); CalculationEnvironment marketData = CalculationEnvironment.builder() .valuationDate(date(2011, 3, 8)) .addValue(FxRateId.of(Currency.GBP, Currency.USD), rates) .build(); ConvertibleFunction fn = ConvertibleFunction.of(() -> list); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, REPORTING_RULES); DoubleArray expectedValues = DoubleArray.of(1 * 1.61, 2 * 1.62, 3 * 1.63); CurrencyValuesArray expectedArray = CurrencyValuesArray.of(Currency.USD, expectedValues); CalculationResult calculationResult = task.execute(marketData); Result<?> result = calculationResult.getResult(); assertThat(result).hasValue(expectedArray); } /** * Test that the result is converted to the reporting currency if it implements * CurrencyConvertible and the FX rates are available in the market data. The default reporting * currency is taken from the function. */ public void convertResultCurrencyUsingDefaultReportingCurrency() { DoubleArray values = DoubleArray.of(1, 2, 3); List<FxRate> rates = ImmutableList.of(1.61, 1.62, 1.63) .stream() .map(rate -> FxRate.of(Currency.GBP, Currency.USD, rate)) .collect(toImmutableList()); CurrencyValuesArray list = CurrencyValuesArray.of(Currency.GBP, values); CalculationEnvironment marketData = CalculationEnvironment.builder() .valuationDate(date(2011, 3, 8)) .addValue(FxRateId.of(Currency.GBP, Currency.USD), rates) .build(); ConvertibleFunction fn = ConvertibleFunction.of(() -> list, Currency.USD); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, ReportingRules.empty()); DoubleArray expectedValues = DoubleArray.of(1 * 1.61, 2 * 1.62, 3 * 1.63); CurrencyValuesArray expectedArray = CurrencyValuesArray.of(Currency.USD, expectedValues); CalculationResult calculationResult = task.execute(marketData); Result<?> result = calculationResult.getResult(); assertThat(result).hasValue(expectedArray); } /** Test that the result is returned unchanged if it is a failure. */ public void convertResultCurrencyFailure() { ConvertibleFunction fn = ConvertibleFunction.of( () -> { throw new RuntimeException("This is a failure"); }); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, REPORTING_RULES); CalculationEnvironment marketData = CalculationEnvironment.builder().valuationDate(date(2011, 3, 8)).build(); CalculationResult calculationResult = task.execute(marketData); Result<?> result = calculationResult.getResult(); assertThat(result).hasFailureMessageMatching("This is a failure"); } /** Test the result is returned unchanged if it is not CurrencyConvertible. */ public void convertResultCurrencyNotConvertible() { TestFunction fn = new TestFunction(); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, REPORTING_RULES); CalculationEnvironment marketData = CalculationEnvironment.builder().valuationDate(date(2011, 3, 8)).build(); CalculationResult calculationResult = task.execute(marketData); Result<?> result = calculationResult.getResult(); assertThat(result).hasValue("bar"); } /** Test a failure is returned for a convertible value if there is no reporting currency. */ public void convertResultCurrencyNoReportingCurrency() { DoubleArray values = DoubleArray.of(1, 2, 3); List<FxRate> rates = ImmutableList.of(1.61, 1.62, 1.63) .stream() .map(rate -> FxRate.of(Currency.GBP, Currency.USD, rate)) .collect(toImmutableList()); CurrencyValuesArray list = CurrencyValuesArray.of(Currency.GBP, values); CalculationEnvironment marketData = CalculationEnvironment.builder() .valuationDate(date(2011, 3, 8)) .addValue(FxRateId.of(Currency.GBP, Currency.USD), rates) .build(); ConvertibleFunction fn = ConvertibleFunction.of(() -> list); ReportingRules reportingRules = ReportingRules.empty(); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, reportingRules); CalculationResult calculationResult = task.execute(marketData); Result<?> result = calculationResult.getResult(); assertThat(result).hasFailureMessageMatching("No reporting currency available.*"); } /** Test a non-convertible result is returned even if there is no reporting currency. */ public void nonConvertibleResultReturnedWhenNoReportingCurrency() { TestFunction fn = new TestFunction(); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, ReportingRules.empty()); CalculationEnvironment marketData = CalculationEnvironment.builder().valuationDate(date(2011, 3, 8)).build(); CalculationResult calculationResult = task.execute(marketData); Result<?> result = calculationResult.getResult(); assertThat(result).hasValue("bar"); } /** Test that a failure is returned if currency conversion fails. */ public void convertResultCurrencyConversionFails() { DoubleArray values = DoubleArray.of(1, 2, 3); CurrencyValuesArray list = CurrencyValuesArray.of(Currency.GBP, values); // Market data doesn't include FX rates, conversion to USD will fail CalculationEnvironment marketData = CalculationEnvironment.builder().valuationDate(date(2011, 3, 8)).build(); ConvertibleFunction fn = ConvertibleFunction.of(() -> list); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, REPORTING_RULES); CalculationResult calculationResult = task.execute(marketData); Result<?> result = calculationResult.getResult(); assertThat(result).hasFailureMessageMatching("Failed to convert value .* to currency USD"); } /** Tests that executing a function wraps its return value in a success result. */ public void execute() { SupplierFunction<String> fn = SupplierFunction.of(() -> "foo"); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, REPORTING_RULES); CalculationEnvironment marketData = CalculationEnvironment.builder().valuationDate(date(2011, 3, 8)).build(); CalculationResult calculationResult = task.execute(marketData); Result<?> result = calculationResult.getResult(); assertThat(result).hasValue("foo"); } /** * Tests that executing a function that throws an exception wraps the exception in a failure * result. */ public void executeException() { SupplierFunction<String> fn = SupplierFunction.of( () -> { throw new IllegalArgumentException("foo"); }); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, REPORTING_RULES); CalculationEnvironment marketData = CalculationEnvironment.builder().valuationDate(date(2011, 3, 8)).build(); CalculationResult calculationResult = task.execute(marketData); Result<?> result = calculationResult.getResult(); assertThat(result).isFailure(FailureReason.ERROR).hasFailureMessageMatching("foo"); } /** * Tests that executing a function that returns a success result returns the underlying result * without wrapping it. */ public void executeSuccessResultValue() { SupplierFunction<Result<String>> fn = SupplierFunction.of(() -> Result.success("foo")); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, REPORTING_RULES); CalculationEnvironment marketData = CalculationEnvironment.builder().valuationDate(date(2011, 3, 8)).build(); CalculationResult calculationResult = task.execute(marketData); Result<?> result = calculationResult.getResult(); assertThat(result).hasValue("foo"); } /** * Tests that executing a function that returns a failure result returns the underlying result * without wrapping it. */ public void executeFailureResultValue() { SupplierFunction<Result<String>> fn = SupplierFunction.of(() -> Result.failure(FailureReason.NOT_APPLICABLE, "bar")); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, REPORTING_RULES); CalculationEnvironment marketData = CalculationEnvironment.builder().valuationDate(date(2011, 3, 8)).build(); CalculationResult calculationResult = task.execute(marketData); Result<?> result = calculationResult.getResult(); assertThat(result).isFailure(FailureReason.NOT_APPLICABLE).hasFailureMessageMatching("bar"); } /** * Tests that requirements are added for the FX rates needed to convert the results into the * reporting currency. */ public void fxConversionRequirements() { OutputCurrenciesFunction fn = new OutputCurrenciesFunction(); CalculationTask task = new CalculationTask(TARGET, 0, 0, fn, MAPPINGS, REPORTING_RULES); MarketDataRequirements requirements = task.requirements(); assertThat(requirements.getNonObservables()) .containsOnly( FxRateId.of(Currency.GBP, Currency.USD), FxRateId.of(Currency.EUR, Currency.USD)); } // -------------------------------------------------------------------------------------------------------------------- private static class TestTarget implements CalculationTarget {} /** Function that returns a value that is not currency convertible. */ public static final class TestFunction implements CalculationSingleFunction<TestTarget, Object> { @Override public FunctionRequirements requirements(TestTarget target) { return FunctionRequirements.builder() .singleValueRequirements(ImmutableSet.of(TestKey.of("1"), TestObservableKey.of("2"))) .timeSeriesRequirements(TestObservableKey.of("3")) .build(); } @Override public Object execute(TestTarget target, CalculationMarketData marketData) { return "bar"; } } /** Function that returns a value that is currency convertible. */ private static final class ConvertibleFunction implements CalculationSingleFunction<TestTarget, CurrencyValuesArray> { private final Supplier<CurrencyValuesArray> supplier; private final Optional<Currency> reportingCurrency; public static ConvertibleFunction of(Supplier<CurrencyValuesArray> supplier) { return new ConvertibleFunction(supplier, Optional.<Currency>empty()); } public static ConvertibleFunction of( Supplier<CurrencyValuesArray> supplier, Currency reportingCurrency) { return new ConvertibleFunction(supplier, Optional.of(reportingCurrency)); } private ConvertibleFunction( Supplier<CurrencyValuesArray> supplier, Optional<Currency> reportingCurrency) { this.supplier = supplier; this.reportingCurrency = reportingCurrency; } @Override public CurrencyValuesArray execute(TestTarget target, CalculationMarketData marketData) { return supplier.get(); } @Override public FunctionRequirements requirements(TestTarget target) { return FunctionRequirements.empty(); } @Override public Optional<Currency> defaultReportingCurrency(TestTarget target) { return reportingCurrency; } } /** Function that returns a value from a Supplier. */ private static final class SupplierFunction<T> implements CalculationSingleFunction<TestTarget, T> { private final Supplier<T> supplier; public static <T> SupplierFunction<T> of(Supplier<T> supplier) { return new SupplierFunction<>(supplier); } private SupplierFunction(Supplier<T> supplier) { this.supplier = supplier; } @Override public T execute(TestTarget target, CalculationMarketData marketData) { return supplier.get(); } @Override public FunctionRequirements requirements(TestTarget target) { return FunctionRequirements.empty(); } } /** Function that returns requirements containing output currencies. */ private static final class OutputCurrenciesFunction implements CalculationSingleFunction<TestTarget, Object> { @Override public Object execute(TestTarget target, CalculationMarketData marketData) { throw new UnsupportedOperationException("execute not implemented"); } @Override public FunctionRequirements requirements(TestTarget target) { return FunctionRequirements.builder() .outputCurrencies(Currency.GBP, Currency.EUR, Currency.USD) .build(); } } }