/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.fra; import static com.opengamma.strata.basics.currency.Currency.GBP; import static com.opengamma.strata.basics.index.IborIndices.GBP_LIBOR_3M; import static com.opengamma.strata.pricer.fra.FraDummyData.FRA; import static com.opengamma.strata.pricer.fra.FraDummyData.FRA_AFMA; import static com.opengamma.strata.pricer.fra.FraDummyData.FRA_NONE; import static com.opengamma.strata.pricer.fra.FraDummyData.FRA_TRADE; 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.assertTrue; import java.time.LocalDate; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; 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.date.DayCount; import com.opengamma.strata.basics.date.DayCounts; import com.opengamma.strata.basics.index.IborIndexObservation; import com.opengamma.strata.collect.array.DoubleArray; import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeries; import com.opengamma.strata.market.amount.CashFlows; import com.opengamma.strata.market.curve.ConstantCurve; import com.opengamma.strata.market.curve.Curves; import com.opengamma.strata.market.curve.InterpolatedNodalCurve; import com.opengamma.strata.market.curve.interpolator.CurveInterpolator; import com.opengamma.strata.market.curve.interpolator.CurveInterpolators; import com.opengamma.strata.market.explain.ExplainKey; import com.opengamma.strata.market.explain.ExplainMap; import com.opengamma.strata.market.param.CurrencyParameterSensitivities; import com.opengamma.strata.market.sensitivity.PointSensitivities; import com.opengamma.strata.market.sensitivity.PointSensitivity; import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder; import com.opengamma.strata.pricer.DiscountFactors; import com.opengamma.strata.pricer.SimpleDiscountFactors; import com.opengamma.strata.pricer.ZeroRateSensitivity; import com.opengamma.strata.pricer.datasets.RatesProviderDataSets; import com.opengamma.strata.pricer.rate.IborIndexRates; 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.SimpleIborIndexRates; import com.opengamma.strata.pricer.rate.SimpleRatesProvider; import com.opengamma.strata.pricer.sensitivity.RatesFiniteDifferenceSensitivityCalculator; import com.opengamma.strata.product.fra.Fra; import com.opengamma.strata.product.fra.ResolvedFra; import com.opengamma.strata.product.fra.ResolvedFraTrade; import com.opengamma.strata.product.rate.IborRateComputation; import com.opengamma.strata.product.rate.RateComputation; /** * Tests {@link DiscountingFraProductPricer}. */ @Test public class DiscountingFraProductPricerTest { private static final ReferenceData REF_DATA = ReferenceData.standard(); private static final LocalDate VAL_DATE = LocalDate.of(2014, 1, 22); private static final DayCount DAY_COUNT = DayCounts.ACT_ACT_ISDA; private static final double TOLERANCE = 1E-12; private static final double DISCOUNT_FACTOR = 0.98d; private static final double FORWARD_RATE = 0.02; private static final ResolvedFraTrade RFRA_TRADE = FRA_TRADE.resolve(REF_DATA); private static final ResolvedFra RFRA = FRA.resolve(REF_DATA); private static final ResolvedFra RFRA_NONE = FRA_NONE.resolve(REF_DATA); private static final ResolvedFra RFRA_AFMA = FRA_AFMA.resolve(REF_DATA); /** * Test forecast value for ISDA FRA Discounting method. */ public void test_forecastValue_ISDA() { SimpleRatesProvider prov = createProvider(RFRA); double fixedRate = FRA.getFixedRate(); double yearFraction = RFRA.getYearFraction(); double notional = RFRA.getNotional(); double expected = notional * yearFraction * (FORWARD_RATE - fixedRate) / (1.0 + yearFraction * FORWARD_RATE); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; CurrencyAmount computed = test.forecastValue(RFRA, prov); assertEquals(computed.getAmount(), expected, TOLERANCE); // test via FraTrade DiscountingFraTradePricer testTrade = new DiscountingFraTradePricer(test); assertEquals(testTrade.forecastValue(RFRA_TRADE, prov), test.forecastValue(RFRA, prov)); } /** * Test forecast value for NONE FRA Discounting method. */ public void test_forecastValue_NONE() { SimpleRatesProvider prov = createProvider(RFRA_NONE); double fixedRate = FRA_NONE.getFixedRate(); double yearFraction = RFRA_NONE.getYearFraction(); double notional = RFRA_NONE.getNotional(); double expected = notional * yearFraction * (FORWARD_RATE - fixedRate); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; CurrencyAmount computed = test.forecastValue(RFRA_NONE, prov); assertEquals(computed.getAmount(), expected, TOLERANCE); } /** * Test forecast value for AFMA FRA Discounting method. */ public void test_forecastValue_AFMA() { SimpleRatesProvider prov = createProvider(RFRA_AFMA); double fixedRate = FRA_AFMA.getFixedRate(); double yearFraction = RFRA_AFMA.getYearFraction(); double notional = RFRA_AFMA.getNotional(); double expected = -notional * (1.0 / (1.0 + yearFraction * FORWARD_RATE) - 1.0 / (1.0 + yearFraction * fixedRate)); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; CurrencyAmount computed = test.forecastValue(RFRA_AFMA, prov); assertEquals(computed.getAmount(), expected, TOLERANCE); } /** * Test FRA paying in the past. */ public void test_forecastValue_inPast() { SimpleRatesProvider prov = createProvider(RFRA.toBuilder().paymentDate(VAL_DATE.minusDays(1)).build()); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; CurrencyAmount computed = test.forecastValue(RFRA.toBuilder().paymentDate(VAL_DATE.minusDays(1)).build(), prov); assertEquals(computed.getAmount(), 0d, TOLERANCE); } //------------------------------------------------------------------------- /** * Test present value for ISDA FRA Discounting method. */ public void test_presentValue_ISDA() { SimpleRatesProvider prov = createProvider(RFRA); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; CurrencyAmount pvComputed = test.presentValue(RFRA, prov); CurrencyAmount pvExpected = test.forecastValue(RFRA, prov).multipliedBy(DISCOUNT_FACTOR); assertEquals(pvComputed.getAmount(), pvExpected.getAmount(), TOLERANCE); // test via FraTrade DiscountingFraTradePricer testTrade = new DiscountingFraTradePricer(test); assertEquals(testTrade.presentValue(RFRA_TRADE, prov), test.presentValue(RFRA, prov)); } /** * Test present value for NONE FRA Discounting method. */ public void test_presentValue_NONE() { SimpleRatesProvider prov = createProvider(RFRA_NONE); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; CurrencyAmount pvComputed = test.presentValue(RFRA_NONE, prov); CurrencyAmount pvExpected = test.forecastValue(RFRA_NONE, prov).multipliedBy(DISCOUNT_FACTOR); assertEquals(pvComputed.getAmount(), pvExpected.getAmount(), TOLERANCE); } /** * Test present value for ISDA FRA Discounting method. */ public void test_presentValue_AFMA() { SimpleRatesProvider prov = createProvider(RFRA_AFMA); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; CurrencyAmount pvComputed = test.presentValue(RFRA_AFMA, prov); CurrencyAmount pvExpected = test.forecastValue(RFRA_AFMA, prov).multipliedBy(DISCOUNT_FACTOR); assertEquals(pvComputed.getAmount(), pvExpected.getAmount(), TOLERANCE); } /** * Test FRA paying in the past. */ public void test_presentValue_inPast() { ResolvedFra fra = RFRA.toBuilder().paymentDate(VAL_DATE.minusDays(1)).build(); SimpleRatesProvider prov = createProvider(fra); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; CurrencyAmount computed = test.presentValue(fra, prov); assertEquals(computed.getAmount(), 0d, TOLERANCE); } //------------------------------------------------------------------------- /** * Test forecast value sensitivity for ISDA FRA discounting method. */ public void test_forecastValueSensitivity_ISDA() { SimpleRatesProvider prov = createProvider(RFRA); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; PointSensitivities sensitivity = test.forecastValueSensitivity(RFRA, prov); double eps = 1.e-7; double fdSense = forecastValueFwdSensitivity(RFRA, FORWARD_RATE, eps); ImmutableList<PointSensitivity> sensitivities = sensitivity.getSensitivities(); assertEquals(sensitivities.size(), 1); IborRateSensitivity sensitivity0 = (IborRateSensitivity) sensitivities.get(0); assertEquals(sensitivity0.getIndex(), FRA.getIndex()); assertEquals(sensitivity0.getObservation().getFixingDate(), FRA.getStartDate()); assertEquals(sensitivity0.getSensitivity(), fdSense, FRA.getNotional() * eps); // test via FraTrade DiscountingFraTradePricer testTrade = new DiscountingFraTradePricer(test); assertEquals(testTrade.forecastValueSensitivity(RFRA_TRADE, prov), test.forecastValueSensitivity(RFRA, prov)); } /** * Test forecast value sensitivity for NONE FRA discounting method. */ public void test_forecastValueSensitivity_NONE() { SimpleRatesProvider prov = createProvider(RFRA_NONE); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; PointSensitivities sensitivity = test.forecastValueSensitivity(RFRA_NONE, prov); double eps = 1.e-7; double fdSense = forecastValueFwdSensitivity(RFRA_NONE, FORWARD_RATE, eps); ImmutableList<PointSensitivity> sensitivities = sensitivity.getSensitivities(); assertEquals(sensitivities.size(), 1); IborRateSensitivity sensitivity0 = (IborRateSensitivity) sensitivities.get(0); assertEquals(sensitivity0.getIndex(), FRA_NONE.getIndex()); assertEquals(sensitivity0.getObservation().getFixingDate(), FRA_NONE.getStartDate()); assertEquals(sensitivity0.getSensitivity(), fdSense, FRA_NONE.getNotional() * eps); } /** * Test forecast value sensitivity for AFMA FRA discounting method. */ public void test_forecastValueSensitivity_AFMA() { SimpleRatesProvider prov = createProvider(RFRA_AFMA); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; PointSensitivities sensitivity = test.forecastValueSensitivity(RFRA_AFMA, prov); double eps = 1.e-7; double fdSense = forecastValueFwdSensitivity(RFRA_AFMA, FORWARD_RATE, eps); ImmutableList<PointSensitivity> sensitivities = sensitivity.getSensitivities(); assertEquals(sensitivities.size(), 1); IborRateSensitivity sensitivity0 = (IborRateSensitivity) sensitivities.get(0); assertEquals(sensitivity0.getIndex(), FRA_AFMA.getIndex()); assertEquals(sensitivity0.getObservation().getFixingDate(), FRA_AFMA.getStartDate()); assertEquals(sensitivity0.getSensitivity(), fdSense, FRA_AFMA.getNotional() * eps); } //------------------------------------------------------------------------- /** * Test present value sensitivity for ISDA */ public void test_presentValueSensitivity_ISDA() { RateComputationFn<RateComputation> mockObs = mock(RateComputationFn.class); DiscountFactors mockDf = mock(DiscountFactors.class); SimpleRatesProvider simpleProv = new SimpleRatesProvider(VAL_DATE, mockDf); ResolvedFra fraExp = RFRA; double forwardRate = 0.05; double discountRate = 0.015; double paymentTime = 0.3; double discountFactor = Math.exp(-discountRate * paymentTime); LocalDate fixingDate = FRA.getStartDate(); IborIndexObservation obs = IborIndexObservation.of(FRA.getIndex(), fixingDate, REF_DATA); PointSensitivityBuilder sens = IborRateSensitivity.of(obs, 1d); when(mockDf.discountFactor(fraExp.getPaymentDate())) .thenReturn(discountFactor); when(mockDf.zeroRatePointSensitivity(fraExp.getPaymentDate())) .thenReturn(ZeroRateSensitivity.of(fraExp.getCurrency(), paymentTime, -discountFactor * paymentTime)); when(mockObs.rateSensitivity(fraExp.getFloatingRate(), fraExp.getStartDate(), fraExp.getEndDate(), simpleProv)) .thenReturn(sens); when(mockObs.rate(fraExp.getFloatingRate(), FRA.getStartDate(), FRA.getEndDate(), simpleProv)) .thenReturn(forwardRate); DiscountingFraProductPricer test = new DiscountingFraProductPricer(mockObs); PointSensitivities sensitivity = test.presentValueSensitivity(fraExp, simpleProv); double eps = 1.e-7; double fdDscSense = dscSensitivity(RFRA, forwardRate, discountFactor, paymentTime, eps); double fdSense = presentValueFwdSensitivity(RFRA, forwardRate, discountFactor, eps); ImmutableList<PointSensitivity> sensitivities = sensitivity.getSensitivities(); assertEquals(sensitivities.size(), 2); IborRateSensitivity sensitivity0 = (IborRateSensitivity) sensitivities.get(0); assertEquals(sensitivity0.getIndex(), FRA.getIndex()); assertEquals(sensitivity0.getObservation().getFixingDate(), fixingDate); assertEquals(sensitivity0.getSensitivity(), fdSense, FRA.getNotional() * eps); ZeroRateSensitivity sensitivity1 = (ZeroRateSensitivity) sensitivities.get(1); assertEquals(sensitivity1.getCurrency(), FRA.getCurrency()); assertEquals(sensitivity1.getYearFraction(), paymentTime); assertEquals(sensitivity1.getSensitivity(), fdDscSense, FRA.getNotional() * eps); // test via FraTrade DiscountingFraTradePricer testTrade = new DiscountingFraTradePricer(test); assertEquals(testTrade.presentValueSensitivity(RFRA_TRADE, simpleProv), test.presentValueSensitivity(fraExp, simpleProv)); } /** * Test present value sensitivity for NONE FRA discounting method. */ public void test_presentValueSensitivity_NONE() { RateComputationFn<RateComputation> mockObs = mock(RateComputationFn.class); DiscountFactors mockDf = mock(DiscountFactors.class); SimpleRatesProvider simpleProv = new SimpleRatesProvider(VAL_DATE, mockDf); ResolvedFra fraExp = RFRA_NONE; double forwardRate = 0.025; double discountRate = 0.01; double paymentTime = 0.3; double discountFactor = Math.exp(-discountRate * paymentTime); LocalDate fixingDate = FRA_NONE.getStartDate(); IborIndexObservation obs = IborIndexObservation.of(FRA.getIndex(), fixingDate, REF_DATA); PointSensitivityBuilder sens = IborRateSensitivity.of(obs, 1d); when(mockDf.discountFactor(fraExp.getPaymentDate())) .thenReturn(discountFactor); when(mockDf.zeroRatePointSensitivity(fraExp.getPaymentDate())) .thenReturn(ZeroRateSensitivity.of(fraExp.getCurrency(), paymentTime, -discountFactor * paymentTime)); when(mockObs.rateSensitivity(fraExp.getFloatingRate(), fraExp.getStartDate(), fraExp.getEndDate(), simpleProv)) .thenReturn(sens); when(mockObs.rate(fraExp.getFloatingRate(), FRA_NONE.getStartDate(), FRA_NONE.getEndDate(), simpleProv)) .thenReturn(forwardRate); DiscountingFraProductPricer test = new DiscountingFraProductPricer(mockObs); PointSensitivities sensitivity = test.presentValueSensitivity(fraExp, simpleProv); double eps = 1.e-7; double fdDscSense = dscSensitivity(RFRA_NONE, forwardRate, discountFactor, paymentTime, eps); double fdSense = presentValueFwdSensitivity(RFRA_NONE, forwardRate, discountFactor, eps); ImmutableList<PointSensitivity> sensitivities = sensitivity.getSensitivities(); assertEquals(sensitivities.size(), 2); IborRateSensitivity sensitivity0 = (IborRateSensitivity) sensitivities.get(0); assertEquals(sensitivity0.getIndex(), FRA_NONE.getIndex()); assertEquals(sensitivity0.getObservation().getFixingDate(), fixingDate); assertEquals(sensitivity0.getSensitivity(), fdSense, FRA_NONE.getNotional() * eps); ZeroRateSensitivity sensitivity1 = (ZeroRateSensitivity) sensitivities.get(1); assertEquals(sensitivity1.getCurrency(), FRA_NONE.getCurrency()); assertEquals(sensitivity1.getYearFraction(), paymentTime); assertEquals(sensitivity1.getSensitivity(), fdDscSense, FRA_NONE.getNotional() * eps); } /** * Test present value sensitivity for AFMA FRA discounting method. */ public void test_presentValueSensitivity_AFMA() { RateComputationFn<RateComputation> mockObs = mock(RateComputationFn.class); DiscountFactors mockDf = mock(DiscountFactors.class); SimpleRatesProvider simpleProv = new SimpleRatesProvider(VAL_DATE, mockDf); ResolvedFra fraExp = RFRA_AFMA; double forwardRate = 0.05; double discountRate = 0.025; double paymentTime = 0.3; double discountFactor = Math.exp(-discountRate * paymentTime); LocalDate fixingDate = FRA_AFMA.getStartDate(); IborIndexObservation obs = IborIndexObservation.of(FRA.getIndex(), fixingDate, REF_DATA); PointSensitivityBuilder sens = IborRateSensitivity.of(obs, 1d); when(mockDf.discountFactor(fraExp.getPaymentDate())) .thenReturn(discountFactor); when(mockDf.zeroRatePointSensitivity(fraExp.getPaymentDate())) .thenReturn(ZeroRateSensitivity.of(fraExp.getCurrency(), paymentTime, -discountFactor * paymentTime)); when(mockObs.rateSensitivity(fraExp.getFloatingRate(), fraExp.getStartDate(), fraExp.getEndDate(), simpleProv)) .thenReturn(sens); when(mockObs.rate(fraExp.getFloatingRate(), FRA_AFMA.getStartDate(), FRA_AFMA.getEndDate(), simpleProv)) .thenReturn(forwardRate); DiscountingFraProductPricer test = new DiscountingFraProductPricer(mockObs); PointSensitivities sensitivity = test.presentValueSensitivity(fraExp, simpleProv); double eps = 1.e-7; double fdDscSense = dscSensitivity(RFRA_AFMA, forwardRate, discountFactor, paymentTime, eps); double fdSense = presentValueFwdSensitivity(RFRA_AFMA, forwardRate, discountFactor, eps); ImmutableList<PointSensitivity> sensitivities = sensitivity.getSensitivities(); assertEquals(sensitivities.size(), 2); IborRateSensitivity sensitivity0 = (IborRateSensitivity) sensitivities.get(0); assertEquals(sensitivity0.getIndex(), FRA_AFMA.getIndex()); assertEquals(sensitivity0.getObservation().getFixingDate(), fixingDate); assertEquals(sensitivity0.getSensitivity(), fdSense, FRA_AFMA.getNotional() * eps); ZeroRateSensitivity sensitivity1 = (ZeroRateSensitivity) sensitivities.get(1); assertEquals(sensitivity1.getCurrency(), FRA_AFMA.getCurrency()); assertEquals(sensitivity1.getYearFraction(), paymentTime); assertEquals(sensitivity1.getSensitivity(), fdDscSense, FRA_AFMA.getNotional() * eps); } //------------------------------------------------------------------------- /** * Test par rate for ISDA FRA Discounting method. */ public void test_parRate_ISDA() { ResolvedFra fraExp = RFRA; SimpleRatesProvider prov = createProvider(fraExp); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; double parRate = test.parRate(fraExp, prov); assertEquals(parRate, FORWARD_RATE); ResolvedFra fra = createNewFra(FRA, parRate); CurrencyAmount pv = test.presentValue(fra, prov); assertEquals(pv.getAmount(), 0.0, TOLERANCE); // test via FraTrade DiscountingFraTradePricer testTrade = new DiscountingFraTradePricer(test); assertEquals(testTrade.parRate(RFRA_TRADE, prov), test.parRate(RFRA, prov)); } /** * Test par rate for NONE FRA Discounting method. */ public void test_parRate_NONE() { ResolvedFra fraExp = RFRA_NONE; SimpleRatesProvider prov = createProvider(fraExp); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; double parRate = test.parRate(fraExp, prov); assertEquals(parRate, FORWARD_RATE); ResolvedFra fra = createNewFra(FRA_NONE, parRate); CurrencyAmount pv = test.presentValue(fra, prov); assertEquals(pv.getAmount(), 0.0, TOLERANCE); } /** * Test par rate for AFMA FRA Discounting method. */ public void test_parRate_AFMA() { ResolvedFra fraExp = RFRA_AFMA; SimpleRatesProvider prov = createProvider(fraExp); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; double parRate = test.parRate(fraExp, prov); assertEquals(parRate, FORWARD_RATE); ResolvedFra fra = createNewFra(FRA_AFMA, parRate); CurrencyAmount pv = test.presentValue(fra, prov); assertEquals(pv.getAmount(), 0.0, TOLERANCE); } /** * Test par spread for ISDA FRA Discounting method. */ public void test_parSpread_ISDA() { ResolvedFra fraExp = RFRA; SimpleRatesProvider prov = createProvider(fraExp); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; double parSpread = test.parSpread(fraExp, prov); ResolvedFra fra = createNewFra(FRA, FRA.getFixedRate() + parSpread); CurrencyAmount pv = test.presentValue(fra, prov); assertEquals(pv.getAmount(), 0.0, TOLERANCE); // test via FraTrade DiscountingFraTradePricer testTrade = new DiscountingFraTradePricer(test); assertEquals(testTrade.parSpread(RFRA_TRADE, prov), test.parSpread(RFRA, prov)); } /** * Test par spread for NONE FRA Discounting method. */ public void test_parSpread_NONE() { ResolvedFra fraExp = RFRA_NONE; SimpleRatesProvider prov = createProvider(fraExp); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; double parSpread = test.parSpread(fraExp, prov); ResolvedFra fra = createNewFra(FRA_NONE, FRA_NONE.getFixedRate() + parSpread); CurrencyAmount pv = test.presentValue(fra, prov); assertEquals(pv.getAmount(), 0.0, TOLERANCE); } /** * Test par spread for AFMA FRA Discounting method. */ public void test_parSpread_AFMA() { ResolvedFra fraExp = RFRA_AFMA; SimpleRatesProvider prov = createProvider(fraExp); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; double parSpread = test.parSpread(fraExp, prov); ResolvedFra fra = createNewFra(FRA_AFMA, FRA_AFMA.getFixedRate() + parSpread); CurrencyAmount pv = test.presentValue(fra, prov); assertEquals(pv.getAmount(), 0.0, TOLERANCE); } private static final double EPS_FD = 1E-7; private static final DiscountingFraProductPricer DEFAULT_PRICER = DiscountingFraProductPricer.DEFAULT; private static final DiscountingFraTradePricer DEFAULT_TRADE_PRICER = DiscountingFraTradePricer.DEFAULT; private static final RatesFiniteDifferenceSensitivityCalculator CAL_FD = new RatesFiniteDifferenceSensitivityCalculator(EPS_FD); private static final ImmutableRatesProvider IMM_PROV; static { CurveInterpolator interp = CurveInterpolators.DOUBLE_QUADRATIC; DoubleArray time_gbp = DoubleArray.of(0.0, 0.1, 0.25, 0.5, 0.75, 1.0, 2.0); DoubleArray rate_gbp = DoubleArray.of(0.0160, 0.0165, 0.0155, 0.0155, 0.0155, 0.0150, 0.014); InterpolatedNodalCurve dscCurve = InterpolatedNodalCurve.of(Curves.zeroRates("GBP-Discount", DAY_COUNT), time_gbp, rate_gbp, interp); DoubleArray time_index = DoubleArray.of(0.0, 0.25, 0.5, 1.0); DoubleArray rate_index = DoubleArray.of(0.0180, 0.0180, 0.0175, 0.0165); InterpolatedNodalCurve indexCurve = InterpolatedNodalCurve.of(Curves.zeroRates("GBP-GBPIBOR3M", DAY_COUNT), time_index, rate_index, interp); IMM_PROV = ImmutableRatesProvider.builder(VAL_DATE) .discountCurve(GBP, dscCurve) .iborIndexCurve(GBP_LIBOR_3M, indexCurve) .build(); } /** * Test par spread sensitivity for ISDA FRA Discounting method. */ public void test_parSpreadSensitivity_ISDA() { PointSensitivities sensiSpread = DEFAULT_PRICER.parSpreadSensitivity(RFRA, IMM_PROV); CurrencyParameterSensitivities sensiComputed = IMM_PROV.parameterSensitivity(sensiSpread); CurrencyParameterSensitivities sensiExpected = CAL_FD.sensitivity(IMM_PROV, (p) -> CurrencyAmount.of(FRA.getCurrency(), DEFAULT_PRICER.parSpread(RFRA, (p)))); assertTrue(sensiComputed.equalWithTolerance(sensiExpected, EPS_FD)); PointSensitivities sensiRate = DEFAULT_PRICER.parRateSensitivity(RFRA, IMM_PROV); assertTrue(sensiSpread.equalWithTolerance(sensiRate, EPS_FD)); // test via FraTrade assertEquals( DEFAULT_TRADE_PRICER.parRateSensitivity(RFRA_TRADE, IMM_PROV), DEFAULT_PRICER.parRateSensitivity(RFRA, IMM_PROV)); assertEquals( DEFAULT_TRADE_PRICER.parSpreadSensitivity(RFRA_TRADE, IMM_PROV), DEFAULT_PRICER.parSpreadSensitivity(RFRA, IMM_PROV)); } /** * Test par spread sensitivity for NONE FRA Discounting method. */ public void test_parSpreadSensitivity_NONE() { PointSensitivities sensiSpread = DEFAULT_PRICER.parSpreadSensitivity(RFRA_NONE, IMM_PROV); CurrencyParameterSensitivities sensiComputed = IMM_PROV.parameterSensitivity(sensiSpread); CurrencyParameterSensitivities sensiExpected = CAL_FD.sensitivity(IMM_PROV, (p) -> CurrencyAmount.of(FRA_NONE.getCurrency(), DEFAULT_PRICER.parSpread(RFRA_NONE, (p)))); assertTrue(sensiComputed.equalWithTolerance(sensiExpected, EPS_FD)); PointSensitivities sensiRate = DEFAULT_PRICER.parRateSensitivity(RFRA_NONE, IMM_PROV); assertTrue(sensiSpread.equalWithTolerance(sensiRate, EPS_FD)); } /** * Test par spread sensitivity for AFMA FRA Discounting method. */ public void test_parSpreadSensitivity_AFMA() { PointSensitivities sensiSpread = DEFAULT_PRICER.parSpreadSensitivity(RFRA_AFMA, IMM_PROV); CurrencyParameterSensitivities sensiComputed = IMM_PROV.parameterSensitivity(sensiSpread); CurrencyParameterSensitivities sensiExpected = CAL_FD.sensitivity(IMM_PROV, (p) -> CurrencyAmount.of(FRA_AFMA.getCurrency(), DEFAULT_PRICER.parSpread(RFRA_AFMA, (p)))); assertTrue(sensiComputed.equalWithTolerance(sensiExpected, EPS_FD)); PointSensitivities sensiRate = DEFAULT_PRICER.parRateSensitivity(RFRA_AFMA, IMM_PROV); assertTrue(sensiSpread.equalWithTolerance(sensiRate, EPS_FD)); } private ResolvedFra createNewFra(Fra product, double newFixedRate) { return Fra.builder() .buySell(product.getBuySell()) .notional(product.getNotional()) .startDate(product.getStartDate()) .endDate(product.getEndDate()) .index(product.getIndex()) .fixedRate(newFixedRate) .currency(product.getCurrency()) .build() .resolve(REF_DATA); } //------------------------------------------------------------------------- /** * Test cash flow for ISDA FRA Discounting method. */ public void test_cashFlows_ISDA() { ResolvedFra fraExp = RFRA; SimpleRatesProvider prov = createProvider(fraExp); double fixedRate = FRA.getFixedRate(); double yearFraction = fraExp.getYearFraction(); double notional = fraExp.getNotional(); double expected = notional * yearFraction * (FORWARD_RATE - fixedRate) / (1.0 + yearFraction * FORWARD_RATE); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; CashFlows computed = test.cashFlows(fraExp, prov); assertEquals(computed.getCashFlows().size(), 1); assertEquals(computed.getCashFlows().size(), 1); assertEquals(computed.getCashFlows().get(0).getPaymentDate(), fraExp.getPaymentDate()); assertEquals(computed.getCashFlows().get(0).getForecastValue().getCurrency(), fraExp.getCurrency()); assertEquals(computed.getCashFlows().get(0).getForecastValue().getAmount(), expected, TOLERANCE); // test via FraTrade DiscountingFraTradePricer testTrade = new DiscountingFraTradePricer(test); assertEquals(testTrade.cashFlows(RFRA_TRADE, prov), test.cashFlows(fraExp, prov)); } //------------------------------------------------------------------------- /** * Test explain. */ public void test_explainPresentValue_ISDA() { ResolvedFra fraExp = RFRA; SimpleRatesProvider prov = createProvider(fraExp); DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT; CurrencyAmount fvExpected = test.forecastValue(fraExp, prov); CurrencyAmount pvExpected = test.presentValue(fraExp, prov); ExplainMap explain = test.explainPresentValue(fraExp, prov); Currency currency = fraExp.getCurrency(); int daysBetween = (int) DAYS.between(fraExp.getStartDate(), fraExp.getEndDate()); assertEquals(explain.get(ExplainKey.ENTRY_TYPE).get(), "FRA"); assertEquals(explain.get(ExplainKey.PAYMENT_DATE).get(), fraExp.getPaymentDate()); assertEquals(explain.get(ExplainKey.START_DATE).get(), fraExp.getStartDate()); assertEquals(explain.get(ExplainKey.END_DATE).get(), fraExp.getEndDate()); assertEquals(explain.get(ExplainKey.ACCRUAL_YEAR_FRACTION).get(), fraExp.getYearFraction()); assertEquals(explain.get(ExplainKey.DAYS).get(), (Integer) (int) daysBetween); assertEquals(explain.get(ExplainKey.PAYMENT_CURRENCY).get(), currency); assertEquals(explain.get(ExplainKey.NOTIONAL).get().getAmount(), fraExp.getNotional(), TOLERANCE); assertEquals(explain.get(ExplainKey.TRADE_NOTIONAL).get().getAmount(), fraExp.getNotional(), TOLERANCE); assertEquals(explain.get(ExplainKey.OBSERVATIONS).get().size(), 1); ExplainMap explainObs = explain.get(ExplainKey.OBSERVATIONS).get().get(0); IborRateComputation floatingRate = (IborRateComputation) fraExp.getFloatingRate(); assertEquals(explainObs.get(ExplainKey.INDEX).get(), floatingRate.getIndex()); assertEquals(explainObs.get(ExplainKey.FIXING_DATE).get(), floatingRate.getFixingDate()); assertEquals(explainObs.get(ExplainKey.INDEX_VALUE).get(), FORWARD_RATE, TOLERANCE); assertEquals(explainObs.get(ExplainKey.FROM_FIXING_SERIES).isPresent(), false); assertEquals(explain.get(ExplainKey.DISCOUNT_FACTOR).get(), DISCOUNT_FACTOR, TOLERANCE); assertEquals(explain.get(ExplainKey.FIXED_RATE).get(), fraExp.getFixedRate(), TOLERANCE); assertEquals(explain.get(ExplainKey.PAY_OFF_RATE).get(), FORWARD_RATE, TOLERANCE); assertEquals(explain.get(ExplainKey.COMBINED_RATE).get(), FORWARD_RATE, TOLERANCE); assertEquals(explain.get(ExplainKey.UNIT_AMOUNT).get(), fvExpected.getAmount() / fraExp.getNotional(), TOLERANCE); assertEquals(explain.get(ExplainKey.FORECAST_VALUE).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.FORECAST_VALUE).get().getAmount(), fvExpected.getAmount(), TOLERANCE); assertEquals(explain.get(ExplainKey.PRESENT_VALUE).get().getCurrency(), currency); assertEquals(explain.get(ExplainKey.PRESENT_VALUE).get().getAmount(), pvExpected.getAmount(), TOLERANCE); // test via FraTrade DiscountingFraTradePricer testTrade = new DiscountingFraTradePricer(test); assertEquals(testTrade.explainPresentValue(RFRA_TRADE, prov), test.explainPresentValue(RFRA, prov)); } //------------------------------------------------------------------------- // creates a simple provider private SimpleRatesProvider createProvider(ResolvedFra fraExp) { DiscountFactors mockDf = SimpleDiscountFactors.of( GBP, VAL_DATE, ConstantCurve.of(Curves.discountFactors("DSC", DAY_COUNT), DISCOUNT_FACTOR)); LocalDateDoubleTimeSeries timeSeries = LocalDateDoubleTimeSeries.of(VAL_DATE, FORWARD_RATE); IborIndexRates mockIbor = SimpleIborIndexRates.of( GBP_LIBOR_3M, VAL_DATE, ConstantCurve.of(Curves.forwardRates("L3M", DAY_COUNT), FORWARD_RATE), timeSeries); SimpleRatesProvider prov = new SimpleRatesProvider(VAL_DATE, mockDf); prov.setIborRates(mockIbor); return prov; } //------------------------------------------------------------------------- public void test_presentValueSensitivity_zeroCurve_FD() { double eps = 1.0e-6; ImmutableRatesProvider prov = RatesProviderDataSets.MULTI_GBP_USD; RatesFiniteDifferenceSensitivityCalculator cal = new RatesFiniteDifferenceSensitivityCalculator(eps); DiscountingFraProductPricer pricer = DiscountingFraProductPricer.DEFAULT; ResolvedFra fraExp = RFRA; PointSensitivities point = pricer.presentValueSensitivity(fraExp, prov); CurrencyParameterSensitivities computed = prov.parameterSensitivity(point); CurrencyParameterSensitivities expected = cal.sensitivity(prov, p -> pricer.presentValue(fraExp, p)); assertTrue(computed.equalWithTolerance(expected, eps * FRA.getNotional())); } public void test_presentValueSensitivity_dfCurve_FD() { double eps = 1.0e-6; ImmutableRatesProvider prov = RatesProviderDataSets.MULTI_GBP_USD_SIMPLE; RatesFiniteDifferenceSensitivityCalculator cal = new RatesFiniteDifferenceSensitivityCalculator(eps); DiscountingFraProductPricer pricer = DiscountingFraProductPricer.DEFAULT; ResolvedFra fraExp = RFRA; PointSensitivities point = pricer.presentValueSensitivity(fraExp, prov); CurrencyParameterSensitivities computed = prov.parameterSensitivity(point); CurrencyParameterSensitivities expected = cal.sensitivity(prov, p -> pricer.presentValue(fraExp, p)); assertTrue(computed.equalWithTolerance(expected, eps * FRA.getNotional())); } //------------------------------------------------------------------------- private double forecastValueFwdSensitivity(ResolvedFra fra, double forwardRate, double eps) { RateComputationFn<RateComputation> obsFuncNew = mock(RateComputationFn.class); RatesProvider provNew = mock(RatesProvider.class); when(provNew.getValuationDate()).thenReturn(VAL_DATE); when(obsFuncNew.rate(fra.getFloatingRate(), fra.getStartDate(), fra.getEndDate(), provNew)) .thenReturn(forwardRate + eps); CurrencyAmount upValue = new DiscountingFraProductPricer(obsFuncNew).forecastValue(fra, provNew); when(obsFuncNew.rate(fra.getFloatingRate(), fra.getStartDate(), fra.getEndDate(), provNew)) .thenReturn(forwardRate - eps); CurrencyAmount downValue = new DiscountingFraProductPricer(obsFuncNew).forecastValue(fra, provNew); return upValue.minus(downValue).multipliedBy(0.5 / eps).getAmount(); } private double presentValueFwdSensitivity(ResolvedFra fra, double forwardRate, double discountFactor, double eps) { RateComputationFn<RateComputation> obsFuncNew = mock(RateComputationFn.class); RatesProvider provNew = mock(RatesProvider.class); when(provNew.getValuationDate()).thenReturn(VAL_DATE); when(provNew.discountFactor(fra.getCurrency(), fra.getPaymentDate())) .thenReturn(discountFactor); when(obsFuncNew.rate(fra.getFloatingRate(), fra.getStartDate(), fra.getEndDate(), provNew)) .thenReturn(forwardRate + eps); CurrencyAmount upValue = new DiscountingFraProductPricer(obsFuncNew).presentValue(fra, provNew); when(obsFuncNew.rate(fra.getFloatingRate(), fra.getStartDate(), fra.getEndDate(), provNew)) .thenReturn(forwardRate - eps); CurrencyAmount downValue = new DiscountingFraProductPricer(obsFuncNew).presentValue(fra, provNew); return upValue.minus(downValue).multipliedBy(0.5 / eps).getAmount(); } private double dscSensitivity( ResolvedFra fra, double forwardRate, double discountFactor, double paymentTime, double eps) { RatesProvider provNew = mock(RatesProvider.class); when(provNew.getValuationDate()).thenReturn(VAL_DATE); RateComputationFn<RateComputation> obsFuncNew = mock(RateComputationFn.class); when(obsFuncNew.rate(fra.getFloatingRate(), fra.getStartDate(), fra.getEndDate(), provNew)) .thenReturn(forwardRate); when(provNew.discountFactor(fra.getCurrency(), fra.getPaymentDate())) .thenReturn(discountFactor * Math.exp(-eps * paymentTime)); CurrencyAmount upDscValue = new DiscountingFraProductPricer(obsFuncNew).presentValue(fra, provNew); when(provNew.discountFactor(fra.getCurrency(), fra.getPaymentDate())) .thenReturn(discountFactor * Math.exp(eps * paymentTime)); CurrencyAmount downDscValue = new DiscountingFraProductPricer(obsFuncNew).presentValue(fra, provNew); return upDscValue.minus(downDscValue).multipliedBy(0.5 / eps).getAmount(); } }