/**
   * Test for the case where publication lag=0, effective offset=0 (GBP conventions) and no cutoff
   * period. The arithmetic average coupons are used mainly in USD. This test is more for
   * completeness than a real case.
   */
  public void rateGbpNoCutOff() {
    OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
    when(mockRates.getIndex()).thenReturn(GBP_SONIA);
    SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);

    for (int i = 0; i < FIXING_DATES.length; i++) {
      when(mockRates.rate(FIXING_DATES[i])).thenReturn(FIXING_RATES[i]);
    }
    OvernightAveragedRateObservation ro =
        OvernightAveragedRateObservation.of(GBP_SONIA, FIXING_START_DATE, FIXING_END_DATE, 0);
    ForwardOvernightAveragedRateObservationFn obsFn =
        ForwardOvernightAveragedRateObservationFn.DEFAULT;
    double accrualFactorTotal = 0.0d;
    double accruedRate = 0.0d;
    int indexLast = 5; // Fixing in the observation period are from 1 to 5 (inclusive)
    for (int i = 1; i <= indexLast; i++) {
      LocalDate startDate = FIXING_DATES[i]; // no effective lag
      LocalDate endDate = GBP_SONIA.calculateMaturityFromEffective(startDate);
      double af = GBP_SONIA.getDayCount().yearFraction(startDate, endDate);
      accrualFactorTotal += af;
      accruedRate += FIXING_RATES[i] * af;
    }
    double rateExpected = accruedRate / accrualFactorTotal;
    double rateComputed =
        obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProv);
    assertEquals(rateExpected, rateComputed, TOLERANCE_RATE);
  }
  /**
   * Test for the case where publication lag=0, effective offset=0 (GBP conventions) and no cutoff
   * period. The arithmetic average coupons are used mainly in USD. This test is more for
   * completeness than a real case.
   */
  public void rateGbpNoCutOffSensitivity() {
    OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
    when(mockRates.getIndex()).thenReturn(GBP_SONIA);
    SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);

    for (int i = 0; i < FIXING_DATES.length; i++) {
      when(mockRates.rate(FIXING_DATES[i])).thenReturn(FIXING_RATES[i]);
      LocalDate fixingStartDate = GBP_SONIA.calculateEffectiveFromFixing(FIXING_DATES[i]);
      LocalDate fixingEndDate = GBP_SONIA.calculateMaturityFromEffective(fixingStartDate);
      OvernightRateSensitivity sensitivity =
          OvernightRateSensitivity.of(
              GBP_SONIA, FIXING_DATES[i], fixingEndDate, GBP_SONIA.getCurrency(), 1d);
      when(mockRates.ratePointSensitivity(FIXING_DATES[i])).thenReturn(sensitivity);
    }
    OvernightAveragedRateObservation ro =
        OvernightAveragedRateObservation.of(GBP_SONIA, FIXING_START_DATE, FIXING_END_DATE, 0);
    ForwardOvernightAveragedRateObservationFn obsFn =
        ForwardOvernightAveragedRateObservationFn.DEFAULT;
    PointSensitivityBuilder sensitivityBuilderComputed =
        obsFn.rateSensitivity(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProv);
    PointSensitivities sensitivityComputed = sensitivityBuilderComputed.build().normalized();
    Double[] sensitivityExpected = computedSensitivityFD(ro, GBP_SONIA);
    assertEquals(sensitivityComputed.getSensitivities().size(), sensitivityExpected.length);
    for (int i = 0; i < sensitivityExpected.length; ++i) {
      assertEquals(
          sensitivityComputed.getSensitivities().get(i).getSensitivity(),
          sensitivityExpected[i],
          EPS_FD);
    }
  }
  /**
   * Test for the case where publication lag=1, effective offset=0 (USD conventions) and cutoff=2
   * (FedFund swaps).
   */
  public void rateFedFund() {
    OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
    when(mockRates.getIndex()).thenReturn(USD_FED_FUND);
    SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);

    for (int i = 0; i < FIXING_DATES.length; i++) {
      when(mockRates.rate(FIXING_DATES[i])).thenReturn(FIXING_RATES[i]);
    }
    OvernightAveragedRateObservation ro =
        OvernightAveragedRateObservation.of(USD_FED_FUND, FIXING_START_DATE, FIXING_END_DATE, 2);
    ForwardOvernightAveragedRateObservationFn obsFn =
        ForwardOvernightAveragedRateObservationFn.DEFAULT;
    double accrualFactorTotal = 0.0d;
    double accruedRate = 0.0d;
    int indexLast =
        5; // Fixing in the observation period are from 1 to 5 (inclusive), but last is modified by
           // cut-off
    for (int i = 1; i <= indexLast - 1; i++) {
      LocalDate endDate = USD_FED_FUND.calculateMaturityFromEffective(FIXING_DATES[i]);
      double af = USD_FED_FUND.getDayCount().yearFraction(FIXING_DATES[i], endDate);
      accrualFactorTotal += af;
      accruedRate += FIXING_RATES[i] * af;
    }
    // CutOff
    LocalDate endDate = USD_FED_FUND.calculateMaturityFromEffective(FIXING_DATES[indexLast]);
    double af = USD_FED_FUND.getDayCount().yearFraction(FIXING_DATES[indexLast], endDate);
    accrualFactorTotal += af;
    accruedRate += FIXING_RATES[indexLast - 1] * af;
    double rateExpected = accruedRate / accrualFactorTotal;
    double rateComputed =
        obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProv);
    assertEquals(rateExpected, rateComputed, TOLERANCE_RATE);
  }
  /**
   * Test for the case where publication lag=1, effective offset=0 (USD conventions) and no cutoff
   * period.
   */
  public void rateFedFundNoCutOff() {
    OvernightIndexRates mockRates = mock(OvernightIndexRates.class);
    when(mockRates.getIndex()).thenReturn(USD_FED_FUND);
    SimpleRatesProvider simpleProv = new SimpleRatesProvider(mockRates);

    for (int i = 0; i < FIXING_DATES.length; i++) {
      when(mockRates.rate(FIXING_DATES[i])).thenReturn(FIXING_RATES[i]);
    }
    OvernightAveragedRateObservation ro =
        OvernightAveragedRateObservation.of(USD_FED_FUND, FIXING_START_DATE, FIXING_END_DATE, 0);
    // Accrual dates = fixing dates
    ForwardOvernightAveragedRateObservationFn obsFn =
        ForwardOvernightAveragedRateObservationFn.DEFAULT;
    double accrualFactorTotal = 0.0d;
    double accruedRate = 0.0d;
    int indexLast = 5; // Fixing in the observation period are from 1 to 5 (inclusive)
    for (int i = 1; i <= indexLast; i++) {
      LocalDate endDate = USD_FED_FUND.calculateMaturityFromEffective(FIXING_DATES[i]);
      double af = USD_FED_FUND.getDayCount().yearFraction(FIXING_DATES[i], endDate);
      accrualFactorTotal += af;
      accruedRate += FIXING_RATES[i] * af;
    }
    double rateExpected = accruedRate / accrualFactorTotal;
    double rateComputed =
        obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProv);
    assertEquals(rateExpected, rateComputed, TOLERANCE_RATE);

    // explain
    ExplainMapBuilder builder = ExplainMap.builder();
    double explainedRate =
        obsFn.explainRate(
            ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, simpleProv, builder);
    assertEquals(explainedRate, rateExpected, TOLERANCE_RATE);

    ExplainMap built = builder.build();
    assertEquals(built.get(ExplainKey.OBSERVATIONS).isPresent(), false);
    assertEquals(
        built.get(ExplainKey.COMBINED_RATE).get().doubleValue(), rateExpected, TOLERANCE_RATE);
  }
  /** Test parameter sensitivity with finite difference sensitivity calculator. No cutoff period. */
  public void rateChfNoCutOffParameterSensitivity() {
    LocalDate[] valuationDate = {date(2015, 1, 1), date(2015, 1, 8)};
    double[] time = new double[] {0.0, 0.5, 1.0, 2.0, 5.0, 10.0};
    double[] rate = new double[] {0.0100, 0.0110, 0.0115, 0.0130, 0.0135, 0.0135};

    for (int loopvaldate = 0; loopvaldate < 2; loopvaldate++) {
      Curve onCurve =
          InterpolatedNodalCurve.of(Curves.zeroRates("ON", ACT_ACT_ISDA), time, rate, INTERPOLATOR);
      ImmutableRatesProvider prov =
          ImmutableRatesProvider.builder()
              .valuationDate(valuationDate[loopvaldate])
              .indexCurves(ImmutableMap.of(CHF_TOIS, onCurve))
              .timeSeries(ImmutableMap.of(CHF_TOIS, TIME_SERIES_BUILDER.build()))
              .build();
      OvernightAveragedRateObservation ro =
          OvernightAveragedRateObservation.of(CHF_TOIS, FIXING_START_DATE, FIXING_END_DATE, 0);
      ForwardOvernightAveragedRateObservationFn obsFn =
          ForwardOvernightAveragedRateObservationFn.DEFAULT;

      PointSensitivityBuilder sensitivityBuilderComputed =
          obsFn.rateSensitivity(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, prov);
      CurveCurrencyParameterSensitivities parameterSensitivityComputed =
          prov.curveParameterSensitivity(sensitivityBuilderComputed.build());

      CurveCurrencyParameterSensitivities parameterSensitivityExpected =
          CAL_FD.sensitivity(
              prov,
              (p) ->
                  CurrencyAmount.of(
                      CHF_TOIS.getCurrency(),
                      obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, (p))));
      assertTrue(
          parameterSensitivityComputed.equalWithTolerance(
              parameterSensitivityExpected, EPS_FD * 10.0));
    }
  }