/** Tests on the construction of interest rate future option with up-front payment. */
public class InterestRateFutureOptionMarginSecurityTest {
  // EURIBOR 3M Index
  private static final Period TENOR = Period.ofMonths(3);
  private static final int SETTLEMENT_DAYS = 2;
  private static final Calendar CALENDAR = new MondayToFridayCalendar("A");
  private static final DayCount DAY_COUNT_INDEX =
      DayCountFactory.INSTANCE.getDayCount("Actual/360");
  private static final BusinessDayConvention BUSINESS_DAY =
      BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("Modified Following");
  private static final boolean IS_EOM = true;
  private static final Currency CUR = Currency.EUR;
  private static final IborIndex IBOR_INDEX =
      new IborIndex(CUR, TENOR, SETTLEMENT_DAYS, DAY_COUNT_INDEX, BUSINESS_DAY, IS_EOM);
  // Future
  private static final ZonedDateTime SPOT_LAST_TRADING_DATE = DateUtils.getUTCDate(2012, 9, 19);
  private static final ZonedDateTime LAST_TRADING_DATE =
      ScheduleCalculator.getAdjustedDate(SPOT_LAST_TRADING_DATE, -SETTLEMENT_DAYS, CALENDAR);
  private static final double NOTIONAL = 1000000.0; // 1m
  private static final double FUTURE_FACTOR = 0.25;
  private static final String NAME = "EDU2";
  private static final double STRIKE = 0.9850;
  private static final InterestRateFutureSecurityDefinition EDU2_DEFINITION =
      new InterestRateFutureSecurityDefinition(
          LAST_TRADING_DATE, IBOR_INDEX, NOTIONAL, FUTURE_FACTOR, NAME, CALENDAR);
  private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2010, 8, 18);
  private static final String DISCOUNTING_CURVE_NAME = "Funding";
  private static final String FORWARD_CURVE_NAME = "Forward";
  private static final String[] CURVES = {DISCOUNTING_CURVE_NAME, FORWARD_CURVE_NAME};
  private static final InterestRateFutureSecurity EDU2 =
      EDU2_DEFINITION.toDerivative(REFERENCE_DATE, CURVES);
  // Option
  private static final ZonedDateTime EXPIRATION_DATE = DateUtils.getUTCDate(2011, 9, 16);
  private static final DayCount ACT_ACT =
      DayCountFactory.INSTANCE.getDayCount("Actual/Actual ISDA");
  private static final double EXPIRATION_TIME =
      ACT_ACT.getDayCountFraction(REFERENCE_DATE, EXPIRATION_DATE);
  private static final boolean IS_CALL = true;
  private static final InterestRateFutureOptionPremiumSecurity OPTION_EDU2 =
      new InterestRateFutureOptionPremiumSecurity(EDU2, EXPIRATION_TIME, STRIKE, IS_CALL);

  @Test(expectedExceptions = IllegalArgumentException.class)
  public void testNullUnderlying() {
    new InterestRateFutureOptionPremiumSecurity(null, EXPIRATION_TIME, STRIKE, IS_CALL);
  }

  @Test
  public void getter() {
    assertEquals(EDU2, OPTION_EDU2.getUnderlyingFuture());
    assertEquals(EXPIRATION_TIME, OPTION_EDU2.getExpirationTime());
    assertEquals(STRIKE, OPTION_EDU2.getStrike());
    assertEquals(IS_CALL, OPTION_EDU2.isCall());
    assertEquals(DISCOUNTING_CURVE_NAME, OPTION_EDU2.getDiscountingCurveName());
    assertEquals(FORWARD_CURVE_NAME, OPTION_EDU2.getForwardCurveName());
  }

  @Test
  /** Tests the equal and hash code methods. */
  public void equalHash() {
    final InterestRateFutureOptionPremiumSecurity newOption =
        new InterestRateFutureOptionPremiumSecurity(EDU2, EXPIRATION_TIME, STRIKE, IS_CALL);
    assertTrue(OPTION_EDU2.equals(newOption));
    assertEquals(OPTION_EDU2.hashCode(), newOption.hashCode());
    InterestRateFutureOptionPremiumSecurity modifiedOption;
    modifiedOption =
        new InterestRateFutureOptionPremiumSecurity(EDU2, EXPIRATION_TIME - 0.01, STRIKE, IS_CALL);
    assertFalse(OPTION_EDU2.equals(modifiedOption));
    modifiedOption =
        new InterestRateFutureOptionPremiumSecurity(EDU2, EXPIRATION_TIME, STRIKE + 0.01, IS_CALL);
    assertFalse(OPTION_EDU2.equals(modifiedOption));
    modifiedOption =
        new InterestRateFutureOptionPremiumSecurity(EDU2, EXPIRATION_TIME, STRIKE, !IS_CALL);
    assertFalse(OPTION_EDU2.equals(modifiedOption));
  }
}
/**
 * Tests for the methods related to interest rate securities pricing with Hull-White model convexity
 * adjustment.
 */
@Test(groups = TestGroup.UNIT)
public class InterestRateFutureSecurityHullWhiteMethodTest {

  private static final MulticurveProviderDiscount MULTICURVES =
      MulticurveProviderDiscountDataSets.createMulticurveEurUsd();
  private static final IborIndex[] INDEX_LIST =
      MulticurveProviderDiscountDataSets.getIndexesIborMulticurveEurUsd();
  private static final IborIndex EURIBOR3M = INDEX_LIST[0];
  private static final Currency EUR = EURIBOR3M.getCurrency();
  private static final Calendar CALENDAR = MulticurveProviderDiscountDataSets.getEURCalendar();
  // Future
  private static final ZonedDateTime SPOT_LAST_TRADING_DATE = DateUtils.getUTCDate(2012, 9, 19);
  private static final ZonedDateTime LAST_TRADING_DATE =
      ScheduleCalculator.getAdjustedDate(SPOT_LAST_TRADING_DATE, -EURIBOR3M.getSpotLag(), CALENDAR);
  private static final double NOTIONAL = 1000000.0; // 1m
  private static final double FUTURE_FACTOR = 0.25;
  private static final String NAME = "ERU2";

  private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2011, 5, 12);
  private static final InterestRateFutureSecurityDefinition ERU2_DEFINITION =
      new InterestRateFutureSecurityDefinition(
          LAST_TRADING_DATE, EURIBOR3M, NOTIONAL, FUTURE_FACTOR, NAME, CALENDAR);

  private static final InterestRateFutureSecurity ERU2 =
      ERU2_DEFINITION.toDerivative(REFERENCE_DATE);

  private static final double MEAN_REVERSION = 0.01;
  private static final double[] VOLATILITY = new double[] {0.01, 0.011, 0.012, 0.013, 0.014};
  private static final double[] VOLATILITY_TIME = new double[] {0.5, 1.0, 2.0, 5.0};
  private static final HullWhiteOneFactorPiecewiseConstantParameters MODEL_PARAMETERS =
      new HullWhiteOneFactorPiecewiseConstantParameters(
          MEAN_REVERSION, VOLATILITY, VOLATILITY_TIME);

  private static final HullWhiteOneFactorProviderDiscount HW_MULTICURVES =
      new HullWhiteOneFactorProviderDiscount(MULTICURVES, MODEL_PARAMETERS, EUR);
  private static final HullWhiteOneFactorPiecewiseConstantInterestRateModel MODEL =
      new HullWhiteOneFactorPiecewiseConstantInterestRateModel();

  private static final InterestRateFutureSecurityHullWhiteMethod METHOD_IRFUT_HW =
      InterestRateFutureSecurityHullWhiteMethod.getInstance();

  private static final MarketQuoteHullWhiteCalculator MQHWC =
      MarketQuoteHullWhiteCalculator.getInstance();
  private static final MarketQuoteCurveSensitivityHullWhiteCalculator MQCSHWC =
      MarketQuoteCurveSensitivityHullWhiteCalculator.getInstance();
  private static final ConvexityAdjustmentHullWhiteCalculator CAHWC =
      ConvexityAdjustmentHullWhiteCalculator.getInstance();
  private static final ParRateHullWhiteCalculator PRHWC = ParRateHullWhiteCalculator.getInstance();

  private static final double SHIFT_FD = 1.0E-6;
  private static final SimpleParameterSensitivityParameterCalculator<
          HullWhiteOneFactorProviderInterface>
      SPSHWC = new SimpleParameterSensitivityParameterCalculator<>(MQCSHWC);
  private static final SimpleParameterSensitivityHullWhiteDiscountInterpolatedFDCalculator
      SPSHWC_FD =
          new SimpleParameterSensitivityHullWhiteDiscountInterpolatedFDCalculator(MQHWC, SHIFT_FD);

  private static final double TOLERANCE_PRICE = 1.0E-10;
  private static final double TOLERANCE_PRICE_DELTA = 1.0E-8;

  @Test
  /** Test the price computed from the curves and HW parameters. */
  public void price() {
    final double price = METHOD_IRFUT_HW.price(ERU2, HW_MULTICURVES);
    final double forward =
        MULTICURVES.getSimplyCompoundForwardRate(
            EURIBOR3M,
            ERU2.getFixingPeriodStartTime(),
            ERU2.getFixingPeriodEndTime(),
            ERU2.getFixingPeriodAccrualFactor());
    final double factor =
        MODEL.futuresConvexityFactor(
            MODEL_PARAMETERS,
            ERU2.getTradingLastTime(),
            ERU2.getFixingPeriodStartTime(),
            ERU2.getFixingPeriodEndTime());
    final double expectedPrice =
        1.0 - factor * forward + (1 - factor) / ERU2.getFixingPeriodAccrualFactor();
    assertEquals(
        "InterestRateFutureSecurityHullWhiteProviderMethod: price",
        expectedPrice,
        price,
        TOLERANCE_PRICE);
  }

  @Test
  /** Test the par rate computed from the curves and HW parameters. Par rate = 1-price. */
  public void parRate() {
    final double price = METHOD_IRFUT_HW.price(ERU2, HW_MULTICURVES);
    final double parRateExpected = 1.0d - price;
    final double parRateComputed = METHOD_IRFUT_HW.parRate(ERU2, HW_MULTICURVES);
    assertEquals(
        "InterestRateFutureSecurityHullWhiteProviderMethod: parRate",
        parRateExpected,
        parRateComputed,
        TOLERANCE_PRICE);
  }

  @Test
  /** Test the par rate computed from the method and the calculator. */
  public void parRateMethodVsCalculator() {
    final double parRateMethod = METHOD_IRFUT_HW.parRate(ERU2, HW_MULTICURVES);
    final double parRateCalculator = ERU2.accept(PRHWC, HW_MULTICURVES);
    assertEquals(
        "InterestRateFutureSecurityHullWhiteProviderMethod: parRate",
        parRateMethod,
        parRateCalculator,
        TOLERANCE_PRICE);
  }

  @Test
  /** Test the price as "MarketQuote" */
  public void marketQuote() {
    final double priceMethod = METHOD_IRFUT_HW.price(ERU2, HW_MULTICURVES);
    final double marketQuote = ERU2.accept(MQHWC, HW_MULTICURVES);
    assertEquals(
        "InterestRateFutureSecurityHullWhiteProviderMethod: price",
        priceMethod,
        marketQuote,
        TOLERANCE_PRICE);
  }

  @Test
  /** Test the convexity adjustment */
  public void convexityAdjustment() {
    final double price = METHOD_IRFUT_HW.price(ERU2, HW_MULTICURVES);
    final double forward =
        MULTICURVES.getSimplyCompoundForwardRate(
            EURIBOR3M,
            ERU2.getFixingPeriodStartTime(),
            ERU2.getFixingPeriodEndTime(),
            ERU2.getFixingPeriodAccrualFactor());
    final double convexityAdjustment = METHOD_IRFUT_HW.convexityAdjustment(ERU2, HW_MULTICURVES);
    assertEquals(
        "InterestRateFutureSecurityHullWhiteProviderMethod: convexity adjustment",
        price - (1.0d - forward),
        convexityAdjustment,
        TOLERANCE_PRICE);
    final double caCalculator = ERU2.accept(CAHWC, HW_MULTICURVES);
    assertEquals(
        "DeliverableSwapFuturesSecurityDefinition: convexity adjustment",
        caCalculator,
        convexityAdjustment,
        TOLERANCE_PRICE);
  }

  @Test
  /** Test the price curve sensitivity versus a finite difference computation. */
  public void priceCurveSensitivity() {
    final SimpleParameterSensitivity pcsExact =
        SPSHWC.calculateSensitivity(ERU2, HW_MULTICURVES, MULTICURVES.getAllNames());
    final SimpleParameterSensitivity pcsFD = SPSHWC_FD.calculateSensitivity(ERU2, HW_MULTICURVES);
    AssertSensitivityObjects.assertEquals(
        "DeliverableSwapFuturesSecurityHullWhiteMethod: priceCurveSensitivity",
        pcsExact,
        pcsFD,
        TOLERANCE_PRICE_DELTA);
  }
}