@Override public Optional<Currency> defaultReportingCurrency(FxSwapTrade target) { Currency base = target.getProduct().getNearLeg().getBaseCurrencyAmount().getCurrency(); Currency counter = target.getProduct().getNearLeg().getCounterCurrencyAmount().getCurrency(); CurrencyPair marketConventionPair = CurrencyPair.of(base, counter).toConventional(); return Optional.of(marketConventionPair.getBase()); }
/** * Obtains an instance from currency pair, reference currency, reference date and sensitivity * value. * * <p>The sensitivity currency is defaulted to be a currency of the currency pair that is not the * reference currency. * * @param currencyPair the currency pair * @param referenceCurrency the reference currency * @param referenceDate the reference date * @param sensitivity the value of the sensitivity * @return the point sensitivity object */ public static FxForwardSensitivity of( CurrencyPair currencyPair, Currency referenceCurrency, LocalDate referenceDate, double sensitivity) { boolean inverse = referenceCurrency.equals(currencyPair.getCounter()); CurrencyPair pair = inverse ? currencyPair.inverse() : currencyPair; Currency sensitivityCurrency = pair.getCounter(); return new FxForwardSensitivity( currencyPair, referenceCurrency, referenceDate, sensitivityCurrency, sensitivity); }
// ------------------------------------------------------------------------- @ImmutableValidator private void validate() { if (!currencyPair.contains(referenceCurrency)) { throw new IllegalArgumentException( Messages.format( "Reference currency {} must be one of those in the currency pair {}", referenceCurrency, currencyPair)); } }
@Override public int compareKey(PointSensitivity other) { if (other instanceof FxForwardSensitivity) { FxForwardSensitivity otherFx = (FxForwardSensitivity) other; return ComparisonChain.start() .compare(currencyPair.toString(), otherFx.currencyPair.toString()) .compare(currency, otherFx.currency) .compare(referenceCurrency, otherFx.referenceCurrency) .compare(referenceDate, otherFx.referenceDate) .result(); } return getClass().getSimpleName().compareTo(other.getClass().getSimpleName()); }
// ------------------------------------------------------------------------- public void coverage() { DiscountFxForwardRates test1 = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); coverImmutableBean(test1); DiscountFxForwardRates test2 = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE.inverse(), DFCURVE_GBP2, DFCURVE_USD2); coverBeanEquals(test1, test2); DiscountFxForwardRates test3 = DiscountFxForwardRates.of( CurrencyPair.of(USD, EUR), FxRate.of(EUR, USD, 1.2d), DFCURVE_USD, ZeroRateDiscountFactors.of(EUR, DATE_VAL, CURVE2)); coverBeanEquals(test1, test3); }
// ------------------------------------------------------------------------- public void test_convertedTo() { BondFutureOptionSensitivity base = BondFutureOptionSensitivity.of( NAME, OPTION_EXPIRY, FUTURE_EXPIRY, STRIKE_PRICE, FUTURE_PRICE, GBP, SENSITIVITY); double rate = 1.5d; FxMatrix matrix = FxMatrix.of(CurrencyPair.of(GBP, USD), rate); BondFutureOptionSensitivity test1 = (BondFutureOptionSensitivity) base.convertedTo(USD, matrix); BondFutureOptionSensitivity expected = BondFutureOptionSensitivity.of( NAME, OPTION_EXPIRY, FUTURE_EXPIRY, STRIKE_PRICE, FUTURE_PRICE, USD, SENSITIVITY * rate); assertEquals(test1, expected); BondFutureOptionSensitivity test2 = (BondFutureOptionSensitivity) base.convertedTo(GBP, matrix); assertEquals(test2, base); }
/** * Gets the currency counter to the reference currency. * * <p>The currency pair contains two currencies. One is the reference currency. This method * returns the other. * * @return the counter currency */ public Currency getReferenceCounterCurrency() { boolean inverse = referenceCurrency.equals(currencyPair.getBase()); return inverse ? currencyPair.getCounter() : currencyPair.getBase(); }
/** * Gets the FX rate for the specified currency pair on the valuation date. * * <p>The rate returned is the rate from the base currency to the counter currency as defined by * this formula: {@code (1 * baseCurrency = fxRate * counterCurrency)}. * * @param currencyPair the ordered currency pair defining the rate required * @return the current FX rate for the currency pair * @throws IllegalArgumentException if the rate is not available */ @Override public default double fxRate(CurrencyPair currencyPair) { return fxRate(currencyPair.getBase(), currencyPair.getCounter()); }
/** Test {@link BlackFxVanillaOptionTradePricer}. */ @Test public class BlackFxVanillaOptionTradePricerTest { private static final LocalDate VAL_DATE = RatesProviderDataSets.VAL_DATE_2014_01_22; private static final LocalTime VAL_TIME = LocalTime.of(13, 45); private static final ZoneId ZONE = ZoneId.of("Z"); private static final ZonedDateTime VAL_DATE_TIME = VAL_DATE.atTime(VAL_TIME).atZone(ZONE); private static final ZonedDateTime EXPIRY = ZonedDateTime.of(2014, 5, 9, 13, 10, 0, 0, ZONE); private static final FxMatrix FX_MATRIX = RatesProviderFxDataSets.fxMatrix(); private static final RatesProvider RATES_PROVIDER = RatesProviderFxDataSets.createProviderEURUSD(VAL_DATE); private static final DoubleArray TIME_TO_EXPIRY = DoubleArray.of(0.01, 0.252, 0.501, 1.0, 2.0, 5.0); private static final DoubleArray ATM = DoubleArray.of(0.175, 0.185, 0.18, 0.17, 0.16, 0.16); private static final DoubleArray DELTA = DoubleArray.of(0.10, 0.25); private static final DoubleMatrix RISK_REVERSAL = DoubleMatrix.ofUnsafe( new double[][] { {-0.010, -0.0050}, {-0.011, -0.0060}, {-0.012, -0.0070}, {-0.013, -0.0080}, {-0.014, -0.0090}, {-0.014, -0.0090} }); private static final DoubleMatrix STRANGLE = DoubleMatrix.ofUnsafe( new double[][] { {0.0300, 0.0100}, {0.0310, 0.0110}, {0.0320, 0.0120}, {0.0330, 0.0130}, {0.0340, 0.0140}, {0.0340, 0.0140} }); private static final InterpolatedStrikeSmileDeltaTermStructure SMILE_TERM = InterpolatedStrikeSmileDeltaTermStructure.of( TIME_TO_EXPIRY, DELTA, ATM, RISK_REVERSAL, STRANGLE, ACT_365F); private static final CurrencyPair CURRENCY_PAIR = CurrencyPair.of(EUR, USD); private static final BlackFxOptionSmileVolatilities VOLS = BlackFxOptionSmileVolatilities.of( FxOptionVolatilitiesName.of("Test"), CURRENCY_PAIR, VAL_DATE_TIME, SMILE_TERM); private static final LocalDate PAYMENT_DATE = LocalDate.of(2014, 5, 13); private static final double NOTIONAL = 1.0e6; private static final CurrencyAmount EUR_AMOUNT = CurrencyAmount.of(EUR, NOTIONAL); private static final CurrencyAmount USD_AMOUNT = CurrencyAmount.of(USD, -NOTIONAL * FX_MATRIX.fxRate(EUR, USD)); private static final ResolvedFxSingle FX_PRODUCT = ResolvedFxSingle.of(EUR_AMOUNT, USD_AMOUNT, PAYMENT_DATE); private static final ResolvedFxVanillaOption OPTION_PRODUCT = ResolvedFxVanillaOption.builder() .longShort(SHORT) .expiry(EXPIRY) .underlying(FX_PRODUCT) .build(); private static final TradeInfo TRADE_INFO = TradeInfo.builder().tradeDate(VAL_DATE).build(); private static final LocalDate CASH_SETTLE_DATE = LocalDate.of(2014, 1, 25); private static final Payment PREMIUM = Payment.of(EUR, NOTIONAL * 0.027, CASH_SETTLE_DATE); private static final ResolvedFxVanillaOptionTrade OPTION_TRADE = ResolvedFxVanillaOptionTrade.builder() .premium(PREMIUM) .product(OPTION_PRODUCT) .info(TRADE_INFO) .build(); private static final BlackFxVanillaOptionProductPricer PRICER_PRODUCT = BlackFxVanillaOptionProductPricer.DEFAULT; private static final BlackFxVanillaOptionTradePricer PRICER_TRADE = BlackFxVanillaOptionTradePricer.DEFAULT; private static final DiscountingPaymentPricer PRICER_PAYMENT = DiscountingPaymentPricer.DEFAULT; private static final double TOL = 1.0e-13; public void test_presentValue() { MultiCurrencyAmount pvSensiTrade = PRICER_TRADE.presentValue(OPTION_TRADE, RATES_PROVIDER, VOLS); CurrencyAmount pvSensiProduct = PRICER_PRODUCT.presentValue(OPTION_PRODUCT, RATES_PROVIDER, VOLS); CurrencyAmount pvSensiPremium = PRICER_PAYMENT.presentValue(PREMIUM, RATES_PROVIDER); assertEquals(pvSensiTrade, MultiCurrencyAmount.of(pvSensiProduct, pvSensiPremium)); } public void test_presentValueSensitivity() { PointSensitivities pvSensiTrade = PRICER_TRADE.presentValueSensitivityRates(OPTION_TRADE, RATES_PROVIDER, VOLS); PointSensitivities pvSensiProduct = PRICER_PRODUCT.presentValueSensitivityRates(OPTION_PRODUCT, RATES_PROVIDER, VOLS); PointSensitivities pvSensiPremium = PRICER_PAYMENT.presentValueSensitivity(PREMIUM, RATES_PROVIDER).build(); assertEquals(pvSensiTrade, pvSensiProduct.combinedWith(pvSensiPremium)); } public void test_presentValueSensitivityBlackVolatility() { PointSensitivities pvSensiTrade = PRICER_TRADE.presentValueSensitivityModelParamsVolatility( OPTION_TRADE, RATES_PROVIDER, VOLS); PointSensitivities pvSensiProduct = PRICER_PRODUCT .presentValueSensitivityModelParamsVolatility(OPTION_PRODUCT, RATES_PROVIDER, VOLS) .build(); assertEquals(pvSensiTrade, pvSensiProduct); } public void test_currencyExposure() { MultiCurrencyAmount ceComputed = PRICER_TRADE.currencyExposure(OPTION_TRADE, RATES_PROVIDER, VOLS); PointSensitivities point = PRICER_TRADE.presentValueSensitivityRates(OPTION_TRADE, RATES_PROVIDER, VOLS); MultiCurrencyAmount pv = PRICER_TRADE.presentValue(OPTION_TRADE, RATES_PROVIDER, VOLS); MultiCurrencyAmount ceExpected = RATES_PROVIDER.currencyExposure(point).plus(pv); assertEquals(ceComputed.size(), 2); assertEquals( ceComputed.getAmount(EUR).getAmount(), ceExpected.getAmount(EUR).getAmount(), TOL * NOTIONAL); assertEquals( ceComputed.getAmount(USD).getAmount(), ceExpected.getAmount(USD).getAmount(), TOL * NOTIONAL); } public void test_currentCash_zero() { assertEquals( PRICER_TRADE.currentCash(OPTION_TRADE, VAL_DATE), CurrencyAmount.zero(PREMIUM.getCurrency())); } public void test_currentCash_onSettle() { assertEquals(PRICER_TRADE.currentCash(OPTION_TRADE, CASH_SETTLE_DATE), PREMIUM.getValue()); } }
/** Test {@link DiscountFxForwardRates}. */ @Test public class DiscountFxForwardRatesTest { private static final LocalDate DATE_VAL = date(2015, 6, 4); private static final LocalDate DATE_REF = date(2015, 7, 30); private static final FxRate FX_RATE = FxRate.of(GBP, USD, 1.5d); private static final CurrencyPair CURRENCY_PAIR = CurrencyPair.of(GBP, USD); private static final CurveInterpolator INTERPOLATOR = CurveInterpolators.LINEAR; private static final CurveMetadata METADATA1 = Curves.zeroRates("TestCurve", ACT_365F); private static final CurveMetadata METADATA2 = Curves.zeroRates("TestCurveUSD", ACT_365F); private static final InterpolatedNodalCurve CURVE1 = InterpolatedNodalCurve.of( METADATA1, DoubleArray.of(0, 10), DoubleArray.of(0.01, 0.02), INTERPOLATOR); private static final InterpolatedNodalCurve CURVE2 = InterpolatedNodalCurve.of( METADATA2, DoubleArray.of(0, 10), DoubleArray.of(0.015, 0.025), INTERPOLATOR); private static final ZeroRateDiscountFactors DFCURVE_GBP = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE1); private static final ZeroRateDiscountFactors DFCURVE_GBP2 = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE2); private static final ZeroRateDiscountFactors DFCURVE_USD = ZeroRateDiscountFactors.of(USD, DATE_VAL, CURVE2); private static final ZeroRateDiscountFactors DFCURVE_USD2 = ZeroRateDiscountFactors.of(USD, DATE_VAL, CURVE1); // ------------------------------------------------------------------------- public void test_of() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); assertEquals(test.getCurrencyPair(), CURRENCY_PAIR); assertEquals(test.getValuationDate(), DATE_VAL); assertEquals(test.getBaseCurrencyDiscountFactors(), DFCURVE_GBP); assertEquals(test.getCounterCurrencyDiscountFactors(), DFCURVE_USD); assertEquals(test.getFxRateProvider(), FX_RATE); assertEquals(test.findData(CURVE1.getName()), Optional.of(CURVE1)); assertEquals(test.findData(CURVE2.getName()), Optional.of(CURVE2)); assertEquals(test.findData(CurveName.of("Rubbish")), Optional.empty()); int baseSize = DFCURVE_USD.getParameterCount(); assertEquals(test.getParameterCount(), DFCURVE_GBP.getParameterCount() + baseSize); assertEquals(test.getParameter(0), DFCURVE_GBP.getParameter(0)); assertEquals(test.getParameter(baseSize), DFCURVE_USD.getParameter(0)); assertEquals(test.getParameterMetadata(0), DFCURVE_GBP.getParameterMetadata(0)); assertEquals(test.getParameterMetadata(baseSize), DFCURVE_USD.getParameterMetadata(0)); assertEquals( test.withParameter(0, 1d).getBaseCurrencyDiscountFactors(), DFCURVE_GBP.withParameter(0, 1d)); assertEquals(test.withParameter(0, 1d).getCounterCurrencyDiscountFactors(), DFCURVE_USD); assertEquals(test.withParameter(baseSize, 1d).getBaseCurrencyDiscountFactors(), DFCURVE_GBP); assertEquals( test.withParameter(baseSize, 1d).getCounterCurrencyDiscountFactors(), DFCURVE_USD.withParameter(0, 1d)); assertEquals( test.withPerturbation((i, v, m) -> v + 1d).getBaseCurrencyDiscountFactors(), DFCURVE_GBP.withPerturbation((i, v, m) -> v + 1d)); assertEquals( test.withPerturbation((i, v, m) -> v + 1d).getCounterCurrencyDiscountFactors(), DFCURVE_USD.withPerturbation((i, v, m) -> v + 1d)); } public void test_of_nonMatchingCurrency() { assertThrowsIllegalArg( () -> DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_GBP)); assertThrowsIllegalArg( () -> DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_USD, DFCURVE_USD)); } public void test_of_nonMatchingValuationDates() { DiscountFactors curve2 = ZeroRateDiscountFactors.of(USD, DATE_REF, CURVE2); assertThrowsIllegalArg( () -> DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, curve2)); } public void test_builder() { assertThrowsIllegalArg( () -> DiscountFxForwardRates.meta() .builder() .setString(DiscountFxForwardRates.meta().currencyPair(), "GBP/USD") .build()); assertThrowsIllegalArg( () -> DiscountFxForwardRates.meta() .builder() .setString(DiscountFxForwardRates.meta().currencyPair().name(), "GBP/USD") .build()); } // ------------------------------------------------------------------------- public void test_withDiscountFactors() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); test = test.withDiscountFactors(DFCURVE_GBP2, DFCURVE_USD2); assertEquals(test.getCurrencyPair(), CURRENCY_PAIR); assertEquals(test.getValuationDate(), DATE_VAL); assertEquals(test.getBaseCurrencyDiscountFactors(), DFCURVE_GBP2); assertEquals(test.getCounterCurrencyDiscountFactors(), DFCURVE_USD2); assertEquals(test.getFxRateProvider(), FX_RATE); } // ------------------------------------------------------------------------- public void test_rate() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); double dfCcyBaseAtMaturity = DFCURVE_GBP.discountFactor(DATE_REF); double dfCcyCounterAtMaturity = DFCURVE_USD.discountFactor(DATE_REF); double expected = FX_RATE.fxRate(GBP, USD) * (dfCcyBaseAtMaturity / dfCcyCounterAtMaturity); assertEquals(test.rate(GBP, DATE_REF), expected, 1e-12); assertEquals(test.rate(USD, DATE_REF), 1d / expected, 1e-12); } public void test_rate_nonMatchingCurrency() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); assertThrowsIllegalArg(() -> test.rate(EUR, DATE_VAL)); } // ------------------------------------------------------------------------- public void test_ratePointSensitivity() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); assertEquals( test.ratePointSensitivity(GBP, DATE_REF), FxForwardSensitivity.of(CURRENCY_PAIR, GBP, DATE_REF, 1d)); assertEquals( test.ratePointSensitivity(USD, DATE_REF), FxForwardSensitivity.of(CURRENCY_PAIR, USD, DATE_REF, 1d)); } public void test_ratePointSensitivity_nonMatchingCurrency() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); assertThrowsIllegalArg(() -> test.ratePointSensitivity(EUR, DATE_VAL)); } // ------------------------------------------------------------------------- public void test_rateFxSpotSensitivity() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); double dfCcyBaseAtMaturity = DFCURVE_GBP.discountFactor(DATE_REF); double dfCcyCounterAtMaturity = DFCURVE_USD.discountFactor(DATE_REF); double expected = dfCcyBaseAtMaturity / dfCcyCounterAtMaturity; assertEquals(test.rateFxSpotSensitivity(GBP, DATE_REF), expected, 1e-12); assertEquals(test.rateFxSpotSensitivity(USD, DATE_REF), 1d / expected, 1e-12); } public void test_rateFxSpotSensitivity_nonMatchingCurrency() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); assertThrowsIllegalArg(() -> test.rateFxSpotSensitivity(EUR, DATE_VAL)); } // ------------------------------------------------------------------------- // proper end-to-end tests are elsewhere public void test_parameterSensitivity() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); FxForwardSensitivity point = FxForwardSensitivity.of(CURRENCY_PAIR, GBP, DATE_VAL, 1d); assertEquals(test.parameterSensitivity(point).size(), 2); FxForwardSensitivity point2 = FxForwardSensitivity.of(CURRENCY_PAIR, USD, DATE_VAL, 1d); assertEquals(test.parameterSensitivity(point2).size(), 2); } // ------------------------------------------------------------------------- public void coverage() { DiscountFxForwardRates test1 = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); coverImmutableBean(test1); DiscountFxForwardRates test2 = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE.inverse(), DFCURVE_GBP2, DFCURVE_USD2); coverBeanEquals(test1, test2); DiscountFxForwardRates test3 = DiscountFxForwardRates.of( CurrencyPair.of(USD, EUR), FxRate.of(EUR, USD, 1.2d), DFCURVE_USD, ZeroRateDiscountFactors.of(EUR, DATE_VAL, CURVE2)); coverBeanEquals(test1, test3); } }