/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.impl.swap; import static com.opengamma.strata.basics.currency.Currency.GBP; import static com.opengamma.strata.basics.currency.Currency.USD; import static com.opengamma.strata.basics.date.DayCounts.ACT_365F; import static com.opengamma.strata.basics.index.FxIndices.GBP_USD_WM; import static com.opengamma.strata.basics.index.IborIndices.GBP_LIBOR_3M; import static com.opengamma.strata.pricer.datasets.RatesProviderDataSets.FX_MATRIX_GBP_USD; import static com.opengamma.strata.pricer.datasets.RatesProviderDataSets.MULTI_GBP_USD; import static com.opengamma.strata.pricer.datasets.RatesProviderDataSets.MULTI_GBP_USD_SIMPLE; import static com.opengamma.strata.pricer.datasets.RatesProviderDataSets.VAL_DATE_2014_01_22; import static java.time.temporal.ChronoUnit.DAYS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; import com.google.common.math.DoubleMath; import com.opengamma.strata.basics.ReferenceData; import com.opengamma.strata.basics.currency.Currency; import com.opengamma.strata.basics.currency.CurrencyAmount; import com.opengamma.strata.basics.currency.FxMatrix; import com.opengamma.strata.basics.currency.MultiCurrencyAmount; import com.opengamma.strata.basics.date.DayCount; import com.opengamma.strata.basics.date.DayCounts; import com.opengamma.strata.basics.index.FxIndexObservation; import com.opengamma.strata.basics.index.FxIndices; import com.opengamma.strata.basics.index.IborIndex; import com.opengamma.strata.basics.index.IborIndexObservation; import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeries; import com.opengamma.strata.market.explain.ExplainKey; import com.opengamma.strata.market.explain.ExplainMap; import com.opengamma.strata.market.explain.ExplainMapBuilder; import com.opengamma.strata.market.param.CurrencyParameterSensitivities; import com.opengamma.strata.market.sensitivity.PointSensitivities; import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder; import com.opengamma.strata.pricer.DiscountFactors; import com.opengamma.strata.pricer.ZeroRateSensitivity; import com.opengamma.strata.pricer.datasets.RatesProviderDataSets; import com.opengamma.strata.pricer.fx.FxIndexRates; import com.opengamma.strata.pricer.rate.IborRateSensitivity; import com.opengamma.strata.pricer.rate.ImmutableRatesProvider; import com.opengamma.strata.pricer.rate.RateComputationFn; import com.opengamma.strata.pricer.rate.RatesProvider; import com.opengamma.strata.pricer.rate.SimpleRatesProvider; import com.opengamma.strata.pricer.sensitivity.RatesFiniteDifferenceSensitivityCalculator; import com.opengamma.strata.product.rate.FixedRateComputation; import com.opengamma.strata.product.rate.IborRateComputation; import com.opengamma.strata.product.rate.RateComputation; import com.opengamma.strata.product.swap.CompoundingMethod; import com.opengamma.strata.product.swap.FxReset; import com.opengamma.strata.product.swap.NegativeRateMethod; import com.opengamma.strata.product.swap.RateAccrualPeriod; import com.opengamma.strata.product.swap.RatePaymentPeriod; /** * Test {@link DiscountingRatePaymentPeriodPricer} */ @Test public class DiscountingRatePaymentPeriodPricerTest { private static final ReferenceData REF_DATA = ReferenceData.standard(); private static final LocalDate VAL_DATE = VAL_DATE_2014_01_22; private static final DayCount DAY_COUNT = DayCounts.ACT_360; private static final LocalDate FX_DATE_1 = LocalDate.of(2014, 1, 22); private static final LocalDate CPN_DATE_1 = LocalDate.of(2014, 1, 24); private static final LocalDate CPN_DATE_2 = LocalDate.of(2014, 4, 24); private static final LocalDate CPN_DATE_3 = LocalDate.of(2014, 7, 24); private static final LocalDate CPN_DATE_4 = LocalDate.of(2014, 10, 24); private static final double ACCRUAL_FACTOR_1 = 0.245; private static final double ACCRUAL_FACTOR_2 = 0.255; private static final double ACCRUAL_FACTOR_3 = 0.25; private static final double GEARING = 2.0; private static final double SPREAD = -0.0025; private static final double NOTIONAL_100 = 1.0E8; private static final LocalDate PAYMENT_DATE_1 = LocalDate.of(2014, 4, 26); private static final LocalDate PAYMENT_DATE_3 = LocalDate.of(2014, 10, 26); private static final double RATE_1 = 0.0123d; private static final double RATE_2 = 0.0127d; private static final double RATE_3 = 0.0135d; private static final double RATE_FX = 1.6d; private static final double DISCOUNT_FACTOR = 0.976d; private static final double TOLERANCE_PV = 1E-7; private static final double EPS_FD = 1.0e-7; private static final RatesFiniteDifferenceSensitivityCalculator CAL_FD = new RatesFiniteDifferenceSensitivityCalculator(EPS_FD); private static final double FX_RATE = FX_MATRIX_GBP_USD.fxRate(GBP, USD); private static final FxMatrix FX_MATRIX_BUMP = FxMatrix.of(GBP, USD, FX_MATRIX_GBP_USD.fxRate(GBP, USD) + EPS_FD); private static final RateAccrualPeriod ACCRUAL_PERIOD_1 = RateAccrualPeriod.builder() .startDate(CPN_DATE_1) .endDate(CPN_DATE_2) .yearFraction(ACCRUAL_FACTOR_1) .rateComputation(FixedRateComputation.of(RATE_1)) .build(); private static final RateAccrualPeriod ACCRUAL_PERIOD_1_GS = RateAccrualPeriod.builder() .startDate(CPN_DATE_1) .endDate(CPN_DATE_2) .yearFraction(ACCRUAL_FACTOR_1) .rateComputation(FixedRateComputation.of(RATE_1)) .gearing(GEARING) .spread(SPREAD) .build(); private static final RateAccrualPeriod ACCRUAL_PERIOD_1_NEG = RateAccrualPeriod.builder() .startDate(CPN_DATE_1) .endDate(CPN_DATE_2) .yearFraction(ACCRUAL_FACTOR_1) .rateComputation(FixedRateComputation.of(RATE_1)) .gearing(-1d) .negativeRateMethod(NegativeRateMethod.NOT_NEGATIVE) .build(); private static final RateAccrualPeriod ACCRUAL_PERIOD_2_GS = RateAccrualPeriod.builder() .startDate(CPN_DATE_2) .endDate(CPN_DATE_3) .yearFraction(ACCRUAL_FACTOR_2) .rateComputation(FixedRateComputation.of(RATE_2)) .gearing(GEARING) .spread(SPREAD) .build(); private static final RateAccrualPeriod ACCRUAL_PERIOD_3_GS = RateAccrualPeriod.builder() .startDate(CPN_DATE_3) .endDate(CPN_DATE_4) .yearFraction(ACCRUAL_FACTOR_3) .rateComputation(FixedRateComputation.of(RATE_3)) .gearing(GEARING) .spread(SPREAD) .build(); private static final RatePaymentPeriod PAYMENT_PERIOD_1 = RatePaymentPeriod.builder() .paymentDate(PAYMENT_DATE_1) .accrualPeriods(ImmutableList.of(ACCRUAL_PERIOD_1)) .dayCount(ACT_365F) .currency(USD) .notional(NOTIONAL_100) .build(); private static final RatePaymentPeriod PAYMENT_PERIOD_1_FX = RatePaymentPeriod.builder() .paymentDate(PAYMENT_DATE_1) .accrualPeriods(ImmutableList.of(ACCRUAL_PERIOD_1)) .dayCount(ACT_365F) .currency(USD) .notional(NOTIONAL_100) .fxReset(FxReset.of(FxIndexObservation.of(GBP_USD_WM, FX_DATE_1, REF_DATA), GBP)) .build(); private static final RatePaymentPeriod PAYMENT_PERIOD_1_GS = RatePaymentPeriod.builder() .paymentDate(PAYMENT_DATE_1) .accrualPeriods(ImmutableList.of(ACCRUAL_PERIOD_1_GS)) .dayCount(ACT_365F) .currency(USD) .notional(NOTIONAL_100) .build(); private static final RatePaymentPeriod PAYMENT_PERIOD_1_NEG = RatePaymentPeriod.builder() .paymentDate(PAYMENT_DATE_1) .accrualPeriods(ImmutableList.of(ACCRUAL_PERIOD_1_NEG)) .dayCount(ACT_365F) .currency(USD) .notional(NOTIONAL_100) .build(); private static final RatePaymentPeriod PAYMENT_PERIOD_FULL_GS = RatePaymentPeriod.builder() .paymentDate(PAYMENT_DATE_3) .accrualPeriods(ImmutableList.of(ACCRUAL_PERIOD_1_GS, ACCRUAL_PERIOD_2_GS, ACCRUAL_PERIOD_3_GS)) .dayCount(ACT_365F) .currency(USD) .notional(NOTIONAL_100) .build(); private static final RatePaymentPeriod PAYMENT_PERIOD_FULL_GS_FX_USD = RatePaymentPeriod.builder() .paymentDate(PAYMENT_DATE_3) .accrualPeriods(ImmutableList.of(ACCRUAL_PERIOD_1_GS, ACCRUAL_PERIOD_2_GS, ACCRUAL_PERIOD_3_GS)) .dayCount(ACT_365F) .currency(USD) .notional(NOTIONAL_100) .fxReset(FxReset.of(FxIndexObservation.of(GBP_USD_WM, FX_DATE_1, REF_DATA), GBP)) .build(); private static final RatePaymentPeriod PAYMENT_PERIOD_FULL_GS_FX_GBP = RatePaymentPeriod.builder() .paymentDate(PAYMENT_DATE_3) .accrualPeriods(ImmutableList.of(ACCRUAL_PERIOD_1_GS, ACCRUAL_PERIOD_2_GS, ACCRUAL_PERIOD_3_GS)) .dayCount(ACT_365F) .currency(GBP) .notional(NOTIONAL_100) .fxReset(FxReset.of(FxIndexObservation.of(GBP_USD_WM, FX_DATE_1, REF_DATA), USD)) .build(); // all tests use a fixed rate to avoid excessive use of mocks // rate observation is separated from this class, so nothing is missed in unit test terms // most testing on forecastValue as methods only differ in discountFactor //------------------------------------------------------------------------- public void test_presentValue_single() { SimpleRatesProvider prov = createProvider(VAL_DATE); double pvExpected = RATE_1 * ACCRUAL_FACTOR_1 * NOTIONAL_100 * DISCOUNT_FACTOR; double pvComputed = DiscountingRatePaymentPeriodPricer.DEFAULT.presentValue(PAYMENT_PERIOD_1, prov); assertEquals(pvComputed, pvExpected, TOLERANCE_PV); } //------------------------------------------------------------------------- public void test_forecastValue_single() { SimpleRatesProvider prov = createProvider(VAL_DATE); double fvExpected = RATE_1 * ACCRUAL_FACTOR_1 * NOTIONAL_100; double fvComputed = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(PAYMENT_PERIOD_1, prov); assertEquals(fvComputed, fvExpected, TOLERANCE_PV); } public void test_forecastValue_single_fx() { SimpleRatesProvider prov = createProvider(VAL_DATE); double fvExpected = RATE_1 * ACCRUAL_FACTOR_1 * NOTIONAL_100 * RATE_FX; double fvComputed = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(PAYMENT_PERIOD_1_FX, prov); assertEquals(fvComputed, fvExpected, TOLERANCE_PV); } public void test_forecastValue_single_gearingSpread() { SimpleRatesProvider prov = createProvider(VAL_DATE); double fvExpected = (RATE_1 * GEARING + SPREAD) * ACCRUAL_FACTOR_1 * NOTIONAL_100; double fvComputed = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(PAYMENT_PERIOD_1_GS, prov); assertEquals(fvComputed, fvExpected, TOLERANCE_PV); } public void test_forecastValue_single_gearingNoNegative() { SimpleRatesProvider prov = createProvider(VAL_DATE); double fvComputed = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(PAYMENT_PERIOD_1_NEG, prov); assertEquals(fvComputed, 0d, TOLERANCE_PV); } //------------------------------------------------------------------------- public void test_forecastValue_compoundNone() { SimpleRatesProvider prov = createProvider(VAL_DATE); double fvExpected = ((RATE_1 * GEARING + SPREAD) * ACCRUAL_FACTOR_1 * NOTIONAL_100) + ((RATE_2 * GEARING + SPREAD) * ACCRUAL_FACTOR_2 * NOTIONAL_100) + ((RATE_3 * GEARING + SPREAD) * ACCRUAL_FACTOR_3 * NOTIONAL_100); double fvComputed = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(PAYMENT_PERIOD_FULL_GS, prov); assertEquals(fvComputed, fvExpected, TOLERANCE_PV); } public void test_forecastValue_compoundNone_fx() { SimpleRatesProvider prov = createProvider(VAL_DATE); double fvExpected = ((RATE_1 * GEARING + SPREAD) * ACCRUAL_FACTOR_1 * NOTIONAL_100 * RATE_FX) + ((RATE_2 * GEARING + SPREAD) * ACCRUAL_FACTOR_2 * NOTIONAL_100 * RATE_FX) + ((RATE_3 * GEARING + SPREAD) * ACCRUAL_FACTOR_3 * NOTIONAL_100 * RATE_FX); double fvComputed = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(PAYMENT_PERIOD_FULL_GS_FX_USD, prov); assertEquals(fvComputed, fvExpected, TOLERANCE_PV); } //------------------------------------------------------------------------- public void test_forecastValue_compoundStraight() { SimpleRatesProvider prov = createProvider(VAL_DATE); RatePaymentPeriod period = PAYMENT_PERIOD_FULL_GS.toBuilder() .compoundingMethod(CompoundingMethod.STRAIGHT).build(); double invFactor1 = 1.0d + ACCRUAL_FACTOR_1 * (RATE_1 * GEARING + SPREAD); double invFactor2 = 1.0d + ACCRUAL_FACTOR_2 * (RATE_2 * GEARING + SPREAD); double invFactor3 = 1.0d + ACCRUAL_FACTOR_3 * (RATE_3 * GEARING + SPREAD); double fvExpected = NOTIONAL_100 * (invFactor1 * invFactor2 * invFactor3 - 1.0d); double fvComputed = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(period, prov); assertEquals(fvComputed, fvExpected, TOLERANCE_PV); } public void test_forecastValue_compoundFlat() { SimpleRatesProvider prov = createProvider(VAL_DATE); RatePaymentPeriod period = PAYMENT_PERIOD_FULL_GS.toBuilder() .compoundingMethod(CompoundingMethod.FLAT).build(); double cpa1 = NOTIONAL_100 * ACCRUAL_FACTOR_1 * (RATE_1 * GEARING + SPREAD); double cpa2 = NOTIONAL_100 * ACCRUAL_FACTOR_2 * (RATE_2 * GEARING + SPREAD) + cpa1 * ACCRUAL_FACTOR_2 * (RATE_2 * GEARING); double cpa3 = NOTIONAL_100 * ACCRUAL_FACTOR_3 * (RATE_3 * GEARING + SPREAD) + (cpa1 + cpa2) * ACCRUAL_FACTOR_3 * (RATE_3 * GEARING); double fvExpected = cpa1 + cpa2 + cpa3; double fvComputed = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(period, prov); assertEquals(fvComputed, fvExpected, TOLERANCE_PV); } public void test_forecastValue_compoundFlat_notional() { SimpleRatesProvider prov = createProvider(VAL_DATE); RatePaymentPeriod periodNot = PAYMENT_PERIOD_FULL_GS.toBuilder() .compoundingMethod(CompoundingMethod.FLAT).build(); RatePaymentPeriod period1 = PAYMENT_PERIOD_FULL_GS.toBuilder() .compoundingMethod(CompoundingMethod.FLAT).notional(1.0d).build(); double fvComputedNot = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(periodNot, prov); double fvComputed1 = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(period1, prov); assertEquals(fvComputedNot, fvComputed1 * NOTIONAL_100, TOLERANCE_PV); } public void test_forecastValue_compoundSpreadExclusive() { SimpleRatesProvider prov = createProvider(VAL_DATE); RatePaymentPeriod period = PAYMENT_PERIOD_FULL_GS.toBuilder() .compoundingMethod(CompoundingMethod.SPREAD_EXCLUSIVE).build(); double invFactor1 = 1.0d + ACCRUAL_FACTOR_1 * (RATE_1 * GEARING); double invFactor2 = 1.0d + ACCRUAL_FACTOR_2 * (RATE_2 * GEARING); double invFactor3 = 1.0d + ACCRUAL_FACTOR_3 * (RATE_3 * GEARING); double fvExpected = NOTIONAL_100 * (invFactor1 * invFactor2 * invFactor3 - 1.0d + (ACCRUAL_FACTOR_1 + ACCRUAL_FACTOR_2 + ACCRUAL_FACTOR_3) * SPREAD); double fvComputed = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(period, prov); assertEquals(fvComputed, fvExpected, TOLERANCE_PV); } public void test_forecastValue_compoundSpreadExclusive_fx() { SimpleRatesProvider prov = createProvider(VAL_DATE); RatePaymentPeriod period = PAYMENT_PERIOD_FULL_GS_FX_USD.toBuilder() .compoundingMethod(CompoundingMethod.SPREAD_EXCLUSIVE).build(); double invFactor1 = 1.0d + ACCRUAL_FACTOR_1 * (RATE_1 * GEARING); double invFactor2 = 1.0d + ACCRUAL_FACTOR_2 * (RATE_2 * GEARING); double invFactor3 = 1.0d + ACCRUAL_FACTOR_3 * (RATE_3 * GEARING); double fvExpected = NOTIONAL_100 * RATE_FX * (invFactor1 * invFactor2 * invFactor3 - 1.0d + (ACCRUAL_FACTOR_1 + ACCRUAL_FACTOR_2 + ACCRUAL_FACTOR_3) * SPREAD); double fvComputed = DiscountingRatePaymentPeriodPricer.DEFAULT.forecastValue(period, prov); assertEquals(fvComputed, fvExpected, TOLERANCE_PV); } //------------------------------------------------------------------------- private static final RateAccrualPeriod ACCRUAL_PERIOD_1_FLOATING = RateAccrualPeriod.builder() .startDate(CPN_DATE_1) .endDate(CPN_DATE_2) .yearFraction(ACCRUAL_FACTOR_1) .rateComputation(IborRateComputation.of(GBP_LIBOR_3M, CPN_DATE_1, REF_DATA)) .gearing(GEARING) .spread(SPREAD) .build(); private static final RateAccrualPeriod ACCRUAL_PERIOD_2_FLOATING = RateAccrualPeriod.builder() .startDate(CPN_DATE_2) .endDate(CPN_DATE_3) .yearFraction(ACCRUAL_FACTOR_2) .rateComputation(IborRateComputation.of(GBP_LIBOR_3M, CPN_DATE_2, REF_DATA)) .gearing(GEARING) .spread(SPREAD) .build(); private static final RateAccrualPeriod ACCRUAL_PERIOD_3_FLOATING = RateAccrualPeriod.builder() .startDate(CPN_DATE_3) .endDate(CPN_DATE_4) .yearFraction(ACCRUAL_FACTOR_3) .rateComputation(IborRateComputation.of(GBP_LIBOR_3M, CPN_DATE_3, REF_DATA)) .gearing(GEARING) .spread(SPREAD) .build(); private static final RatePaymentPeriod PAYMENT_PERIOD_FLOATING = RatePaymentPeriod.builder() .paymentDate(PAYMENT_DATE_3) .accrualPeriods(ImmutableList.of(ACCRUAL_PERIOD_1_FLOATING, ACCRUAL_PERIOD_2_FLOATING, ACCRUAL_PERIOD_3_FLOATING)) .dayCount(ACT_365F) .currency(USD) .notional(NOTIONAL_100) .build(); private static final RatePaymentPeriod PAYMENT_PERIOD_COMPOUNDING_STRAIGHT = RatePaymentPeriod.builder() .paymentDate(PAYMENT_DATE_3) .accrualPeriods(ImmutableList.of(ACCRUAL_PERIOD_1_FLOATING, ACCRUAL_PERIOD_2_FLOATING, ACCRUAL_PERIOD_3_FLOATING)) .compoundingMethod(CompoundingMethod.STRAIGHT) .dayCount(ACT_365F) .currency(USD) .notional(NOTIONAL_100) .build(); private static final RatePaymentPeriod PAYMENT_PERIOD_COMPOUNDING_FLAT = RatePaymentPeriod.builder() .paymentDate(PAYMENT_DATE_3) .accrualPeriods(ImmutableList.of(ACCRUAL_PERIOD_1_FLOATING, ACCRUAL_PERIOD_2_FLOATING, ACCRUAL_PERIOD_3_FLOATING)) .compoundingMethod(CompoundingMethod.FLAT) .dayCount(ACT_365F) .currency(USD) .notional(NOTIONAL_100) .build(); private static final RatePaymentPeriod PAYMENT_PERIOD_COMPOUNDING_EXCLUSIVE = RatePaymentPeriod.builder() .paymentDate(PAYMENT_DATE_3) .accrualPeriods(ImmutableList.of(ACCRUAL_PERIOD_1_FLOATING, ACCRUAL_PERIOD_2_FLOATING, ACCRUAL_PERIOD_3_FLOATING)) .compoundingMethod(CompoundingMethod.SPREAD_EXCLUSIVE) .dayCount(ACT_365F) .currency(USD) .notional(NOTIONAL_100) .build(); /** * Test present value sensitivity for ibor, no compounding. */ public void test_presentValueSensitivity_ibor_noCompounding() { LocalDate valDate = PAYMENT_PERIOD_FLOATING.getPaymentDate().minusDays(90); double paymentTime = DAY_COUNT.relativeYearFraction(valDate, PAYMENT_PERIOD_FLOATING.getPaymentDate()); DiscountFactors mockDf = mock(DiscountFactors.class); SimpleRatesProvider simpleProv = new SimpleRatesProvider(valDate, mockDf); simpleProv.setDayCount(DAY_COUNT); RateComputationFn<RateComputation> obsFunc = mock(RateComputationFn.class); when(mockDf.discountFactor(PAYMENT_PERIOD_FLOATING.getPaymentDate())) .thenReturn(DISCOUNT_FACTOR); ZeroRateSensitivity builder = ZeroRateSensitivity.of( PAYMENT_PERIOD_FLOATING.getCurrency(), paymentTime, -DISCOUNT_FACTOR * paymentTime); when(mockDf.zeroRatePointSensitivity(PAYMENT_PERIOD_FLOATING.getPaymentDate())).thenReturn(builder); DiscountingRatePaymentPeriodPricer pricer = new DiscountingRatePaymentPeriodPricer(obsFunc); LocalDate[] dates = new LocalDate[] {CPN_DATE_1, CPN_DATE_2, CPN_DATE_3, CPN_DATE_4}; double[] rates = new double[] {RATE_1, RATE_2, RATE_3}; for (int i = 0; i < 3; ++i) { IborRateComputation rateComputation = (IborRateComputation) PAYMENT_PERIOD_FLOATING.getAccrualPeriods().get(i).getRateComputation(); IborRateSensitivity iborSense = IborRateSensitivity.of(rateComputation.getObservation(), 1d); when(obsFunc.rateSensitivity(rateComputation, dates[i], dates[i + 1], simpleProv)).thenReturn(iborSense); when(obsFunc.rate(rateComputation, dates[i], dates[i + 1], simpleProv)).thenReturn(rates[i]); } PointSensitivities senseComputed = pricer.presentValueSensitivity(PAYMENT_PERIOD_FLOATING, simpleProv).build(); double eps = 1.e-7; List<IborRateSensitivity> senseExpectedList = futureFwdSensitivityFD(simpleProv, PAYMENT_PERIOD_FLOATING, obsFunc, eps); PointSensitivities senseExpected = PointSensitivities.of(senseExpectedList).multipliedBy(DISCOUNT_FACTOR); List<ZeroRateSensitivity> dscExpectedList = dscSensitivityFD(simpleProv, PAYMENT_PERIOD_FLOATING, obsFunc, eps); PointSensitivities senseExpectedDsc = PointSensitivities.of(dscExpectedList); assertTrue(senseComputed.equalWithTolerance( senseExpected.combinedWith(senseExpectedDsc), eps * PAYMENT_PERIOD_FLOATING.getNotional())); } /** * test forecast value sensitivity for ibor, no compounding. */ public void test_forecastValueSensitivity_ibor_noCompounding() { RatesProvider mockProv = mock(RatesProvider.class); RateComputationFn<RateComputation> obsFunc = mock(RateComputationFn.class); when(mockProv.getValuationDate()).thenReturn(VAL_DATE); DiscountingRatePaymentPeriodPricer pricer = new DiscountingRatePaymentPeriodPricer(obsFunc); LocalDate[] dates = new LocalDate[] {CPN_DATE_1, CPN_DATE_2, CPN_DATE_3, CPN_DATE_4}; double[] rates = new double[] {RATE_1, RATE_2, RATE_3}; for (int i = 0; i < 3; ++i) { IborRateComputation rateObs = (IborRateComputation) PAYMENT_PERIOD_FLOATING.getAccrualPeriods().get(i).getRateComputation(); IborRateSensitivity iborSense = IborRateSensitivity.of(rateObs.getObservation(), 1.0d); when(obsFunc.rateSensitivity(rateObs, dates[i], dates[i + 1], mockProv)).thenReturn(iborSense); when(obsFunc.rate(rateObs, dates[i], dates[i + 1], mockProv)).thenReturn(rates[i]); } PointSensitivities senseComputed = pricer.forecastValueSensitivity(PAYMENT_PERIOD_FLOATING, mockProv).build(); double eps = 1.e-7; List<IborRateSensitivity> senseExpectedList = futureFwdSensitivityFD(mockProv, PAYMENT_PERIOD_FLOATING, obsFunc, eps); PointSensitivities senseExpected = PointSensitivities.of(senseExpectedList); assertTrue(senseComputed.equalWithTolerance(senseExpected, eps * PAYMENT_PERIOD_FLOATING.getNotional())); } // test forecast value sensitivity for ibor, with straight, flat and exclusive compounding. @DataProvider(name = "compoundingRatePaymentPeriod") Object[][] data_forecastValueSensitivity_ibor_compounding() { return new Object[][] { {PAYMENT_PERIOD_COMPOUNDING_STRAIGHT}, {PAYMENT_PERIOD_COMPOUNDING_FLAT}, {PAYMENT_PERIOD_COMPOUNDING_EXCLUSIVE}, }; } @Test(dataProvider = "compoundingRatePaymentPeriod") public void test_forecastValueSensitivity_ibor_compounding(RatePaymentPeriod period) { RatesProvider mockProv = mock(RatesProvider.class); RateComputationFn<RateComputation> obsFunc = mock(RateComputationFn.class); when(mockProv.getValuationDate()).thenReturn(VAL_DATE); DiscountingRatePaymentPeriodPricer pricer = new DiscountingRatePaymentPeriodPricer(obsFunc); LocalDate[] dates = new LocalDate[] {CPN_DATE_1, CPN_DATE_2, CPN_DATE_3, CPN_DATE_4}; double[] rates = new double[] {RATE_1, RATE_2, RATE_3}; for (int i = 0; i < 3; ++i) { IborRateComputation rateObs = (IborRateComputation) period.getAccrualPeriods().get(i).getRateComputation(); IborRateSensitivity iborSense = IborRateSensitivity.of(rateObs.getObservation(), 1.0d); when(obsFunc.rateSensitivity(rateObs, dates[i], dates[i + 1], mockProv)).thenReturn(iborSense); when(obsFunc.rate(rateObs, dates[i], dates[i + 1], mockProv)).thenReturn(rates[i]); } PointSensitivities senseComputed = pricer.forecastValueSensitivity(period, mockProv).build(); List<IborRateSensitivity> senseExpectedList = futureFwdSensitivityFD(mockProv, period, obsFunc, EPS_FD); PointSensitivities senseExpected = PointSensitivities.of(senseExpectedList); assertTrue(senseComputed.equalWithTolerance(senseExpected, EPS_FD * period.getNotional())); } //------------------------------------------------------------------------- public void test_forecastValueSensitivity_compoundNone_fx() { DiscountingRatePaymentPeriodPricer pricer = DiscountingRatePaymentPeriodPricer.DEFAULT; ImmutableRatesProvider provider = MULTI_GBP_USD; PointSensitivityBuilder pointSensiComputedUSD = pricer.forecastValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_USD, provider); CurrencyParameterSensitivities sensiComputedUSD = provider.parameterSensitivity(pointSensiComputedUSD.build().normalized()); CurrencyParameterSensitivities sensiExpectedUSD = CAL_FD.sensitivity( provider, (p) -> CurrencyAmount.of(USD, pricer.forecastValue(PAYMENT_PERIOD_FULL_GS_FX_USD, (p)))); assertTrue(sensiComputedUSD.equalWithTolerance( sensiExpectedUSD, EPS_FD * PAYMENT_PERIOD_FULL_GS_FX_USD.getNotional())); PointSensitivityBuilder pointSensiComputedGBP = pricer.forecastValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider); CurrencyParameterSensitivities sensiComputedGBP = provider.parameterSensitivity(pointSensiComputedGBP.build().normalized()); CurrencyParameterSensitivities sensiExpectedGBP = CAL_FD.sensitivity( provider, (p) -> CurrencyAmount.of(GBP, pricer.forecastValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, (p)))); assertTrue(sensiComputedGBP.equalWithTolerance( sensiExpectedGBP, EPS_FD * PAYMENT_PERIOD_FULL_GS_FX_GBP.getNotional())); } public void test_presentValueSensitivity_compoundNone_fx() { DiscountingRatePaymentPeriodPricer pricer = DiscountingRatePaymentPeriodPricer.DEFAULT; ImmutableRatesProvider provider = MULTI_GBP_USD; PointSensitivityBuilder pointSensiComputedUSD = pricer.presentValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_USD, provider); CurrencyParameterSensitivities sensiComputedUSD = provider.parameterSensitivity(pointSensiComputedUSD.build().normalized()); CurrencyParameterSensitivities sensiExpectedUSD = CAL_FD.sensitivity( provider, (p) -> CurrencyAmount.of(USD, pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_USD, (p)))); assertTrue(sensiComputedUSD.equalWithTolerance( sensiExpectedUSD, EPS_FD * PAYMENT_PERIOD_FULL_GS_FX_USD.getNotional())); PointSensitivityBuilder pointSensiComputedGBP = pricer.presentValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider); CurrencyParameterSensitivities sensiComputedGBP = provider.parameterSensitivity(pointSensiComputedGBP.build().normalized()); CurrencyParameterSensitivities sensiExpectedGBP = CAL_FD.sensitivity( provider, (p) -> CurrencyAmount.of(GBP, pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, (p)))); assertTrue(sensiComputedGBP.equalWithTolerance( sensiExpectedGBP, EPS_FD * PAYMENT_PERIOD_FULL_GS_FX_GBP.getNotional())); } public void test_forecastValueSensitivity_compoundNone_fx_dfCurve() { DiscountingRatePaymentPeriodPricer pricer = DiscountingRatePaymentPeriodPricer.DEFAULT; ImmutableRatesProvider provider = MULTI_GBP_USD_SIMPLE; PointSensitivityBuilder pointSensiComputedUSD = pricer.forecastValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_USD, provider); CurrencyParameterSensitivities sensiComputedUSD = provider.parameterSensitivity(pointSensiComputedUSD.build().normalized()); CurrencyParameterSensitivities sensiExpectedUSD = CAL_FD.sensitivity( provider, (p) -> CurrencyAmount.of(USD, pricer.forecastValue(PAYMENT_PERIOD_FULL_GS_FX_USD, (p)))); assertTrue(sensiComputedUSD.equalWithTolerance( sensiExpectedUSD, EPS_FD * PAYMENT_PERIOD_FULL_GS_FX_USD.getNotional())); PointSensitivityBuilder pointSensiComputedGBP = pricer.forecastValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider); CurrencyParameterSensitivities sensiComputedGBP = provider.parameterSensitivity(pointSensiComputedGBP.build().normalized()); CurrencyParameterSensitivities sensiExpectedGBP = CAL_FD.sensitivity( provider, (p) -> CurrencyAmount.of(GBP, pricer.forecastValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, (p)))); assertTrue(sensiComputedGBP.equalWithTolerance( sensiExpectedGBP, EPS_FD * PAYMENT_PERIOD_FULL_GS_FX_GBP.getNotional())); } public void test_presentValueSensitivity_compoundNone_fx_dfCurve() { DiscountingRatePaymentPeriodPricer pricer = DiscountingRatePaymentPeriodPricer.DEFAULT; ImmutableRatesProvider provider = MULTI_GBP_USD_SIMPLE; PointSensitivityBuilder pointSensiComputedUSD = pricer.presentValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_USD, provider); CurrencyParameterSensitivities sensiComputedUSD = provider.parameterSensitivity(pointSensiComputedUSD.build().normalized()); CurrencyParameterSensitivities sensiExpectedUSD = CAL_FD.sensitivity( provider, (p) -> CurrencyAmount.of(USD, pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_USD, (p)))); assertTrue(sensiComputedUSD.equalWithTolerance( sensiExpectedUSD, EPS_FD * PAYMENT_PERIOD_FULL_GS_FX_USD.getNotional())); PointSensitivityBuilder pointSensiComputedGBP = pricer.presentValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider); CurrencyParameterSensitivities sensiComputedGBP = provider.parameterSensitivity(pointSensiComputedGBP.build().normalized()); CurrencyParameterSensitivities sensiExpectedGBP = CAL_FD.sensitivity( provider, (p) -> CurrencyAmount.of(GBP, pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, (p)))); assertTrue(sensiComputedGBP.equalWithTolerance( sensiExpectedGBP, EPS_FD * PAYMENT_PERIOD_FULL_GS_FX_GBP.getNotional())); } //------------------------------------------------------------------------- @SuppressWarnings("null") private List<IborRateSensitivity> futureFwdSensitivityFD(RatesProvider provider, RatePaymentPeriod payment, RateComputationFn<RateComputation> obsFunc, double eps) { LocalDate valuationDate = provider.getValuationDate(); RatesProvider provNew = mock(RatesProvider.class); when(provNew.getValuationDate()).thenReturn(valuationDate); ImmutableList<RateAccrualPeriod> periods = payment.getAccrualPeriods(); int nPeriods = periods.size(); List<IborRateSensitivity> forwardRateSensi = new ArrayList<>(); for (int j = 0; j < nPeriods; ++j) { RateComputationFn<RateComputation> obsFuncUp = mock(RateComputationFn.class); RateComputationFn<RateComputation> obsFuncDown = mock(RateComputationFn.class); IborIndex index = null; LocalDate fixingDate = null; for (int i = 0; i < nPeriods; ++i) { RateAccrualPeriod period = periods.get(i); IborRateComputation observation = (IborRateComputation) period.getRateComputation(); double rate = obsFunc.rate(observation, period.getStartDate(), period.getEndDate(), provider); if (i == j) { fixingDate = observation.getFixingDate(); index = observation.getIndex(); when(obsFuncUp.rate(observation, period.getStartDate(), period.getEndDate(), provNew)).thenReturn(rate + eps); when(obsFuncDown.rate(observation, period.getStartDate(), period.getEndDate(), provNew)) .thenReturn(rate - eps); } else { when(obsFuncUp.rate(observation, period.getStartDate(), period.getEndDate(), provNew)).thenReturn(rate); when(obsFuncDown.rate(observation, period.getStartDate(), period.getEndDate(), provNew)).thenReturn(rate); } } DiscountingRatePaymentPeriodPricer pricerUp = new DiscountingRatePaymentPeriodPricer(obsFuncUp); DiscountingRatePaymentPeriodPricer pricerDown = new DiscountingRatePaymentPeriodPricer(obsFuncDown); double up = pricerUp.forecastValue(payment, provNew); double down = pricerDown.forecastValue(payment, provNew); IborRateSensitivity fwdSense = IborRateSensitivity.of(IborIndexObservation.of(index, fixingDate, REF_DATA), 0.5 * (up - down) / eps); forwardRateSensi.add(fwdSense); } return forwardRateSensi; } private List<ZeroRateSensitivity> dscSensitivityFD(RatesProvider provider, RatePaymentPeriod payment, RateComputationFn<RateComputation> obsFunc, double eps) { LocalDate valuationDate = provider.getValuationDate(); LocalDate paymentDate = payment.getPaymentDate(); double discountFactor = provider.discountFactor(payment.getCurrency(), paymentDate); double paymentTime = DAY_COUNT.relativeYearFraction(valuationDate, paymentDate); Currency currency = payment.getCurrency(); RatesProvider provUp = mock(RatesProvider.class); RatesProvider provDw = mock(RatesProvider.class); RateComputationFn<RateComputation> obsFuncNewUp = mock(RateComputationFn.class); RateComputationFn<RateComputation> obsFuncNewDw = mock(RateComputationFn.class); when(provUp.getValuationDate()).thenReturn(valuationDate); when(provDw.getValuationDate()).thenReturn(valuationDate); when(provUp.discountFactor(currency, paymentDate)).thenReturn(discountFactor * Math.exp(-eps * paymentTime)); when(provDw.discountFactor(currency, paymentDate)).thenReturn(discountFactor * Math.exp(eps * paymentTime)); ImmutableList<RateAccrualPeriod> periods = payment.getAccrualPeriods(); for (int i = 0; i < periods.size(); ++i) { RateComputation observation = periods.get(i).getRateComputation(); LocalDate startDate = periods.get(i).getStartDate(); LocalDate endDate = periods.get(i).getEndDate(); double rate = obsFunc.rate(observation, startDate, endDate, provider); when(obsFuncNewUp.rate(observation, startDate, endDate, provUp)).thenReturn(rate); when(obsFuncNewDw.rate(observation, startDate, endDate, provDw)).thenReturn(rate); } DiscountingRatePaymentPeriodPricer pricerUp = new DiscountingRatePaymentPeriodPricer(obsFuncNewUp); DiscountingRatePaymentPeriodPricer pricerDw = new DiscountingRatePaymentPeriodPricer(obsFuncNewDw); double pvUp = pricerUp.presentValue(payment, provUp); double pvDw = pricerDw.presentValue(payment, provDw); double res = 0.5 * (pvUp - pvDw) / eps; List<ZeroRateSensitivity> zeroRateSensi = new ArrayList<>(); zeroRateSensi.add(ZeroRateSensitivity.of(currency, paymentTime, res)); return zeroRateSensi; } //------------------------------------------------------------------------- public void test_accruedInterest_firstAccrualPeriod() { LocalDate valDate = PAYMENT_PERIOD_FULL_GS.getStartDate().plusDays(7); SimpleRatesProvider prov = createProvider(valDate); double partial = PAYMENT_PERIOD_FULL_GS.getDayCount().yearFraction(ACCRUAL_PERIOD_1_GS.getStartDate(), valDate); double fraction = partial / ACCRUAL_FACTOR_1; double expected = ((RATE_1 * GEARING + SPREAD) * ACCRUAL_FACTOR_1 * NOTIONAL_100) * fraction; double computed = DiscountingRatePaymentPeriodPricer.DEFAULT.accruedInterest(PAYMENT_PERIOD_FULL_GS, prov); assertEquals(computed, expected, TOLERANCE_PV); } public void test_accruedInterest_lastAccrualPeriod() { LocalDate valDate = PAYMENT_PERIOD_FULL_GS.getEndDate().minusDays(7); SimpleRatesProvider prov = createProvider(valDate); double partial = PAYMENT_PERIOD_FULL_GS.getDayCount().yearFraction(ACCRUAL_PERIOD_3_GS.getStartDate(), valDate); double fraction = partial / ACCRUAL_FACTOR_3; double expected = ((RATE_1 * GEARING + SPREAD) * ACCRUAL_FACTOR_1 * NOTIONAL_100) + ((RATE_2 * GEARING + SPREAD) * ACCRUAL_FACTOR_2 * NOTIONAL_100) + ((RATE_3 * GEARING + SPREAD) * ACCRUAL_FACTOR_3 * NOTIONAL_100 * fraction); double computed = DiscountingRatePaymentPeriodPricer.DEFAULT.accruedInterest(PAYMENT_PERIOD_FULL_GS, prov); assertEquals(computed, expected, TOLERANCE_PV); } public void test_accruedInterest_valDateBeforePeriod() { SimpleRatesProvider prov = createProvider(PAYMENT_PERIOD_FULL_GS.getStartDate()); double computed = DiscountingRatePaymentPeriodPricer.DEFAULT.accruedInterest(PAYMENT_PERIOD_FULL_GS, prov); assertEquals(computed, 0, TOLERANCE_PV); } public void test_accruedInterest_valDateAfterPeriod() { SimpleRatesProvider prov = createProvider(PAYMENT_PERIOD_FULL_GS.getEndDate().plusDays(1)); double computed = DiscountingRatePaymentPeriodPricer.DEFAULT.accruedInterest(PAYMENT_PERIOD_FULL_GS, prov); assertEquals(computed, 0, TOLERANCE_PV); } //------------------------------------------------------------------------- public void test_explainPresentValue_single() { RatesProvider prov = createProvider(VAL_DATE); DiscountingRatePaymentPeriodPricer test = DiscountingRatePaymentPeriodPricer.DEFAULT; ExplainMapBuilder builder = ExplainMap.builder(); test.explainPresentValue(PAYMENT_PERIOD_1, prov, builder); ExplainMap explain = builder.build(); Currency currency = PAYMENT_PERIOD_1.getCurrency(); double ua = RATE_1 * ACCRUAL_FACTOR_1; double fv = ua * NOTIONAL_100; assertEquals(explain.get(ExplainKey.ENTRY_TYPE).get(), "RatePaymentPeriod"); assertEquals(explain.get(ExplainKey.PAYMENT_DATE).get(), PAYMENT_PERIOD_1.getPaymentDate()); assertEquals(explain.get(ExplainKey.PAYMENT_CURRENCY).get(), currency); assertEquals(explain.get(ExplainKey.NOTIONAL).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.NOTIONAL).get().getAmount(), NOTIONAL_100, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.TRADE_NOTIONAL).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.TRADE_NOTIONAL).get().getAmount(), NOTIONAL_100, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.COMPOUNDING).get(), PAYMENT_PERIOD_1.getCompoundingMethod()); assertEquals(explain.get(ExplainKey.DISCOUNT_FACTOR).get(), DISCOUNT_FACTOR, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.FORECAST_VALUE).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.FORECAST_VALUE).get().getAmount(), fv, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.PRESENT_VALUE).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.PRESENT_VALUE).get().getAmount(), fv * DISCOUNT_FACTOR, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.ACCRUAL_PERIODS).get().size(), 1); ExplainMap explainAccrual = explain.get(ExplainKey.ACCRUAL_PERIODS).get().get(0); RateAccrualPeriod ap = PAYMENT_PERIOD_1.getAccrualPeriods().get(0); int daysBetween = (int) DAYS.between(ap.getStartDate(), ap.getEndDate()); assertEquals(explainAccrual.get(ExplainKey.ENTRY_TYPE).get(), "AccrualPeriod"); assertEquals(explainAccrual.get(ExplainKey.START_DATE).get(), ap.getStartDate()); assertEquals(explainAccrual.get(ExplainKey.UNADJUSTED_START_DATE).get(), ap.getUnadjustedStartDate()); assertEquals(explainAccrual.get(ExplainKey.END_DATE).get(), ap.getEndDate()); assertEquals(explainAccrual.get(ExplainKey.UNADJUSTED_END_DATE).get(), ap.getUnadjustedEndDate()); assertEquals(explainAccrual.get(ExplainKey.ACCRUAL_YEAR_FRACTION).get(), ap.getYearFraction()); assertEquals(explainAccrual.get(ExplainKey.ACCRUAL_DAYS).get(), (Integer) daysBetween); assertEquals(explainAccrual.get(ExplainKey.GEARING).get(), ap.getGearing(), TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.SPREAD).get(), ap.getSpread(), TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.FIXED_RATE).get(), RATE_1, TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.PAY_OFF_RATE).get(), RATE_1, TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.UNIT_AMOUNT).get(), ua, TOLERANCE_PV); } public void test_explainPresentValue_single_paymentDateInPast() { SimpleRatesProvider prov = createProvider(VAL_DATE); prov.setValuationDate(VAL_DATE.plusYears(1)); DiscountingRatePaymentPeriodPricer test = DiscountingRatePaymentPeriodPricer.DEFAULT; ExplainMapBuilder builder = ExplainMap.builder(); test.explainPresentValue(PAYMENT_PERIOD_1, prov, builder); ExplainMap explain = builder.build(); Currency currency = PAYMENT_PERIOD_1.getCurrency(); assertEquals(explain.get(ExplainKey.ENTRY_TYPE).get(), "RatePaymentPeriod"); assertEquals(explain.get(ExplainKey.PAYMENT_DATE).get(), PAYMENT_PERIOD_1.getPaymentDate()); assertEquals(explain.get(ExplainKey.PAYMENT_CURRENCY).get(), currency); assertEquals(explain.get(ExplainKey.NOTIONAL).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.NOTIONAL).get().getAmount(), NOTIONAL_100, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.TRADE_NOTIONAL).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.TRADE_NOTIONAL).get().getAmount(), NOTIONAL_100, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.COMPLETED).get(), Boolean.TRUE); assertEquals(explain.get(ExplainKey.FORECAST_VALUE).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.FORECAST_VALUE).get().getAmount(), 0d, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.PRESENT_VALUE).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.PRESENT_VALUE).get().getAmount(), 0d, TOLERANCE_PV); } public void test_explainPresentValue_single_fx() { RatesProvider prov = createProvider(VAL_DATE); DiscountingRatePaymentPeriodPricer test = DiscountingRatePaymentPeriodPricer.DEFAULT; ExplainMapBuilder builder = ExplainMap.builder(); test.explainPresentValue(PAYMENT_PERIOD_1_FX, prov, builder); ExplainMap explain = builder.build(); FxReset fxReset = PAYMENT_PERIOD_1_FX.getFxReset().get(); Currency currency = PAYMENT_PERIOD_1_FX.getCurrency(); Currency referenceCurrency = fxReset.getReferenceCurrency(); double ua = RATE_1 * ACCRUAL_FACTOR_1; double fv = ua * NOTIONAL_100 * RATE_FX; assertEquals(explain.get(ExplainKey.ENTRY_TYPE).get(), "RatePaymentPeriod"); assertEquals(explain.get(ExplainKey.PAYMENT_DATE).get(), PAYMENT_PERIOD_1_FX.getPaymentDate()); assertEquals(explain.get(ExplainKey.PAYMENT_CURRENCY).get(), currency); assertEquals(explain.get(ExplainKey.NOTIONAL).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.NOTIONAL).get().getAmount(), NOTIONAL_100 * RATE_FX, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.TRADE_NOTIONAL).get().getCurrency(), referenceCurrency); assertEquals(explain.get(ExplainKey.TRADE_NOTIONAL).get().getAmount(), NOTIONAL_100, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.COMPOUNDING).get(), PAYMENT_PERIOD_1_FX.getCompoundingMethod()); assertEquals(explain.get(ExplainKey.DISCOUNT_FACTOR).get(), DISCOUNT_FACTOR, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.FORECAST_VALUE).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.FORECAST_VALUE).get().getAmount(), fv, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.PRESENT_VALUE).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.PRESENT_VALUE).get().getAmount(), fv * DISCOUNT_FACTOR, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.OBSERVATIONS).get().size(), 1); ExplainMap explainFxObs = explain.get(ExplainKey.OBSERVATIONS).get().get(0); assertEquals(explainFxObs.get(ExplainKey.ENTRY_TYPE).get(), "FxObservation"); assertEquals(explainFxObs.get(ExplainKey.INDEX).get(), fxReset.getIndex()); assertEquals(explainFxObs.get(ExplainKey.FIXING_DATE).get(), fxReset.getObservation().getFixingDate()); assertEquals(explainFxObs.get(ExplainKey.INDEX_VALUE).get(), RATE_FX, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.ACCRUAL_PERIODS).get().size(), 1); ExplainMap explainAccrual = explain.get(ExplainKey.ACCRUAL_PERIODS).get().get(0); RateAccrualPeriod ap = PAYMENT_PERIOD_1_FX.getAccrualPeriods().get(0); int daysBetween = (int) DAYS.between(ap.getStartDate(), ap.getEndDate()); assertEquals(explainAccrual.get(ExplainKey.ENTRY_TYPE).get(), "AccrualPeriod"); assertEquals(explainAccrual.get(ExplainKey.START_DATE).get(), ap.getStartDate()); assertEquals(explainAccrual.get(ExplainKey.UNADJUSTED_START_DATE).get(), ap.getUnadjustedStartDate()); assertEquals(explainAccrual.get(ExplainKey.END_DATE).get(), ap.getEndDate()); assertEquals(explainAccrual.get(ExplainKey.UNADJUSTED_END_DATE).get(), ap.getUnadjustedEndDate()); assertEquals(explainAccrual.get(ExplainKey.ACCRUAL_YEAR_FRACTION).get(), ap.getYearFraction()); assertEquals(explainAccrual.get(ExplainKey.ACCRUAL_DAYS).get(), (Integer) daysBetween); assertEquals(explainAccrual.get(ExplainKey.GEARING).get(), ap.getGearing(), TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.SPREAD).get(), ap.getSpread(), TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.FIXED_RATE).get(), RATE_1, TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.PAY_OFF_RATE).get(), RATE_1, TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.UNIT_AMOUNT).get(), ua, TOLERANCE_PV); } public void test_explainPresentValue_single_gearingSpread() { RatesProvider prov = createProvider(VAL_DATE); DiscountingRatePaymentPeriodPricer test = DiscountingRatePaymentPeriodPricer.DEFAULT; ExplainMapBuilder builder = ExplainMap.builder(); test.explainPresentValue(PAYMENT_PERIOD_1_GS, prov, builder); ExplainMap explain = builder.build(); Currency currency = PAYMENT_PERIOD_1_GS.getCurrency(); double payOffRate = RATE_1 * GEARING + SPREAD; double ua = payOffRate * ACCRUAL_FACTOR_1; double fv = ua * NOTIONAL_100; assertEquals(explain.get(ExplainKey.ENTRY_TYPE).get(), "RatePaymentPeriod"); assertEquals(explain.get(ExplainKey.PAYMENT_DATE).get(), PAYMENT_PERIOD_1_GS.getPaymentDate()); assertEquals(explain.get(ExplainKey.PAYMENT_CURRENCY).get(), currency); assertEquals(explain.get(ExplainKey.NOTIONAL).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.NOTIONAL).get().getAmount(), NOTIONAL_100, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.TRADE_NOTIONAL).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.TRADE_NOTIONAL).get().getAmount(), NOTIONAL_100, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.COMPOUNDING).get(), PAYMENT_PERIOD_1_GS.getCompoundingMethod()); assertEquals(explain.get(ExplainKey.DISCOUNT_FACTOR).get(), DISCOUNT_FACTOR, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.FORECAST_VALUE).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.FORECAST_VALUE).get().getAmount(), fv, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.PRESENT_VALUE).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.PRESENT_VALUE).get().getAmount(), fv * DISCOUNT_FACTOR, TOLERANCE_PV); assertEquals(explain.get(ExplainKey.ACCRUAL_PERIODS).get().size(), 1); ExplainMap explainAccrual = explain.get(ExplainKey.ACCRUAL_PERIODS).get().get(0); RateAccrualPeriod ap = PAYMENT_PERIOD_1_GS.getAccrualPeriods().get(0); int daysBetween = (int) DAYS.between(ap.getStartDate(), ap.getEndDate()); assertEquals(explainAccrual.get(ExplainKey.ENTRY_TYPE).get(), "AccrualPeriod"); assertEquals(explainAccrual.get(ExplainKey.START_DATE).get(), ap.getStartDate()); assertEquals(explainAccrual.get(ExplainKey.UNADJUSTED_START_DATE).get(), ap.getUnadjustedStartDate()); assertEquals(explainAccrual.get(ExplainKey.END_DATE).get(), ap.getEndDate()); assertEquals(explainAccrual.get(ExplainKey.UNADJUSTED_END_DATE).get(), ap.getUnadjustedEndDate()); assertEquals(explainAccrual.get(ExplainKey.ACCRUAL_YEAR_FRACTION).get(), ap.getYearFraction()); assertEquals(explainAccrual.get(ExplainKey.ACCRUAL_DAYS).get(), (Integer) daysBetween); assertEquals(explainAccrual.get(ExplainKey.GEARING).get(), ap.getGearing(), TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.SPREAD).get(), ap.getSpread(), TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.FIXED_RATE).get(), RATE_1, TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.PAY_OFF_RATE).get(), payOffRate, TOLERANCE_PV); assertEquals(explainAccrual.get(ExplainKey.UNIT_AMOUNT).get(), ua, TOLERANCE_PV); } //------------------------------------------------------------------------- public void test_currencyExposure_fx() { DiscountingRatePaymentPeriodPricer pricer = DiscountingRatePaymentPeriodPricer.DEFAULT; LocalDate valuationDate = VAL_DATE.minusWeeks(1); ImmutableRatesProvider provider = RatesProviderDataSets.multiGbpUsd(valuationDate); // USD MultiCurrencyAmount computedUSD = pricer.currencyExposure(PAYMENT_PERIOD_FULL_GS_FX_USD, provider); PointSensitivities pointUSD = pricer.presentValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_USD, provider).build(); MultiCurrencyAmount expectedUSD = provider.currencyExposure(pointUSD.convertedTo(GBP, provider)).plus(CurrencyAmount.of( PAYMENT_PERIOD_FULL_GS_FX_USD.getCurrency(), pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_USD, provider))); assertEquals(computedUSD.getAmount(GBP).getAmount(), expectedUSD.getAmount(GBP).getAmount(), TOLERANCE_PV); assertFalse(computedUSD.contains(USD)); // 0 USD // GBP MultiCurrencyAmount computedGBP = pricer.currencyExposure(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider); PointSensitivities pointGBP = pricer.presentValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider).build(); MultiCurrencyAmount expectedGBP = provider.currencyExposure(pointGBP.convertedTo(USD, provider)).plus(CurrencyAmount.of( PAYMENT_PERIOD_FULL_GS_FX_GBP.getCurrency(), pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider))); assertEquals(computedGBP.getAmount(USD).getAmount(), expectedGBP.getAmount(USD).getAmount(), TOLERANCE_PV); assertFalse(computedGBP.contains(GBP)); // 0 GBP // FD approximation ImmutableRatesProvider provUp = RatesProviderDataSets.multiGbpUsd(valuationDate).toBuilder() .fxRateProvider(FX_MATRIX_BUMP) .build(); double expectedFdUSD = (pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_USD, provUp) - pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_USD, provider)) / EPS_FD; assertEquals(computedUSD.getAmount(GBP).getAmount(), expectedFdUSD, EPS_FD * NOTIONAL_100); double expectedFdGBP = -(pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, provUp) - pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider)) * FX_RATE * FX_RATE / EPS_FD; assertEquals(computedGBP.getAmount(USD).getAmount(), expectedFdGBP, EPS_FD * NOTIONAL_100); } public void test_currencyExposure_fx_betweenFixingAndPayment() { DiscountingRatePaymentPeriodPricer pricer = DiscountingRatePaymentPeriodPricer.DEFAULT; LocalDate valuationDate = VAL_DATE.plusWeeks(1); LocalDateDoubleTimeSeries ts = LocalDateDoubleTimeSeries.of(LocalDate.of(2014, 1, 22), 1.55); ImmutableRatesProvider provider = RatesProviderDataSets.multiGbpUsd(valuationDate).toBuilder() .timeSeries(FxIndices.GBP_USD_WM, ts) .build(); // USD MultiCurrencyAmount computedUSD = pricer.currencyExposure(PAYMENT_PERIOD_FULL_GS_FX_USD, provider); PointSensitivities pointUSD = pricer.presentValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_USD, provider).build(); MultiCurrencyAmount expectedUSD = provider.currencyExposure(pointUSD.convertedTo(GBP, provider)).plus( CurrencyAmount.of( PAYMENT_PERIOD_FULL_GS_FX_USD.getCurrency(), pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_USD, provider))); assertEquals(computedUSD.getAmount(USD).getAmount(), expectedUSD.getAmount(USD).getAmount(), TOLERANCE_PV); assertFalse(computedUSD.contains(GBP)); // 0 GBP // GBP MultiCurrencyAmount computedGBP = pricer.currencyExposure(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider); PointSensitivities pointGBP = pricer.presentValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider).build(); MultiCurrencyAmount expectedGBP = provider.currencyExposure(pointGBP.convertedTo(USD, provider)).plus( CurrencyAmount.of( PAYMENT_PERIOD_FULL_GS_FX_GBP.getCurrency(), pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider))); assertEquals(computedGBP.getAmount(GBP).getAmount(), expectedGBP.getAmount(GBP).getAmount(), TOLERANCE_PV); assertFalse(computedGBP.contains(USD)); // 0 USD // FD approximation ImmutableRatesProvider provUp = RatesProviderDataSets.multiGbpUsd(valuationDate).toBuilder() .fxRateProvider(FX_MATRIX_BUMP) .timeSeries(FxIndices.GBP_USD_WM, ts) .build(); double expectedFdUSD = (pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_USD, provUp) - pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_USD, provider)) / EPS_FD; assertTrue(!computedUSD.contains(GBP) && DoubleMath.fuzzyEquals(expectedFdUSD, 0d, TOLERANCE_PV)); double expectedFdGBP = -(pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, provUp) - pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider)) * FX_RATE * FX_RATE / EPS_FD; assertTrue(!computedGBP.contains(USD) && DoubleMath.fuzzyEquals(expectedFdGBP, 0d, TOLERANCE_PV)); } public void test_currencyExposure_fx_onFixing_noTimeSeries() { DiscountingRatePaymentPeriodPricer pricer = DiscountingRatePaymentPeriodPricer.DEFAULT; ImmutableRatesProvider provider = MULTI_GBP_USD; // USD MultiCurrencyAmount computedUSD = pricer.currencyExposure(PAYMENT_PERIOD_FULL_GS_FX_USD, provider); PointSensitivities pointUSD = pricer.presentValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_USD, provider).build(); MultiCurrencyAmount expectedUSD = provider.currencyExposure(pointUSD.convertedTo(GBP, provider)).plus( CurrencyAmount.of( PAYMENT_PERIOD_FULL_GS_FX_USD.getCurrency(), pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_USD, provider))); assertEquals(computedUSD.getAmount(GBP).getAmount(), expectedUSD.getAmount(GBP).getAmount(), TOLERANCE_PV); assertFalse(computedUSD.contains(USD)); // 0 GBP // GBP MultiCurrencyAmount computedGBP = pricer.currencyExposure(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider); PointSensitivities pointGBP = pricer.presentValueSensitivity(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider).build(); MultiCurrencyAmount expectedGBP = provider.currencyExposure(pointGBP.convertedTo(USD, provider)).plus(CurrencyAmount.of( PAYMENT_PERIOD_FULL_GS_FX_GBP.getCurrency(), pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider))); assertEquals(computedGBP.getAmount(USD).getAmount(), expectedGBP.getAmount(USD).getAmount(), TOLERANCE_PV); assertFalse(computedGBP.contains(GBP)); // 0 GBP // FD approximation ImmutableRatesProvider provUp = RatesProviderDataSets.multiGbpUsd(VAL_DATE).toBuilder() .fxRateProvider(FX_MATRIX_BUMP) .build(); double expectedFdUSD = (pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_USD, provUp) - pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_USD, provider)) / EPS_FD; assertEquals(computedUSD.getAmount(GBP).getAmount(), expectedFdUSD, EPS_FD * NOTIONAL_100); double expectedFdGBP = -(pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, provUp) - pricer.presentValue(PAYMENT_PERIOD_FULL_GS_FX_GBP, provider)) * FX_RATE * FX_RATE / EPS_FD; assertEquals(computedGBP.getAmount(USD).getAmount(), expectedFdGBP, EPS_FD * NOTIONAL_100); } public void test_currencyExposure_single() { DiscountingRatePaymentPeriodPricer pricer = DiscountingRatePaymentPeriodPricer.DEFAULT; ImmutableRatesProvider provider = MULTI_GBP_USD; MultiCurrencyAmount computed = pricer.currencyExposure(PAYMENT_PERIOD_COMPOUNDING_STRAIGHT, provider); PointSensitivities point = pricer.presentValueSensitivity(PAYMENT_PERIOD_COMPOUNDING_STRAIGHT, provider).build(); MultiCurrencyAmount expected = provider.currencyExposure(point) .plus(CurrencyAmount.of(PAYMENT_PERIOD_COMPOUNDING_STRAIGHT.getCurrency(), pricer.presentValue(PAYMENT_PERIOD_COMPOUNDING_STRAIGHT, provider))); assertEquals(computed.size(), expected.size()); assertEquals(computed.getAmount(USD).getAmount(), expected.getAmount(USD).getAmount(), TOLERANCE_PV); } public void test_currentCash_zero() { DiscountingRatePaymentPeriodPricer pricer = DiscountingRatePaymentPeriodPricer.DEFAULT; ImmutableRatesProvider provider = MULTI_GBP_USD; double computed = pricer.currentCash(PAYMENT_PERIOD_COMPOUNDING_FLAT, provider); assertEquals(computed, 0d); } public void test_currentCash_onPayment() { DiscountingRatePaymentPeriodPricer pricer = DiscountingRatePaymentPeriodPricer.DEFAULT; RatesProvider provider = createProvider(PAYMENT_PERIOD_1.getPaymentDate()); double computed = pricer.currentCash(PAYMENT_PERIOD_1, provider); assertEquals(computed, RATE_1 * ACCRUAL_FACTOR_1 * NOTIONAL_100); } //------------------------------------------------------------------------- // creates a simple provider private SimpleRatesProvider createProvider(LocalDate valDate) { DiscountFactors mockDf = mock(DiscountFactors.class); when(mockDf.discountFactor(PAYMENT_DATE_1)).thenReturn(DISCOUNT_FACTOR); FxIndexRates mockFxRates = mock(FxIndexRates.class); when(mockFxRates.rate(FxIndexObservation.of(GBP_USD_WM, FX_DATE_1, REF_DATA), GBP)).thenReturn(RATE_FX); SimpleRatesProvider prov = new SimpleRatesProvider(valDate); prov.setDayCount(DAY_COUNT); prov.setDiscountFactors(mockDf); prov.setFxIndexRates(mockFxRates); return prov; } }