/** Test FRA paying in the past. */
  public void test_presentValue_inPast() {
    ExpandedFra fraExp = FRA.expand().toBuilder().paymentDate(VAL_DATE.minusDays(1)).build();
    SimpleRatesProvider prov = createProvider(fraExp);

    DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT;
    CurrencyAmount computed = test.presentValue(fraExp, prov);
    assertEquals(computed.getAmount(), 0d, TOLERANCE);
  }
 /** Test par spread sensitivity for ISDA FRA Discounting method. */
 public void test_parSpreadSensitivity_ISDA() {
   PointSensitivities sensi = DEFAULT_PRICER.parSpreadSensitivity(FRA, IMM_PROV);
   CurveCurrencyParameterSensitivities sensiComputed = IMM_PROV.curveParameterSensitivity(sensi);
   CurveCurrencyParameterSensitivities sensiExpected =
       CAL_FD.sensitivity(
           IMM_PROV,
           (p) -> CurrencyAmount.of(FRA.getCurrency(), DEFAULT_PRICER.parSpread(FRA, (p))));
   assertTrue(sensiComputed.equalWithTolerance(sensiExpected, EPS_FD));
 }
  /** Test present value for ISDA FRA Discounting method. */
  public void test_presentValue_AFMA() {
    ExpandedFra fraExp = FRA_AFMA.expand();
    SimpleRatesProvider prov = createProvider(fraExp);

    DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT;
    CurrencyAmount pvComputed = test.presentValue(fraExp, prov);
    CurrencyAmount pvExpected = test.forecastValue(fraExp, prov).multipliedBy(DISCOUNT_FACTOR);
    assertEquals(pvComputed.getAmount(), pvExpected.getAmount(), TOLERANCE);
  }
  /** Test par spread for AFMA FRA Discounting method. */
  public void test_parSpread_AFMA() {
    ExpandedFra fraExp = FRA_AFMA.expand();
    SimpleRatesProvider prov = createProvider(fraExp);

    DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT;
    double parSpread = test.parSpread(fraExp, prov);
    Fra fra = createNewFra(FRA_AFMA, FRA_AFMA.getFixedRate() + parSpread);
    CurrencyAmount pv = test.presentValue(fra, prov);
    assertEquals(pv.getAmount(), 0.0, TOLERANCE);
  }
  /** Test par rate for NONE FRA Discounting method. */
  public void test_parRate_NONE() {
    ExpandedFra fraExp = FRA_NONE.expand();
    SimpleRatesProvider prov = createProvider(fraExp);

    DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT;
    double parRate = test.parRate(fraExp, prov);
    assertEquals(parRate, FORWARD_RATE);
    Fra fra = createNewFra(FRA_NONE, parRate);
    CurrencyAmount pv = test.presentValue(fra, prov);
    assertEquals(pv.getAmount(), 0.0, TOLERANCE);
  }
  /** Test forecast value for NONE FRA Discounting method. */
  public void test_forecastValue_NONE() {
    ExpandedFra fraExp = FRA_NONE.expand();
    SimpleRatesProvider prov = createProvider(fraExp);

    double fixedRate = FRA_NONE.getFixedRate();
    double yearFraction = fraExp.getYearFraction();
    double notional = fraExp.getNotional();
    double expected = notional * yearFraction * (FORWARD_RATE - fixedRate);

    DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT;
    CurrencyAmount computed = test.forecastValue(fraExp, prov);
    assertEquals(computed.getAmount(), expected, TOLERANCE);
  }
  /** Test forecast value sensitivity for AFMA FRA discounting method. */
  public void test_forecastValueSensitivity_AFMA() {
    ExpandedFra fraExp = FRA_AFMA.expand();
    SimpleRatesProvider prov = createProvider(fraExp);

    DiscountingFraProductPricer test = DiscountingFraProductPricer.DEFAULT;
    PointSensitivities sensitivity = test.forecastValueSensitivity(fraExp, prov);
    double eps = 1.e-7;
    double fdSense = forecastValueFwdSensitivity(FRA_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.getFixingDate(), FRA_AFMA.getStartDate());
    assertEquals(sensitivity0.getSensitivity(), fdSense, FRA_AFMA.getNotional() * eps);
  }
  /** Test explain. */
  public void test_explainPresentValue_ISDA() {
    ExpandedFra fraExp = FRA.expand();
    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.ACCRUAL_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);
    IborRateObservation floatingRate = (IborRateObservation) 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(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 present value sensitivity for ISDA */
  public void test_presentValueSensitivity_ISDA() {
    RateObservationFn<RateObservation> mockObs = mock(RateObservationFn.class);
    DiscountFactors mockDf = mock(DiscountFactors.class);
    SimpleRatesProvider simpleProv = new SimpleRatesProvider(VAL_DATE, mockDf);

    ExpandedFra fraExp = FRA.expand();
    double forwardRate = 0.05;
    double discountRate = 0.015;
    double paymentTime = 0.3;
    double discountFactor = Math.exp(-discountRate * paymentTime);
    LocalDate fixingDate = FRA.getStartDate();
    PointSensitivityBuilder sens = IborRateSensitivity.of(FRA.getIndex(), fixingDate, 1d);
    when(mockDf.discountFactor(fraExp.getPaymentDate())).thenReturn(discountFactor);
    when(mockDf.zeroRatePointSensitivity(fraExp.getPaymentDate()))
        .thenReturn(
            ZeroRateSensitivity.of(
                fraExp.getCurrency(), fraExp.getPaymentDate(), -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(FRA, forwardRate, discountFactor, paymentTime, eps);
    double fdSense = presentValueFwdSensitivity(FRA, 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.getFixingDate(), fixingDate);
    assertEquals(sensitivity0.getSensitivity(), fdSense, FRA.getNotional() * eps);
    ZeroRateSensitivity sensitivity1 = (ZeroRateSensitivity) sensitivities.get(1);
    assertEquals(sensitivity1.getCurrency(), FRA.getCurrency());
    assertEquals(sensitivity1.getDate(), fraExp.getPaymentDate());
    assertEquals(sensitivity1.getSensitivity(), fdDscSense, FRA.getNotional() * eps);

    // test via FraTrade
    DiscountingFraTradePricer testTrade = new DiscountingFraTradePricer(test);
    assertEquals(
        testTrade.presentValueSensitivity(FRA_TRADE, simpleProv),
        test.presentValueSensitivity(fraExp, simpleProv));
  }
  /** Test forecast value for ISDA FRA Discounting method. */
  public void test_forecastValue_ISDA() {
    ExpandedFra fraExp = FRA.expand();
    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;
    CurrencyAmount computed = test.forecastValue(fraExp, prov);
    assertEquals(computed.getAmount(), expected, TOLERANCE);

    // test via FraTrade
    DiscountingFraTradePricer testTrade = new DiscountingFraTradePricer(test);
    assertEquals(testTrade.forecastValue(FRA_TRADE, prov), test.forecastValue(fraExp, prov));
  }