public void getValues() { FxRateScenarioArray rates = FxRateScenarioArray.of(Currency.EUR, Currency.USD, DoubleArray.of(1.07, 1.08, 1.09)); assertThat(rates.getPair()).isEqualTo(CurrencyPair.of(Currency.EUR, Currency.USD)); assertThat(rates.getScenarioCount()).isEqualTo(3); assertThat(rates.get(0)).isEqualTo(FxRate.of(Currency.EUR, Currency.USD, 1.07)); assertThat(rates.get(1)).isEqualTo(FxRate.of(Currency.EUR, Currency.USD, 1.08)); assertThat(rates.get(2)).isEqualTo(FxRate.of(Currency.EUR, Currency.USD, 1.09)); assertThrows(ArrayIndexOutOfBoundsException.class, () -> rates.get(3)); }
/** * Returns an array of FX rates for a currency pair. * <p> * The rates are the rates from the base currency to the counter currency * as defined by this formula: {@code (1 * baseCurrency = fxRate * counterCurrency)}. * * @param currencyPair the currency pair * @param rates the FX rates for the currency pair * @return an array of FX rates for a currency pair */ public static FxRateScenarioArray of(CurrencyPair currencyPair, DoubleArray rates) { return new FxRateScenarioArray(currencyPair, rates); }
/** * Derives a set of FX rates from these rates and another set of rates. * <p> * For example, given rates for EUR/GBP and EUR/CHF it is possible to derive rates for GBP/CHF. * <p> * There must be exactly one currency in common between the two currency pairs and * each pair must contain two different currencies. The other rates must have the same scenario count * as these rates. * <p> * The returned object contains rates for converting between the two currencies which only appear in * one set of rates. * * @param other the other rates * @return a set of FX rates derived from these rates and the other rates */ public FxRateScenarioArray crossRates(FxRateScenarioArray other) { return pair.cross(other.pair).map(cross -> computeCross(other, cross)) .orElseThrow(() -> new IllegalArgumentException(Messages.format( "Unable to cross when no unique common currency: {} and {}", pair, other.pair))); }
private static void assertArraysEqual(FxRateScenarioArray a1, FxRateScenarioArray a2) { assertThat(a1.getScenarioCount()).isEqualTo(a2.getScenarioCount()); assertThat(a1.getPair()).isEqualTo(a2.getPair()); for (int i = 0; i < a1.getScenarioCount(); i++) { assertThat(a1.fxRate(Currency.GBP, Currency.USD, i)).isEqualTo(a2.fxRate(Currency.GBP, Currency.USD, i), offset(TOLERANCE)); } } }
public void identicalCurrenciesHaveRateOfOne() { assertThrowsIllegalArg( () -> FxRateScenarioArray.of(Currency.EUR, Currency.EUR, DoubleArray.of(1.07, 1.08, 1.09)), "Conversion rate between identical currencies must be one"); }
private FxRateScenarioArray computeCross(FxRateScenarioArray other, CurrencyPair crossPairAC) { // aim is to convert AAA/BBB and BBB/CCC to AAA/CCC Currency currA = crossPairAC.getBase(); Currency currC = crossPairAC.getCounter(); // given the conventional cross rate pair, order the two rates to match boolean crossBaseCurrencyInFx1 = pair.contains(currA); FxRateScenarioArray fxABorBA = crossBaseCurrencyInFx1 ? this : other; FxRateScenarioArray fxBCorCB = crossBaseCurrencyInFx1 ? other : this; // extract the rates, taking the inverse if the pair is in the inverse order DoubleArray ratesAB = fxABorBA.getPair().getBase().equals(currA) ? fxABorBA.rates : fxABorBA.rates.map(v -> 1 / v); DoubleArray ratesBC = fxBCorCB.getPair().getCounter().equals(currC) ? fxBCorCB.rates : fxBCorCB.rates.map(v -> 1 / v); return FxRateScenarioArray.of(crossPairAC, ratesAB.multipliedBy(ratesBC)); }
public void fxRate() { FxRateScenarioArray rates = FxRateScenarioArray.of(CurrencyPair.of(Currency.EUR, Currency.USD), DoubleArray.of(1.07, 1.08, 1.09)); assertThat(rates.fxRate(Currency.EUR, Currency.USD, 0)).isEqualTo(1.07); assertThat(rates.fxRate(Currency.EUR, Currency.USD, 1)).isEqualTo(1.08); assertThat(rates.fxRate(Currency.EUR, Currency.USD, 2)).isEqualTo(1.09); assertThat(rates.fxRate(Currency.USD, Currency.EUR, 0)).isEqualTo(1 / 1.07); assertThat(rates.fxRate(Currency.USD, Currency.EUR, 1)).isEqualTo(1 / 1.08); assertThat(rates.fxRate(Currency.USD, Currency.EUR, 2)).isEqualTo(1 / 1.09); }
public void crossRates() { FxRateScenarioArray eurGbp = FxRateScenarioArray.of(Currency.EUR, Currency.GBP, DoubleArray.of(0.76, 0.75)); FxRateScenarioArray eurUsd = FxRateScenarioArray.of(Currency.EUR, Currency.USD, DoubleArray.of(1.11, 1.12)); FxRateScenarioArray gbpEur = FxRateScenarioArray.of(Currency.GBP, Currency.EUR, DoubleArray.of(1 / 0.76, 1 / 0.75)); FxRateScenarioArray usdEur = FxRateScenarioArray.of(Currency.USD, Currency.EUR, DoubleArray.of(1 / 1.11, 1 / 1.12)); FxRateScenarioArray expectedGbpUsd = FxRateScenarioArray.of(Currency.GBP, Currency.USD, DoubleArray.of(1.460526315789474, 1.4933333333333334)); assertArraysEqual(eurGbp.crossRates(eurUsd), expectedGbpUsd); assertArraysEqual(eurGbp.crossRates(usdEur), expectedGbpUsd); assertArraysEqual(gbpEur.crossRates(eurUsd), expectedGbpUsd); assertArraysEqual(gbpEur.crossRates(usdEur), expectedGbpUsd); assertArraysEqual(eurUsd.crossRates(eurGbp), expectedGbpUsd); assertArraysEqual(usdEur.crossRates(eurGbp), expectedGbpUsd); assertArraysEqual(eurUsd.crossRates(gbpEur), expectedGbpUsd); assertArraysEqual(usdEur.crossRates(gbpEur), expectedGbpUsd); }
@Override public double fxRate(Currency baseCurrency, Currency counterCurrency) { if (baseCurrency.equals(counterCurrency)) { return 1; } if (baseCurrency.equals(rates1.getPair().getBase())) { return rates1.fxRate(baseCurrency, counterCurrency, scenarioIndex); } else if (baseCurrency.equals(rates2.getPair().getBase())) { return rates2.fxRate(baseCurrency, counterCurrency, scenarioIndex); } else { return rates3.fxRate(baseCurrency, counterCurrency, scenarioIndex); } } };
@Override protected Object propertyGet(Bean bean, String propertyName, boolean quiet) { switch (propertyName.hashCode()) { case 3433178: // pair return ((FxRateScenarioArray) bean).getPair(); case 108285843: // rates return ((FxRateScenarioArray) bean).getRates(); } return super.propertyGet(bean, propertyName, quiet); }
@Override public int getScenarioCount() { return rates1.getScenarioCount(); }
private FxRateScenarioArray( CurrencyPair pair, DoubleArray rates) { JodaBeanUtils.notNull(pair, "pair"); JodaBeanUtils.notNull(rates, "rates"); this.pair = pair; this.rates = rates; validate(); }
public void coverage() { FxRateScenarioArray rates1 = FxRateScenarioArray.of(Currency.EUR, Currency.USD, DoubleArray.of(1.07, 1.08, 1.09)); FxRateScenarioArray rates2 = FxRateScenarioArray.of(Currency.GBP, Currency.USD, DoubleArray.of(1.46, 1.47, 1.48)); coverImmutableBean(rates1); coverBeanEquals(rates1, rates2); }
public void convert() { FxRateScenarioArray eurGbp = FxRateScenarioArray.of(Currency.EUR, Currency.GBP, DoubleArray.of(0.76, 0.75)); DoubleArray input = DoubleArray.of(1.11, 1.12); DoubleArray expected = DoubleArray.of(1.11 * 0.76, 1.12 * 0.75); DoubleArray converted = eurGbp.convert(input, Currency.EUR, Currency.GBP); for (int i = 0; i < converted.size(); i++) { assertThat(converted.get(i)).isEqualTo(expected.get(i), offset(TOLERANCE)); } }
public void crossRatesInvalidInputs() { // Argument has both currencies the same assertThrowsIllegalArg(() -> FxRateScenarioArray.of(Currency.GBP, Currency.USD, DoubleArray.of(1)) .crossRates(FxRateScenarioArray.of(Currency.EUR, Currency.EUR, DoubleArray.of(1)))); // Receiver has both currencies the same assertThrowsIllegalArg(() -> FxRateScenarioArray.of(Currency.GBP, Currency.GBP, DoubleArray.of(1)) .crossRates(FxRateScenarioArray.of(Currency.EUR, Currency.USD, DoubleArray.of(1)))); // No currency in common assertThrowsIllegalArg(() -> FxRateScenarioArray.of(Currency.GBP, Currency.CHF, DoubleArray.of(1)) .crossRates(FxRateScenarioArray.of(Currency.EUR, Currency.USD, DoubleArray.of(1)))); // Both pairs the same assertThrowsIllegalArg(() -> FxRateScenarioArray.of(Currency.GBP, Currency.CHF, DoubleArray.of(1)) .crossRates(FxRateScenarioArray.of(Currency.GBP, Currency.CHF, DoubleArray.of(1)))); // Different length arrays assertThrowsIllegalArg(() -> FxRateScenarioArray.of(Currency.GBP, Currency.CHF, DoubleArray.of(1)) .crossRates(FxRateScenarioArray.of(Currency.EUR, Currency.CHF, DoubleArray.of(1, 2)))); }
public void notConvertible() { FxRateScenarioArray rates = FxRateScenarioArray.of(GBP, USD, DoubleArray.of(1.61, 1.62, 1.63)); ScenarioFxRateProvider fxProvider = new TestScenarioFxRateProvider(rates); List<String> values = ImmutableList.of("a", "b", "c"); DefaultScenarioArray<String> test = DefaultScenarioArray.of(values); ScenarioArray<?> convertedList = test.convertedTo(Currency.GBP, fxProvider); assertThat(convertedList).isEqualTo(test); }
public void convert_inverse() { FxRateScenarioArray eurGbp = FxRateScenarioArray.of(Currency.EUR, Currency.GBP, DoubleArray.of(0.76, 0.75)); DoubleArray input = DoubleArray.of(1.11, 1.12); DoubleArray expected = DoubleArray.of(1.11 * 1 / 0.76, 1.12 * 1 / 0.75); DoubleArray converted = eurGbp.convert(input, Currency.GBP, Currency.EUR); for (int i = 0; i < converted.size(); i++) { assertThat(converted.get(i)).isEqualTo(expected.get(i), offset(TOLERANCE)); } }
@Override public FxRateScenarioArray build() { return new FxRateScenarioArray( pair, rates); }