Example #1
0
  public static void annuityFixedDemo(PrintStream out) {
    double[] paymentTimes = fixedPaymentTimes(MATURITY);
    AnnuityCouponFixed annuity =
        new AnnuityCouponFixed(CCY, paymentTimes, R, LIBOR_CURVE_NAME, false);

    out.println(Arrays.deepToString(annuity.getPayments()));

    YieldCurveBundle bundle = getBundle();

    PresentValueCalculator presentValueCalculator = PresentValueCalculator.getInstance();
    double presentValue = presentValueCalculator.visit(annuity, bundle);
    out.format("Present Value %f%n", presentValue);
  }
 @Test
 /** Tests put call parity. */
 public void presentValuePutCallParity() {
   final double strike = 1.45;
   final boolean isCall = true;
   final boolean isLong = true;
   final double notional = 100000000;
   final ZonedDateTime payDate =
       ScheduleCalculator.getAdjustedDate(
           REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR);
   final ZonedDateTime expDate =
       ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR);
   final ForexDefinition forexUnderlyingDefinition =
       new ForexDefinition(EUR, USD, payDate, notional, strike);
   final ForexOptionDigitalDefinition callDefinition =
       new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong);
   final ForexOptionDigitalDefinition putDefinition =
       new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, !isCall, isLong);
   final ForexOptionDigital call = callDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME);
   final ForexOptionDigital put = putDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME);
   final MultipleCurrencyAmount pvCall = METHOD_DIGITAL_SPREAD.presentValue(call, SMILE_BUNDLE);
   final MultipleCurrencyAmount pvPut = METHOD_DIGITAL_SPREAD.presentValue(put, SMILE_BUNDLE);
   final Double pvCash = PVC.visit(put.getUnderlyingForex().getPaymentCurrency2(), CURVES);
   assertEquals(
       "Forex Digital option: call spread method - present value",
       pvCall.getAmount(USD) + pvPut.getAmount(USD),
       Math.abs(pvCash),
       TOLERANCE_PRICE_FLAT);
 }
/** Tests related to the discounting method for bond security. */
public class BondSecurityFRDiscountingMethodTest {

  // Calculators
  private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance();
  private static final BondSecurityDiscountingMethod METHOD =
      BondSecurityDiscountingMethod.getInstance();

  private static final double TOLERANCE_PRICE = 1.0E-8;

  private static final String REPO_TYPE = "General collateral";
  private static final Currency EUR = Currency.EUR;
  private static final Calendar TARGET = new MondayToFridayCalendar("TARGET");
  private static final DayCount DAY_COUNT_ACTACTICMA =
      DayCountFactory.INSTANCE.getDayCount("Actual/Actual ICMA");
  private static final BusinessDayConvention BUSINESS_DAY_FOL =
      BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("Following");
  private static final boolean IS_EOM_FIXED = false;

  // To derivatives
  private static final DayCount ACT_ACT =
      DayCountFactory.INSTANCE.getDayCount("Actual/Actual ISDA");
  private static final String CREDIT_CURVE_NAME = "Credit";
  private static final String REPO_CURVE_NAME = "Repo";
  private static final String FORWARD_CURVE_NAME = "Forward";
  private static final String[] CURVES_NAME = {
    CREDIT_CURVE_NAME, REPO_CURVE_NAME, FORWARD_CURVE_NAME
  };
  private static final YieldCurveBundle CURVES = TestsDataSetsSABR.createCurvesBond1();

  // FRTR 4 1/2 04/25/41 - ISIN:  FR0010773192
  private static final String ISSUER_FR = "FRANCE (GOVT OF)";
  private static final YieldConvention YIELD_CONVENTION_FRANCE =
      SimpleYieldConvention.FRANCE_COMPOUND_METHOD;
  private static final int SETTLEMENT_DAYS_FR = 3;
  private static final Period PAYMENT_TENOR_FR = Period.ofMonths(12);
  //  private static final int COUPON_PER_YEAR_FR = 1;
  private static final ZonedDateTime BOND_START_FR = DateUtils.getUTCDate(2009, 4, 25);
  private static final ZonedDateTime BOND_MATURITY_FR = DateUtils.getUTCDate(2041, 4, 25);
  private static final ZonedDateTime BOND_FIRSTCPN_FR = DateUtils.getUTCDate(2010, 4, 25);
  private static final double RATE_FR = 0.0450;
  private static final BondFixedSecurityDefinition BOND_FR_SECURITY_DEFINITION =
      BondFixedSecurityDefinition.from(
          EUR,
          BOND_START_FR,
          BOND_FIRSTCPN_FR,
          BOND_MATURITY_FR,
          PAYMENT_TENOR_FR,
          RATE_FR,
          SETTLEMENT_DAYS_FR,
          TARGET,
          DAY_COUNT_ACTACTICMA,
          BUSINESS_DAY_FOL,
          YIELD_CONVENTION_FRANCE,
          IS_EOM_FIXED,
          ISSUER_FR);
}
 @Test
 /** Tests the put/call parity currency exposure. */
 public void currencyExposurePutCallParity() {
   final double strike = 1.45;
   final boolean isCall = true;
   final boolean isLong = true;
   final double notional = 100000000;
   final ZonedDateTime payDate =
       ScheduleCalculator.getAdjustedDate(
           REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR);
   final ZonedDateTime expDate =
       ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR);
   final ForexDefinition forexUnderlyingDefinition =
       new ForexDefinition(EUR, USD, payDate, notional, strike);
   final ForexOptionDigitalDefinition forexOptionDefinitionCall =
       new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong);
   final ForexOptionDigitalDefinition forexOptionDefinitionPut =
       new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, !isCall, isLong);
   final ForexOptionDigital forexOptionCall =
       forexOptionDefinitionCall.toDerivative(REFERENCE_DATE, CURVES_NAME);
   final ForexOptionDigital forexOptionPut =
       forexOptionDefinitionPut.toDerivative(REFERENCE_DATE, CURVES_NAME);
   final MultipleCurrencyAmount currencyExposureCall =
       METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionCall, SMILE_BUNDLE);
   final MultipleCurrencyAmount currencyExposurePut =
       METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionPut, SMILE_BUNDLE);
   final Double pvCash =
       PVC.visit(forexOptionPut.getUnderlyingForex().getPaymentCurrency2(), CURVES);
   assertEquals(
       "Forex Digital option: currency exposure put/call parity foreign",
       0,
       currencyExposureCall.getAmount(EUR) + currencyExposurePut.getAmount(EUR),
       TOLERANCE_PRICE);
   assertEquals(
       "Forex Digital option: currency exposure put/call parity domestic",
       Math.abs(pvCash),
       currencyExposureCall.getAmount(USD) + currencyExposurePut.getAmount(USD),
       TOLERANCE_PRICE);
 }
/** Class with methods related to bond transaction valued by discounting. */
public final class BondTransactionDiscountingMethod {

  private static final Logger LOGGER =
      LoggerFactory.getLogger(BondTransactionDiscountingMethod.class);
  /** The unique instance of the class. */
  private static final BondTransactionDiscountingMethod INSTANCE =
      new BondTransactionDiscountingMethod();

  /**
   * Return the class instance.
   *
   * @return The instance.
   */
  public static BondTransactionDiscountingMethod getInstance() {
    return INSTANCE;
  }

  /** Constructor */
  private BondTransactionDiscountingMethod() {}

  /** The present value calculator (for the different parts of the bond transaction). */
  private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance();

  /** The present value calculator (for the different parts of the bond transaction). */
  private static final PresentValueCurveSensitivityCalculator PVSC =
      PresentValueCurveSensitivityCalculator.getInstance();

  private static final BondSecurityDiscountingMethod METHOD_SECURITY =
      BondSecurityDiscountingMethod.getInstance();

  /**
   * Compute the present value of a fixed coupon bond transaction.
   *
   * @param bond The bond transaction.
   * @param curves The curve bundle.
   * @return The present value.
   */
  public double presentValue(final BondFixedTransaction bond, final YieldCurveBundle curves) {
    final double pvNominal = bond.getBondTransaction().getNominal().accept(PVC, curves);
    final double pvCoupon = bond.getBondTransaction().getCoupon().accept(PVC, curves);
    final double settlementAmount =
        -(bond.getTransactionPrice()
                    * bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional()
                + bond.getBondTransaction().getAccruedInterest())
            * bond.getQuantity();
    final PaymentFixed settlement =
        new PaymentFixed(
            bond.getBondTransaction().getCurrency(),
            bond.getBondTransaction().getSettlementTime(),
            settlementAmount,
            bond.getBondTransaction().getRepoCurveName());
    final double pvSettlement = settlement.accept(PVC, curves);
    return (pvNominal + pvCoupon) * bond.getQuantity() + pvSettlement;
  }

  /**
   * Compute the present value of a Ibor coupon bond (FRN) transaction.
   *
   * @param bond The bond transaction.
   * @param curves The curve bundle.
   * @return The present value.
   */
  public double presentValue(final BondIborTransaction bond, final YieldCurveBundle curves) {
    final double pvNominal = bond.getBondTransaction().getNominal().accept(PVC, curves);
    final double pvCoupon = bond.getBondTransaction().getCoupon().accept(PVC, curves);
    final double settlementAmount =
        bond.getTransactionPrice()
            * bond.getBondTransaction()
                .getCoupon()
                .getNthPayment(0)
                .getNotional(); // FIXME: add accrued.
    LOGGER.error("The FRN settlement amount does not include the accrued interests.");
    final PaymentFixed settlement =
        new PaymentFixed(
            bond.getBondTransaction().getCurrency(),
            bond.getBondTransaction().getSettlementTime(),
            settlementAmount,
            bond.getBondTransaction().getRepoCurveName());
    final double pvSettlement = settlement.accept(PVC, curves);
    return (pvNominal + pvCoupon) * bond.getQuantity() + pvSettlement;
  }

  /**
   * Compute the present value of a bond transaction from its clean price.
   *
   * @param bond The bond transaction.
   * @param curves The curve bundle.
   * @param cleanPrice The bond clean price.
   * @return The present value.
   */
  public double presentValueFromCleanPrice(
      final BondTransaction<? extends BondSecurity<? extends Payment, ? extends Coupon>> bond,
      final YieldCurveBundle curves,
      final double cleanPrice) {
    ArgumentChecker.isTrue(
        bond instanceof BondFixedTransaction,
        "Present value from clean price only for fixed coupon bond");
    final BondFixedTransaction bondFixed = (BondFixedTransaction) bond;
    final double dfSettle =
        curves
            .getCurve(bondFixed.getBondStandard().getRepoCurveName())
            .getDiscountFactor(bondFixed.getBondTransaction().getSettlementTime());
    final double pvPriceStandard =
        (cleanPrice * bondFixed.getNotionalStandard()
                + bondFixed.getBondStandard().getAccruedInterest())
            * dfSettle;
    final double pvNominalStandard = bond.getBondStandard().getNominal().accept(PVC, curves);
    final double pvCouponStandard = bond.getBondStandard().getCoupon().accept(PVC, curves);
    final double pvDiscountingStandard = (pvNominalStandard + pvCouponStandard);
    final double pvNominalTransaction = bond.getBondTransaction().getNominal().accept(PVC, curves);
    final double pvCouponTransaction = bond.getBondTransaction().getCoupon().accept(PVC, curves);
    final double pvDiscountingTransaction = (pvNominalTransaction + pvCouponTransaction);
    return (pvDiscountingTransaction - pvDiscountingStandard + pvPriceStandard)
        * bond.getQuantity();
  }

  /**
   * Compute the present value of a bond transaction from its yield-to-maturity.
   *
   * @param bond The bond transaction.
   * @param curves The curve bundle.
   * @param yield The bond yield.
   * @return The present value.
   */
  public double presentValueFromYield(
      final BondTransaction<? extends BondSecurity<? extends Payment, ? extends Coupon>> bond,
      final YieldCurveBundle curves,
      final double yield) {
    ArgumentChecker.notNull(bond, "Bond");
    ArgumentChecker.isTrue(
        bond instanceof BondFixedTransaction,
        "Present value from clean price only for fixed coupon bond");
    final BondFixedTransaction bondFixed = (BondFixedTransaction) bond;
    double cleanPrice = METHOD_SECURITY.cleanPriceFromYield(bondFixed.getBondStandard(), yield);
    return presentValueFromCleanPrice(bond, curves, cleanPrice);
  }

  /**
   * Compute the present value sensitivity of a bond transaction.
   *
   * @param bond The bond transaction.
   * @param curves The curve bundle.
   * @return The present value sensitivity.
   */
  public InterestRateCurveSensitivity presentValueSensitivity(
      final BondFixedTransaction bond, final YieldCurveBundle curves) {
    final InterestRateCurveSensitivity pvsNominal =
        new InterestRateCurveSensitivity(
            bond.getBondTransaction().getNominal().accept(PVSC, curves));
    final InterestRateCurveSensitivity pvsCoupon =
        new InterestRateCurveSensitivity(
            bond.getBondTransaction().getCoupon().accept(PVSC, curves));
    final double settlementAmount =
        -(bond.getTransactionPrice()
                    * bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional()
                + bond.getBondTransaction().getAccruedInterest())
            * bond.getQuantity();
    final PaymentFixed settlement =
        new PaymentFixed(
            bond.getBondTransaction().getCurrency(),
            bond.getBondTransaction().getSettlementTime(),
            settlementAmount,
            bond.getBondTransaction().getRepoCurveName());
    final InterestRateCurveSensitivity pvsSettlement =
        new InterestRateCurveSensitivity(settlement.accept(PVSC, curves));
    return pvsNominal.plus(pvsCoupon).multipliedBy(bond.getQuantity()).plus(pvsSettlement);
  }

  public InterestRateCurveSensitivity presentValueSensitivity(
      final BondIborTransaction bond, final YieldCurveBundle curves) {
    final InterestRateCurveSensitivity pvsNominal =
        new InterestRateCurveSensitivity(
            bond.getBondTransaction().getNominal().accept(PVSC, curves));
    final InterestRateCurveSensitivity pvsCoupon =
        new InterestRateCurveSensitivity(
            bond.getBondTransaction().getCoupon().accept(PVSC, curves));
    final double settlementAmount =
        bond.getTransactionPrice()
            * bond.getBondTransaction()
                .getCoupon()
                .getNthPayment(0)
                .getNotional(); // FIXME: add accrued.
    LOGGER.error("The FRN settlement amount does not include the accrued interests.");
    final PaymentFixed settlement =
        new PaymentFixed(
            bond.getBondTransaction().getCurrency(),
            bond.getBondTransaction().getSettlementTime(),
            settlementAmount,
            bond.getBondTransaction().getRepoCurveName());
    final InterestRateCurveSensitivity pvsSettlement =
        new InterestRateCurveSensitivity(settlement.accept(PVSC, curves));
    return pvsNominal.plus(pvsCoupon).multipliedBy(bond.getQuantity()).plus(pvsSettlement);
  }
}
Example #6
0
/** Unit tests for USD deals */
@Test(groups = TestGroup.UNIT)
public class USDTest extends AbstractMockSourcesTest {
  private static final Logger s_logger = LoggerFactory.getLogger(USDTest.class);
  private static final String CURRENCY = "USD";

  private static final ZonedDateTime TODAY = DateUtils.getUTCDate(2013, 10, 14);
  private static final String ON_NAME = "USD_FEDFUNDS_1D_ERS";
  private static final String ONE_MONTH_NAME = "USD_LIBOR_1M_ERS";
  private static final String THREE_MONTH_NAME = "USD_LIBOR_3M_ERS";
  private static final String SIX_MONTH_NAME = "USD_LIBOR_6M_ERS";
  static final String discountingCurvename = "Discounting";
  static final String forward1MCurveName = "Forward 1M";
  static final String forward3MCurveName = "Forward 3M";
  static final String forward6MCurveName = "Forward 6M";

  static final Currency ccy = Currency.USD;

  private static final String PAY_CURRENCY = "LEG1_CCY";

  private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance();

  public void test() throws Exception {
    IRSwapTradeParser tradeParser = new IRSwapTradeParser();
    Resource resource =
        ResourceUtils.createResource(
            "classpath:com/opengamma/financial/analytics/test/Trades14Oct.csv");
    List<IRSwapSecurity> trades = tradeParser.parseCSVFile(resource.getURL());
    List<IRSwapSecurity> tradesClean = Lists.newArrayList();
    for (IRSwapSecurity irSwapSecurity : trades) {

      String currency = irSwapSecurity.getRawInput().getString(PAY_CURRENCY);
      if (currency.equals(CURRENCY)) {
        tradesClean.add(irSwapSecurity);
      }
    }
    // Build the curve bundle
    final HashMap<String, Currency> ccyMap = new HashMap<>();
    ccyMap.put(discountingCurvename, ccy);
    ccyMap.put(forward3MCurveName, ccy);
    ccyMap.put(forward6MCurveName, ccy);
    final FXMatrix fx = new FXMatrix(ccy);
    final YieldCurveBundle curvesClean = new YieldCurveBundle(fx, ccyMap);

    IRCurveParser curveParser = new IRCurveParser();
    Resource resourceCurve =
        ResourceUtils.createResource(
            "classpath:com/opengamma/financial/analytics/test/Base_Curves_20131014_Clean.csv");
    List<InterpolatedDoublesCurve> curves = curveParser.parseCSVFile(resourceCurve.getURL());

    for (InterpolatedDoublesCurve interpolatedDoublesCurve : curves) {

      String name = interpolatedDoublesCurve.getName();
      if (name.equals(ON_NAME)) {
        curvesClean.setCurve(discountingCurvename, DiscountCurve.from(interpolatedDoublesCurve));
      }
      if (name.equals(ONE_MONTH_NAME)) {
        curvesClean.setCurve(forward1MCurveName, DiscountCurve.from(interpolatedDoublesCurve));
      }
      if (name.equals(THREE_MONTH_NAME)) {
        curvesClean.setCurve(forward3MCurveName, DiscountCurve.from(interpolatedDoublesCurve));
      }
      if (name.equals(SIX_MONTH_NAME)) {
        curvesClean.setCurve(forward6MCurveName, DiscountCurve.from(interpolatedDoublesCurve));
      }
    }

    // Convert the swap security into a swap definition
    final SwapSecurityConverterDeprecated swapConverter =
        new SwapSecurityConverterDeprecated(
            _holidaySource, _conventionBundleSource, _regionSource, false);
    final FRASecurityConverterDeprecated fraConverter =
        new FRASecurityConverterDeprecated(_holidaySource, _regionSource, _conventionBundleSource);
    final ZeroDepositConverter ZeroCouponConverter =
        new ZeroDepositConverter(_conventionBundleSource, _holidaySource);
    List<SwapDefinition> swapsDefinition = Lists.newArrayList();
    List<ForwardRateAgreementDefinition> frasDefinition = Lists.newArrayList();
    List<DepositZeroDefinition> zcsDefinition = Lists.newArrayList();
    /*for (IRSwapSecurity irSwapSecurity : tradesClean) {
      switch (irSwapSecurity.getRawInput().getString("PRODUCT_TYPE")) {
        case "SWAP":
          swapsDefinition.add((SwapDefinition) swapConverter.visitSwapSecurity(irSwapSecurity.getSwapSecurity()));

          // we don't treat the fra case at the moment
          case "FRA":
            frasDefinition.add((ForwardRateAgreementDefinition) fraConverter.visitSwapSecurity(irSwapSecurity.getSwapSecurity()));
          case "OIS":
            swapsDefinition.add((SwapDefinition) swapConverter.visitSwapSecurity(irSwapSecurity.getSwapSecurity()));

          // we don't treat the fra case at the moment
           case "ZCS":
             zcsDefinition.add((DepositZeroDefinition) ZeroCouponConverter.visitSwapSecurity(irSwapSecurity.getSwapSecurity()));
      }
    }
    */
    // Load the historical time series from a csv file

    /* NonVersionedRedisHistoricalTimeSeriesSource source = new NonVersionedRedisHistoricalTimeSeriesSource(getJedisPool(), getRedisPrefix());
    CMECurveFixingTSLoader loader = new CMECurveFixingTSLoader(source);*/
    /*  loader.loadCurveFixingCSVFile("/vols/ogdev/CME/curve-fixing/sample-cme-curve-fixing.csv");

    HistoricalTimeSeries historicalTimeSeries = source.getHistoricalTimeSeries(UniqueId.of(ExternalSchemes.ISDA.getName(), "CHF-LIBOR-BBA-6M"));
    assertNotNull(historicalTimeSeries);
    LocalDateDoubleTimeSeries timeSeries = historicalTimeSeries.getTimeSeries();
    assertNotNull(timeSeries);
    assertEquals(5996, timeSeries.size());*/

    // convert the definition into a derivative
    /*   List<Swap> swapDerivatives = Lists.newArrayList();
    for (SwapDefinition swapDefinition : swapsDefinition) {
      swapDerivatives.add(swapDefinition.toDerivative(TODAY, data, curvesClean));
    }
    List<Swap> frasDerivatives = Lists.newArrayList();
    for (ForwardRateAgreementDefinition fraDefinition : frasDefinition) {
      frasDerivatives.add(fraDefinition.toDerivative(TODAY, data, curvesClean));
    }
    List<Swap> zcsDerivatives = Lists.newArrayList();
    for (DepositZeroDefinition zcDefinition : zcsDefinition) {
      zcsDerivatives.add(zcDefinition.toDerivative(TODAY, data, curvesClean));
    }*/

    // Check the npv

    s_logger.warn("Got {} trades", trades.size());
  }
}
 @Override
 protected InstrumentDerivativeVisitorAdapter<YieldCurveBundle, Double> getCalculator() {
   return PresentValueCalculator.getInstance();
 }
/**
 * Test related to the pricing and sensitivity of the Ibor cap/floor with the SABR model and
 * extrapolation for high strikes.
 *
 * @deprecated This class tests deprecated functionality.
 */
@Deprecated
public class CapFloorIborSABRExtrapolationRightMethodTest {
  // Details
  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 INDEX =
      new IborIndex(CUR, TENOR, SETTLEMENT_DAYS, DAY_COUNT_INDEX, BUSINESS_DAY, IS_EOM, "Ibor");
  private static final ZonedDateTime FIXING_DATE = DateUtils.getUTCDate(2011, 1, 3);
  private static final double NOTIONAL = 1000000; // 1m
  private static final double STRIKE = 0.04;
  private static final double STRIKE_HIGH = 0.09;
  private static final boolean IS_CAP = true;
  // Definition description
  private static final CapFloorIborDefinition CAP_LONG_DEFINITION =
      CapFloorIborDefinition.from(NOTIONAL, FIXING_DATE, INDEX, STRIKE, IS_CAP, CALENDAR);
  private static final CapFloorIborDefinition CAP_HIGH_LONG_DEFINITION =
      CapFloorIborDefinition.from(NOTIONAL, FIXING_DATE, INDEX, STRIKE_HIGH, IS_CAP, CALENDAR);
  private static final CouponIborDefinition COUPON_IBOR_DEFINITION =
      CouponIborDefinition.from(NOTIONAL, FIXING_DATE, INDEX, CALENDAR);
  private static final CouponFixedDefinition COUPON_STRIKE_DEFINITION =
      new CouponFixedDefinition(COUPON_IBOR_DEFINITION, STRIKE);
  private static final CouponFixedDefinition COUPON_STRIKE_HIGH_DEFINITION =
      new CouponFixedDefinition(COUPON_IBOR_DEFINITION, STRIKE_HIGH);
  private static final CapFloorIborDefinition CAP_SHORT_DEFINITION =
      CapFloorIborDefinition.from(-NOTIONAL, FIXING_DATE, INDEX, STRIKE, IS_CAP, CALENDAR);
  private static final CapFloorIborDefinition CAP_HIGH_SHORT_DEFINITION =
      CapFloorIborDefinition.from(-NOTIONAL, FIXING_DATE, INDEX, STRIKE_HIGH, IS_CAP, CALENDAR);
  private static final CapFloorIborDefinition FLOOR_SHORT_DEFINITION =
      CapFloorIborDefinition.from(-NOTIONAL, FIXING_DATE, INDEX, STRIKE, !IS_CAP, CALENDAR);
  private static final CapFloorIborDefinition FLOOR_HIGH_SHORT_DEFINITION =
      CapFloorIborDefinition.from(-NOTIONAL, FIXING_DATE, INDEX, STRIKE_HIGH, !IS_CAP, CALENDAR);
  // Methods and calculator
  private static final double CUT_OFF_STRIKE = 0.08;
  private static final double MU = 2.50;
  private static final CapFloorIborSABRExtrapolationRightMethod METHOD =
      new CapFloorIborSABRExtrapolationRightMethod(CUT_OFF_STRIKE, MU);
  private static final ParRateCalculator PRC = ParRateCalculator.getInstance();
  private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance();
  private static final PresentValueCurveSensitivitySABRExtrapolationCalculator PVSC =
      new PresentValueCurveSensitivitySABRExtrapolationCalculator(CUT_OFF_STRIKE, MU);
  // To derivative
  private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2008, 8, 18);
  private static final String FUNDING_CURVE_NAME = "Funding";
  private static final String FORWARD_CURVE_NAME = "Forward";
  private static final String[] CURVES_NAME = {FUNDING_CURVE_NAME, FORWARD_CURVE_NAME};
  private static final CapFloorIbor CAP_LONG =
      (CapFloorIbor) CAP_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
  private static final CapFloorIbor CAP_HIGH_LONG =
      (CapFloorIbor) CAP_HIGH_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
  private static final CouponIbor COUPON_IBOR =
      (CouponIbor) COUPON_IBOR_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
  private static final CouponFixed COUPON_STRIKE =
      COUPON_STRIKE_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
  private static final CouponFixed COUPON_STRIKE_HIGH =
      COUPON_STRIKE_HIGH_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
  private static final CapFloorIbor CAP_SHORT =
      (CapFloorIbor) CAP_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
  private static final CapFloorIbor CAP_HIGH_SHORT =
      (CapFloorIbor) CAP_HIGH_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
  private static final CapFloorIbor FLOOR_SHORT =
      (CapFloorIbor) FLOOR_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
  private static final CapFloorIbor FLOOR_HIGH_SHORT =
      (CapFloorIbor) FLOOR_HIGH_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
  // Data
  private static final YieldCurveBundle CURVES = TestsDataSetsSABR.createCurves1();
  private static final SABRInterestRateParameters SABR_PARAMETERS = TestsDataSetsSABR.createSABR1();
  private static final SABRInterestRateDataBundle SABR_BUNDLE =
      new SABRInterestRateDataBundle(SABR_PARAMETERS, CURVES);

  @Test
  /** Test the present value using the method with the direct formula with extrapolation. */
  public void presentValueBelowCutOff() {
    final CurrencyAmount methodPrice = METHOD.presentValue(CAP_LONG, SABR_BUNDLE);
    final double df =
        CURVES.getCurve(FUNDING_CURVE_NAME).getDiscountFactor(CAP_LONG.getPaymentTime());
    final double forward = CAP_LONG.accept(PRC, CURVES);
    final double maturity = CAP_LONG.getFixingPeriodEndTime() - CAP_LONG.getFixingPeriodStartTime();
    final DoublesPair expiryMaturity = new DoublesPair(CAP_LONG.getFixingTime(), maturity);
    final double alpha = SABR_PARAMETERS.getAlpha(expiryMaturity);
    final double beta = SABR_PARAMETERS.getBeta(expiryMaturity);
    final double rho = SABR_PARAMETERS.getRho(expiryMaturity);
    final double nu = SABR_PARAMETERS.getNu(expiryMaturity);
    final SABRFormulaData sabrParam = new SABRFormulaData(alpha, beta, rho, nu);
    final SABRExtrapolationRightFunction sabrExtrapolation =
        new SABRExtrapolationRightFunction(
            forward, sabrParam, CUT_OFF_STRIKE, CAP_LONG.getFixingTime(), MU);
    final EuropeanVanillaOption option =
        new EuropeanVanillaOption(CAP_LONG.getStrike(), CAP_LONG.getFixingTime(), CAP_LONG.isCap());
    final double expectedPrice =
        sabrExtrapolation.price(option)
            * CAP_LONG.getNotional()
            * CAP_LONG.getPaymentYearFraction()
            * df;
    assertEquals(
        "Cap/floor: SABR with extrapolation pricing", expectedPrice, methodPrice.getAmount(), 1E-2);
  }

  @Test
  /** Test the present value using the method with the direct formula with extrapolation. */
  public void presentValueAboveCutOff() {
    CurrencyAmount methodPrice = METHOD.presentValue(CAP_HIGH_LONG, SABR_BUNDLE);
    final double df =
        CURVES.getCurve(FUNDING_CURVE_NAME).getDiscountFactor(CAP_HIGH_LONG.getPaymentTime());
    final double forward = CAP_HIGH_LONG.accept(PRC, CURVES);
    final double maturity =
        CAP_HIGH_LONG.getFixingPeriodEndTime() - CAP_LONG.getFixingPeriodStartTime();
    final DoublesPair expiryMaturity = new DoublesPair(CAP_HIGH_LONG.getFixingTime(), maturity);
    final double alpha = SABR_PARAMETERS.getAlpha(expiryMaturity);
    final double beta = SABR_PARAMETERS.getBeta(expiryMaturity);
    final double rho = SABR_PARAMETERS.getRho(expiryMaturity);
    final double nu = SABR_PARAMETERS.getNu(expiryMaturity);
    final SABRFormulaData sabrParam = new SABRFormulaData(alpha, beta, rho, nu);
    final SABRExtrapolationRightFunction sabrExtrapolation =
        new SABRExtrapolationRightFunction(
            forward, sabrParam, CUT_OFF_STRIKE, CAP_HIGH_LONG.getFixingTime(), MU);
    final EuropeanVanillaOption option =
        new EuropeanVanillaOption(
            CAP_HIGH_LONG.getStrike(), CAP_HIGH_LONG.getFixingTime(), CAP_HIGH_LONG.isCap());
    final double expectedPrice =
        sabrExtrapolation.price(option)
            * CAP_HIGH_LONG.getNotional()
            * CAP_HIGH_LONG.getPaymentYearFraction()
            * df;
    assertEquals(
        "Cap/floor: SABR with extrapolation pricing", expectedPrice, methodPrice.getAmount(), 1E-2);
    methodPrice = METHOD.presentValue(CAP_HIGH_LONG, SABR_BUNDLE);
    assertEquals(
        "Cap/floor: SABR with extrapolation pricing", expectedPrice, methodPrice.getAmount(), 1E-2);
  }

  @Test
  /** Test the present value using the method with the direct formula with extrapolation. */
  public void presentValueLongShortParityBelowCutOff() {
    final CurrencyAmount priceLong = METHOD.presentValue(CAP_LONG, SABR_BUNDLE);
    final CurrencyAmount priceShort = METHOD.presentValue(CAP_SHORT, SABR_BUNDLE);
    assertEquals(
        "Cap/floor: SABR with extrapolation pricing: long/short parity",
        priceLong.getAmount(),
        -priceShort.getAmount(),
        1E-2);
  }

  @Test
  /** Test the present value using the method with the direct formula with extrapolation. */
  public void presentValueLongShortParityAboveCutOff() {
    final CurrencyAmount priceLong = METHOD.presentValue(CAP_HIGH_LONG, SABR_BUNDLE);
    final CurrencyAmount priceShort = METHOD.presentValue(CAP_HIGH_SHORT, SABR_BUNDLE);
    assertEquals(
        "Cap/floor: SABR with extrapolation pricing: long/short parity",
        priceLong.getAmount(),
        -priceShort.getAmount(),
        1E-2);
  }

  @Test
  /** Test the cap/floor/forward parity below the cut-off strike. */
  public void presentValueCapFloorParityBelowCutOff() {
    final CurrencyAmount priceCap = METHOD.presentValue(CAP_LONG, SABR_BUNDLE);
    final CurrencyAmount priceFloor = METHOD.presentValue(FLOOR_SHORT, SABR_BUNDLE);
    final double priceCouponStrike = COUPON_STRIKE.accept(PVC, CURVES);
    final double priceCouponIbor = COUPON_IBOR.accept(PVC, CURVES);
    assertEquals(
        "Cap/floor: SABR with extrapolation pricing: cap/floor parity",
        priceCouponIbor - priceCouponStrike,
        priceCap.getAmount() + priceFloor.getAmount(),
        1E-2);
  }

  @Test
  /** Test the cap/floor/forward parity above the cut-off strike. */
  public void presentValueCapFloorParityAboveCutOff() {
    final CurrencyAmount priceCap = METHOD.presentValue(CAP_HIGH_LONG, SABR_BUNDLE);
    final CurrencyAmount priceFloor = METHOD.presentValue(FLOOR_HIGH_SHORT, SABR_BUNDLE);
    final double priceCouponStrike = COUPON_STRIKE_HIGH.accept(PVC, CURVES);
    final double priceCouponIbor = COUPON_IBOR.accept(PVC, CURVES);
    assertEquals(
        "Cap/floor: SABR with extrapolation pricing: cap/floor parity",
        priceCouponIbor - priceCouponStrike,
        priceCap.getAmount() + priceFloor.getAmount(),
        1E-2);
  }

  @Test
  /** Test the present value using the method with the direct formula with extrapolation. */
  public void presentValueMethodVsCalculator() {
    final SABRInterestRateDataBundle sabrExtraBundle =
        new SABRInterestRateDataBundle(SABR_PARAMETERS, CURVES);
    final CurrencyAmount pvMethod = METHOD.presentValue(CAP_LONG, SABR_BUNDLE);
    final PresentValueSABRExtrapolationCalculator pvc =
        new PresentValueSABRExtrapolationCalculator(CUT_OFF_STRIKE, MU);
    final double pvCalculator = CAP_LONG.accept(pvc, sabrExtraBundle);
    assertEquals(
        "Cap/floor: SABR with extrapolation pricing - Method vs Calculator",
        pvMethod.getAmount(),
        pvCalculator,
        1E-2);
  }

  @Test
  /**
   * Test the present value rate sensitivity against a finite difference computation; strike below
   * the cut-off strike. Test sensitivity long/short parity.
   */
  public void testPresentValueSensitivityBelowCutOff() {
    final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1();
    final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1();
    final SABRInterestRateDataBundle sabrBundle =
        new SABRInterestRateDataBundle(sabrParameter, curves);
    InterestRateCurveSensitivity pvsCapLong = METHOD.presentValueSensitivity(CAP_LONG, sabrBundle);
    final InterestRateCurveSensitivity pvsCapShort =
        METHOD.presentValueSensitivity(CAP_SHORT, sabrBundle);
    // Long/short parity
    final InterestRateCurveSensitivity pvsCapShort_1 = pvsCapShort.multipliedBy(-1);
    assertEquals(pvsCapLong.getSensitivities(), pvsCapShort_1.getSensitivities());
    // Present value sensitivity comparison with finite difference.
    final double deltaTolerancePrice = 1.0E-1;
    // Testing note: Sensitivity is for a movement of 1. 1E+2 = 1 cent for a 1 bp move.
    final double deltaShift = 1.0E-7;
    pvsCapLong = pvsCapLong.cleaned();
    final String bumpedCurveName = "Bumped Curve";
    // 1. Forward curve sensitivity
    final String[] CurveNameBumpedForward = {FUNDING_CURVE_NAME, bumpedCurveName};
    final CapFloorIbor capBumpedForward =
        (CapFloorIbor) CAP_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CurveNameBumpedForward);
    final double[] nodeTimesForward =
        new double[] {
          capBumpedForward.getFixingPeriodStartTime(), capBumpedForward.getFixingPeriodEndTime()
        };
    final double[] sensiForwardMethod =
        SensitivityFiniteDifference.curveSensitivity(
            capBumpedForward,
            SABR_BUNDLE,
            FORWARD_CURVE_NAME,
            bumpedCurveName,
            nodeTimesForward,
            deltaShift,
            METHOD);
    assertEquals(
        "Sensitivity finite difference method: number of node", 2, sensiForwardMethod.length);
    final List<DoublesPair> sensiPvForward = pvsCapLong.getSensitivities().get(FORWARD_CURVE_NAME);
    for (int loopnode = 0; loopnode < sensiForwardMethod.length; loopnode++) {
      final DoublesPair pairPv = sensiPvForward.get(loopnode);
      assertEquals(
          "Sensitivity cap/floor pv to forward curve: Node " + loopnode,
          nodeTimesForward[loopnode],
          pairPv.getFirst(),
          1E-8);
      assertEquals(
          "Sensitivity finite difference method: node sensitivity",
          pairPv.second,
          sensiForwardMethod[loopnode],
          deltaTolerancePrice);
    }
    // 2. Discounting curve sensitivity
    final String[] CurveNameBumpedDisc = {bumpedCurveName, FORWARD_CURVE_NAME};
    final CapFloorIbor capBumpedDisc =
        (CapFloorIbor) CAP_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CurveNameBumpedDisc);
    final double[] nodeTimesDisc = new double[] {capBumpedDisc.getPaymentTime()};
    final double[] sensiDiscMethod =
        SensitivityFiniteDifference.curveSensitivity(
            capBumpedDisc,
            SABR_BUNDLE,
            FUNDING_CURVE_NAME,
            bumpedCurveName,
            nodeTimesDisc,
            deltaShift,
            METHOD);
    assertEquals("Sensitivity finite difference method: number of node", 1, sensiDiscMethod.length);
    final List<DoublesPair> sensiPvDisc = pvsCapLong.getSensitivities().get(FUNDING_CURVE_NAME);
    for (int loopnode = 0; loopnode < sensiDiscMethod.length; loopnode++) {
      final DoublesPair pairPv = sensiPvDisc.get(loopnode);
      assertEquals(
          "Sensitivity cap/floor pv to forward curve: Node " + loopnode,
          nodeTimesDisc[loopnode],
          pairPv.getFirst(),
          1E-8);
      assertEquals(
          "Sensitivity finite difference method: node sensitivity",
          pairPv.second,
          sensiDiscMethod[loopnode],
          deltaTolerancePrice);
    }
  }

  @Test
  /**
   * Test the present value rate sensitivity against a finite difference computation; strike above
   * the cut-off strike. Test sensitivity long/short parity.
   */
  public void testPresentValueSensitivityAboveCutOff() {
    final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1();
    final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1();
    final SABRInterestRateDataBundle sabrBundle =
        new SABRInterestRateDataBundle(sabrParameter, curves);
    InterestRateCurveSensitivity pvsCapLong =
        METHOD.presentValueSensitivity(CAP_HIGH_LONG, sabrBundle);
    final InterestRateCurveSensitivity pvsCapShort =
        METHOD.presentValueSensitivity(CAP_HIGH_SHORT, sabrBundle);
    // Long/short parity
    final InterestRateCurveSensitivity pvsCapShort_1 = pvsCapShort.multipliedBy(-1);
    assertEquals(pvsCapLong.getSensitivities(), pvsCapShort_1.getSensitivities());
    // Present value sensitivity comparison with finite difference.
    final double deltaTolerancePrice = 1.0E-1;
    // Testing note: Sensitivity is for a movement of 1. 1E+2 = 1 cent for a 1 bp move.
    final double deltaShift = 1.0E-7;
    pvsCapLong = pvsCapLong.cleaned();
    final String bumpedCurveName = "Bumped Curve";
    // 1. Forward curve sensitivity
    final String[] CurveNameBumpedForward = {FUNDING_CURVE_NAME, bumpedCurveName};
    final CapFloorIbor capBumpedForward =
        (CapFloorIbor)
            CAP_HIGH_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CurveNameBumpedForward);
    final double[] nodeTimesForward =
        new double[] {
          capBumpedForward.getFixingPeriodStartTime(), capBumpedForward.getFixingPeriodEndTime()
        };
    final double[] sensiForwardMethod =
        SensitivityFiniteDifference.curveSensitivity(
            capBumpedForward,
            SABR_BUNDLE,
            FORWARD_CURVE_NAME,
            bumpedCurveName,
            nodeTimesForward,
            deltaShift,
            METHOD);
    assertEquals(
        "Sensitivity finite difference method: number of node", 2, sensiForwardMethod.length);
    final List<DoublesPair> sensiPvForward = pvsCapLong.getSensitivities().get(FORWARD_CURVE_NAME);
    for (int loopnode = 0; loopnode < sensiForwardMethod.length; loopnode++) {
      final DoublesPair pairPv = sensiPvForward.get(loopnode);
      assertEquals(
          "Sensitivity cap/floor pv to forward curve: Node " + loopnode,
          nodeTimesForward[loopnode],
          pairPv.getFirst(),
          1E-8);
      //      assertEquals("Sensitivity finite difference method: node sensitivity: Node " +
      // loopnode, pairPv.second, sensiForwardMethod[loopnode], deltaTolerancePrice);
    }
    // 2. Discounting curve sensitivity
    final String[] CurveNameBumpedDisc = {bumpedCurveName, FORWARD_CURVE_NAME};
    final CapFloorIbor capBumpedDisc =
        (CapFloorIbor) CAP_HIGH_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CurveNameBumpedDisc);
    final double[] nodeTimesDisc = new double[] {capBumpedDisc.getPaymentTime()};
    final double[] sensiDiscMethod =
        SensitivityFiniteDifference.curveSensitivity(
            capBumpedDisc,
            SABR_BUNDLE,
            FUNDING_CURVE_NAME,
            bumpedCurveName,
            nodeTimesDisc,
            deltaShift,
            METHOD);
    assertEquals("Sensitivity finite difference method: number of node", 1, sensiDiscMethod.length);
    final List<DoublesPair> sensiPvDisc = pvsCapLong.getSensitivities().get(FUNDING_CURVE_NAME);
    for (int loopnode = 0; loopnode < sensiDiscMethod.length; loopnode++) {
      final DoublesPair pairPv = sensiPvDisc.get(loopnode);
      assertEquals(
          "Sensitivity cap/floor pv to forward curve: Node " + loopnode,
          nodeTimesDisc[loopnode],
          pairPv.getFirst(),
          1E-8);
      assertEquals(
          "Sensitivity finite difference method: node sensitivity",
          pairPv.second,
          sensiDiscMethod[loopnode],
          deltaTolerancePrice);
    }
  }

  @Test
  /** Test the present value using the method with the direct formula with extrapolation. */
  public void presentValueCurveSensitivityMethodVsCalculator() {
    final SABRInterestRateDataBundle sabrExtraBundle =
        new SABRInterestRateDataBundle(SABR_PARAMETERS, CURVES);
    final InterestRateCurveSensitivity pvsMethod =
        METHOD.presentValueSensitivity(CAP_HIGH_LONG, SABR_BUNDLE);
    final InterestRateCurveSensitivity pvsCalculator =
        new InterestRateCurveSensitivity(CAP_HIGH_LONG.accept(PVSC, sabrExtraBundle));
    assertEquals(
        "Cap/floor: SABR with extrapolation pv curve sensitivity - Method vs Calculator",
        pvsMethod,
        pvsCalculator);
  }

  @Test
  /**
   * Test the present value SABR parameters sensitivity against a finite difference computation;
   * strike below the cut-off strike.
   */
  public void testPresentValueSABRSensitivityBelowCutOff() {
    final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1();
    final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1();
    final SABRInterestRateDataBundle sabrBundle =
        new SABRInterestRateDataBundle(sabrParameter, curves);
    final CurrencyAmount pv = METHOD.presentValue(CAP_LONG, sabrBundle);
    final PresentValueSABRSensitivityDataBundle pvsCapLong =
        METHOD.presentValueSABRSensitivity(CAP_LONG, sabrBundle);
    PresentValueSABRSensitivityDataBundle pvsCapShort =
        METHOD.presentValueSABRSensitivity(CAP_SHORT, sabrBundle);
    // Long/short parity
    pvsCapShort = pvsCapShort.multiplyBy(-1.0);
    assertEquals(pvsCapShort.getAlpha(), pvsCapLong.getAlpha());
    // SABR sensitivity vs finite difference
    final double shift = 0.0001;
    final double shiftAlpha = 0.00001;
    final DoublesPair expectedExpiryTenor =
        new DoublesPair(
            CAP_LONG.getFixingTime(),
            CAP_LONG.getFixingPeriodEndTime() - CAP_LONG.getFixingPeriodStartTime());
    // Alpha sensitivity vs finite difference computation
    final SABRInterestRateParameters sabrParameterAlphaBumped =
        TestsDataSetsSABR.createSABR1AlphaBumped(shiftAlpha);
    final SABRInterestRateDataBundle sabrBundleAlphaBumped =
        new SABRInterestRateDataBundle(sabrParameterAlphaBumped, curves);
    final CurrencyAmount pvLongPayerAlphaBumped =
        METHOD.presentValue(CAP_LONG, sabrBundleAlphaBumped);
    final double expectedAlphaSensi =
        (pvLongPayerAlphaBumped.getAmount() - pv.getAmount()) / shiftAlpha;
    assertEquals("Number of alpha sensitivity", pvsCapLong.getAlpha().getMap().keySet().size(), 1);
    assertEquals(
        "Alpha sensitivity expiry/tenor",
        pvsCapLong.getAlpha().getMap().keySet().contains(expectedExpiryTenor),
        true);
    assertEquals(
        "Alpha sensitivity value",
        expectedAlphaSensi,
        pvsCapLong.getAlpha().getMap().get(expectedExpiryTenor),
        2.0E-1);
    // Rho sensitivity vs finite difference computation
    final SABRInterestRateParameters sabrParameterRhoBumped =
        TestsDataSetsSABR.createSABR1RhoBumped();
    final SABRInterestRateDataBundle sabrBundleRhoBumped =
        new SABRInterestRateDataBundle(sabrParameterRhoBumped, curves);
    final CurrencyAmount pvLongPayerRhoBumped = METHOD.presentValue(CAP_LONG, sabrBundleRhoBumped);
    final double expectedRhoSensi = (pvLongPayerRhoBumped.getAmount() - pv.getAmount()) / shift;
    assertEquals("Number of rho sensitivity", pvsCapLong.getRho().getMap().keySet().size(), 1);
    assertEquals(
        "Rho sensitivity expiry/tenor",
        pvsCapLong.getRho().getMap().keySet().contains(expectedExpiryTenor),
        true);
    assertEquals(
        "Rho sensitivity value",
        pvsCapLong.getRho().getMap().get(expectedExpiryTenor),
        expectedRhoSensi,
        1.0E-2);
    // Alpha sensitivity vs finite difference computation
    final SABRInterestRateParameters sabrParameterNuBumped =
        TestsDataSetsSABR.createSABR1NuBumped();
    final SABRInterestRateDataBundle sabrBundleNuBumped =
        new SABRInterestRateDataBundle(sabrParameterNuBumped, curves);
    final CurrencyAmount pvLongPayerNuBumped = METHOD.presentValue(CAP_LONG, sabrBundleNuBumped);
    final double expectedNuSensi = (pvLongPayerNuBumped.getAmount() - pv.getAmount()) / shift;
    assertEquals("Number of nu sensitivity", pvsCapLong.getNu().getMap().keySet().size(), 1);
    assertEquals(
        "Nu sensitivity expiry/tenor",
        pvsCapLong.getNu().getMap().keySet().contains(expectedExpiryTenor),
        true);
    assertEquals(
        "Nu sensitivity value",
        pvsCapLong.getNu().getMap().get(expectedExpiryTenor),
        expectedNuSensi,
        3.0E-2);
  }

  @Test
  /**
   * Test the present value SABR parameters sensitivity against a finite difference computation;
   * strike above the cut-off strike.
   */
  public void testPresentValueSABRSensitivityAboveCutOff() {
    final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1();
    final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1();
    final SABRInterestRateDataBundle sabrBundle =
        new SABRInterestRateDataBundle(sabrParameter, curves);
    final CurrencyAmount pv = METHOD.presentValue(CAP_HIGH_LONG, sabrBundle);
    final PresentValueSABRSensitivityDataBundle pvsCapLong =
        METHOD.presentValueSABRSensitivity(CAP_HIGH_LONG, sabrBundle);
    PresentValueSABRSensitivityDataBundle pvsCapShort =
        METHOD.presentValueSABRSensitivity(CAP_HIGH_SHORT, sabrBundle);
    // Long/short parity
    pvsCapShort = pvsCapShort.multiplyBy(-1.0);
    assertEquals(pvsCapShort.getAlpha(), pvsCapLong.getAlpha());
    // SABR sensitivity vs finite difference
    final double shift = 0.0001;
    final double shiftAlpha = 0.00001;
    final DoublesPair expectedExpiryTenor =
        new DoublesPair(
            CAP_HIGH_LONG.getFixingTime(),
            CAP_HIGH_LONG.getFixingPeriodEndTime() - CAP_HIGH_LONG.getFixingPeriodStartTime());
    // Alpha sensitivity vs finite difference computation
    final SABRInterestRateParameters sabrParameterAlphaBumped =
        TestsDataSetsSABR.createSABR1AlphaBumped(shiftAlpha);
    final SABRInterestRateDataBundle sabrBundleAlphaBumped =
        new SABRInterestRateDataBundle(sabrParameterAlphaBumped, curves);
    final CurrencyAmount pvLongPayerAlphaBumped =
        METHOD.presentValue(CAP_HIGH_LONG, sabrBundleAlphaBumped);
    final double expectedAlphaSensi =
        (pvLongPayerAlphaBumped.getAmount() - pv.getAmount()) / shiftAlpha;
    assertEquals("Number of alpha sensitivity", pvsCapLong.getAlpha().getMap().keySet().size(), 1);
    assertEquals(
        "Alpha sensitivity expiry/tenor",
        pvsCapLong.getAlpha().getMap().keySet().contains(expectedExpiryTenor),
        true);
    assertEquals(
        "Alpha sensitivity value",
        expectedAlphaSensi,
        pvsCapLong.getAlpha().getMap().get(expectedExpiryTenor),
        1.0E-0);
    // Rho sensitivity vs finite difference computation
    final SABRInterestRateParameters sabrParameterRhoBumped =
        TestsDataSetsSABR.createSABR1RhoBumped();
    final SABRInterestRateDataBundle sabrBundleRhoBumped =
        new SABRInterestRateDataBundle(sabrParameterRhoBumped, curves);
    final CurrencyAmount pvLongPayerRhoBumped =
        METHOD.presentValue(CAP_HIGH_LONG, sabrBundleRhoBumped);
    final double expectedRhoSensi = (pvLongPayerRhoBumped.getAmount() - pv.getAmount()) / shift;
    assertEquals("Number of rho sensitivity", pvsCapLong.getRho().getMap().keySet().size(), 1);
    assertEquals(
        "Rho sensitivity expiry/tenor",
        pvsCapLong.getRho().getMap().keySet().contains(expectedExpiryTenor),
        true);
    assertEquals(
        "Rho sensitivity value",
        pvsCapLong.getRho().getMap().get(expectedExpiryTenor),
        expectedRhoSensi,
        1.0E-1);
    // Alpha sensitivity vs finite difference computation
    final SABRInterestRateParameters sabrParameterNuBumped =
        TestsDataSetsSABR.createSABR1NuBumped();
    final SABRInterestRateDataBundle sabrBundleNuBumped =
        new SABRInterestRateDataBundle(sabrParameterNuBumped, curves);
    final CurrencyAmount pvLongPayerNuBumped =
        METHOD.presentValue(CAP_HIGH_LONG, sabrBundleNuBumped);
    final double expectedNuSensi = (pvLongPayerNuBumped.getAmount() - pv.getAmount()) / shift;
    assertEquals("Number of nu sensitivity", pvsCapLong.getNu().getMap().keySet().size(), 1);
    assertEquals(
        "Nu sensitivity expiry/tenor",
        pvsCapLong.getNu().getMap().keySet().contains(expectedExpiryTenor),
        true);
    assertEquals(
        "Nu sensitivity value",
        pvsCapLong.getNu().getMap().get(expectedExpiryTenor),
        expectedNuSensi,
        2.0E-1);
  }
}
/**
 * Tests related to the pricing of bills security by discounting.
 *
 * @deprecated This class tests deprecated functionality
 */
@Deprecated
public class BillSecurityDiscountingMethodTest {

  private static final Currency EUR = Currency.EUR;
  private static final Calendar CALENDAR = new MondayToFridayCalendar("TARGET");
  private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2012, 1, 17);

  private static final DayCount ACT360 = DayCountFactory.INSTANCE.getDayCount("Actual/360");
  private static final int SETTLEMENT_DAYS = 2;
  private static final YieldConvention YIELD_IAM =
      YieldConventionFactory.INSTANCE.getYieldConvention("INTEREST@MTY");
  private static final YieldConvention YIELD_DSC =
      YieldConventionFactory.INSTANCE.getYieldConvention("DISCOUNT");

  // ISIN: BE0312677462
  private static final String ISSUER_BEL = "BELGIUM GOVT";
  private static final String ISSUER_US = "US GOVT";
  private static final ZonedDateTime END_DATE = DateUtils.getUTCDate(2012, 3, 15);
  private static final double NOTIONAL = 1000;
  private static final double YIELD = 0.00185; // External source
  private static final double PRICE = 0.99971; // External source

  private static final ZonedDateTime SETTLE_DATE =
      ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, SETTLEMENT_DAYS, CALENDAR);
  private static final String[] NAME_CURVES = TestsDataSetsSABR.nameCurvesBond3();
  private static final BillSecurityDefinition BILL_IAM_SEC_DEFINITION =
      new BillSecurityDefinition(
          EUR, END_DATE, NOTIONAL, SETTLEMENT_DAYS, CALENDAR, YIELD_IAM, ACT360, ISSUER_BEL);
  private static final BillSecurityDefinition BILL_DSC_SEC_DEFINITION =
      new BillSecurityDefinition(
          EUR, END_DATE, NOTIONAL, SETTLEMENT_DAYS, CALENDAR, YIELD_DSC, ACT360, ISSUER_US);
  // Should not be in EUR, but this is only a test
  private static final BillSecurity BILL_IAM_SEC =
      BILL_IAM_SEC_DEFINITION.toDerivative(REFERENCE_DATE, SETTLE_DATE, NAME_CURVES);
  private static final BillSecurity BILL_DSC_SEC =
      BILL_DSC_SEC_DEFINITION.toDerivative(REFERENCE_DATE, SETTLE_DATE, NAME_CURVES);
  private static final YieldCurveBundle CURVE_BUNDLE = TestsDataSetsSABR.createCurvesBond3();

  private static final BillSecurityDiscountingMethod METHOD_SECURITY =
      BillSecurityDiscountingMethod.getInstance();
  private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance();
  private static final PresentValueCurveSensitivityCalculator PVCSC =
      PresentValueCurveSensitivityCalculator.getInstance();
  private static final YieldFromCurvesCalculator YFCC = YieldFromCurvesCalculator.getInstance();
  private static final YieldFromPriceCalculator YFPC = YieldFromPriceCalculator.getInstance();

  private static final double TOLERANCE_PV = 1.0E-2;
  private static final double TOLERANCE_PRICE = 1.0E-8;
  private static final double TOLERANCE_PRICE_EXTERNAL = 1.0E-5;
  private static final double TOLERANCE_YIELD = 1.0E-8;
  private static final double TOLERANCE_YIELD_EXTERNAL = 1.0E-4;
  private static final double TOLERANCE_YIELD_DERIVATIVE = 1.0E-6;

  @Test
  /** Tests the present value against explicit computation. */
  public void presentValue() {
    final CurrencyAmount pvComputed = METHOD_SECURITY.presentValue(BILL_IAM_SEC, CURVE_BUNDLE);
    final double pvExpected =
        NOTIONAL
            * CURVE_BUNDLE.getCurve(NAME_CURVES[1]).getDiscountFactor(BILL_IAM_SEC.getEndTime());
    assertEquals(
        "Bill Security: discounting method - present value",
        pvExpected,
        pvComputed.getAmount(),
        TOLERANCE_PV);
  }

  @Test
  /** Tests the present value: Method vs Calculator */
  public void presentValueMethodVsCalculator() {
    final CurrencyAmount pvMethod = METHOD_SECURITY.presentValue(BILL_IAM_SEC, CURVE_BUNDLE);
    final double pvCalculator = BILL_IAM_SEC.accept(PVC, CURVE_BUNDLE);
    assertEquals(
        "Bill Security: discounting method - present value",
        pvMethod.getAmount(),
        pvCalculator,
        TOLERANCE_PV);
  }

  @Test
  public void priceFromYield() {
    final double[] yields = new double[] {0.0010, 0.0, -0.0010};
    for (final double yield2 : yields) {
      final double priceComputed = METHOD_SECURITY.priceFromYield(BILL_IAM_SEC, yield2);
      final double priceExpected = 1.0 / (1 + BILL_IAM_SEC.getAccrualFactor() * yield2);
      assertEquals(
          "Bill Security: discounting method - price",
          priceExpected,
          priceComputed,
          TOLERANCE_PRICE);
    }
  }

  @Test
  public void priceFromYieldExternal() {
    final double priceComputed = METHOD_SECURITY.priceFromYield(BILL_IAM_SEC, YIELD);
    assertEquals(
        "Bill Security: discounting method - price",
        PRICE,
        priceComputed,
        TOLERANCE_PRICE_EXTERNAL);
  }

  @Test
  public void yieldFromPrice() {
    final double yieldComputedIAM = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE);
    final double yieldExpectedIAM = (1.0 / PRICE - 1.0) / BILL_IAM_SEC.getAccrualFactor();
    assertEquals(
        "Bill Security: discounting method - yield",
        yieldExpectedIAM,
        yieldComputedIAM,
        TOLERANCE_YIELD);
    final double yieldComputedDSC = METHOD_SECURITY.yieldFromPrice(BILL_DSC_SEC, PRICE);
    final double yieldExpectedDSC = (1.0 - PRICE) / BILL_DSC_SEC.getAccrualFactor();
    assertEquals(
        "Bill Security: discounting method - yield",
        yieldExpectedDSC,
        yieldComputedDSC,
        TOLERANCE_YIELD);
  }

  @Test
  public void yieldFromPriceDerivative() {
    final double shift = 1.0E-8;
    final double yieldIAM = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE);
    final double yieldPIAM = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE + shift);
    final double yieldDerivativeExpectedIAM = (yieldPIAM - yieldIAM) / shift;
    final double yieldDerivativeComputedIAM =
        METHOD_SECURITY.yieldFromPriceDerivative(BILL_IAM_SEC, PRICE);
    assertEquals(
        "Bill Security: discounting method - yield",
        yieldDerivativeExpectedIAM,
        yieldDerivativeComputedIAM,
        TOLERANCE_YIELD_DERIVATIVE);
    final double yieldDSC = METHOD_SECURITY.yieldFromPrice(BILL_DSC_SEC, PRICE);
    final double yieldPDSC = METHOD_SECURITY.yieldFromPrice(BILL_DSC_SEC, PRICE + shift);
    final double yieldDerivativeExpectedDSC = (yieldPDSC - yieldDSC) / shift;
    final double yieldDerivativeComputedDSC =
        METHOD_SECURITY.yieldFromPriceDerivative(BILL_DSC_SEC, PRICE);
    assertEquals(
        "Bill Security: discounting method - yield",
        yieldDerivativeExpectedDSC,
        yieldDerivativeComputedDSC,
        TOLERANCE_YIELD_DERIVATIVE);
  }

  @Test
  public void yieldFromPriceExternal() {
    final double yieldComputed = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE);
    assertEquals(
        "Bill Security: discounting method - yield",
        YIELD,
        yieldComputed,
        TOLERANCE_YIELD_EXTERNAL);
  }

  @Test
  public void yieldFromPriceCoherence() {
    final double priceComputed = METHOD_SECURITY.priceFromYield(BILL_IAM_SEC, YIELD);
    final double yieldComputed = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, priceComputed);
    assertEquals(
        "Bill Security: discounting method - yield", YIELD, yieldComputed, TOLERANCE_YIELD);
  }

  @Test
  public void priceFromYieldCoherence() {
    final double yieldComputed = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE);
    final double priceComputed = METHOD_SECURITY.priceFromYield(BILL_IAM_SEC, yieldComputed);
    assertEquals(
        "Bill Security: discounting method - price", PRICE, priceComputed, TOLERANCE_PRICE);
  }

  @Test
  public void presentValueFromPrice() {
    final CurrencyAmount pvComputed =
        METHOD_SECURITY.presentValueFromPrice(BILL_IAM_SEC, PRICE, CURVE_BUNDLE);
    final double pvExpected =
        NOTIONAL
            * PRICE
            * CURVE_BUNDLE
                .getCurve(NAME_CURVES[0])
                .getDiscountFactor(BILL_IAM_SEC.getSettlementTime());
    assertEquals(
        "Bill Security: discounting method - present value",
        pvExpected,
        pvComputed.getAmount(),
        TOLERANCE_PV);
  }

  @Test
  public void presentValueFromYield() {
    final CurrencyAmount pvComputed =
        METHOD_SECURITY.presentValueFromYield(BILL_IAM_SEC, YIELD, CURVE_BUNDLE);
    final double price = METHOD_SECURITY.priceFromYield(BILL_IAM_SEC, YIELD);
    final double pvExpected =
        NOTIONAL
            * price
            * CURVE_BUNDLE
                .getCurve(NAME_CURVES[0])
                .getDiscountFactor(BILL_IAM_SEC.getSettlementTime());
    assertEquals(
        "Bill Security: discounting method - present value",
        pvExpected,
        pvComputed.getAmount(),
        TOLERANCE_PV);
  }

  @Test
  public void priceFromCurves() {
    final double priceComputed = METHOD_SECURITY.priceFromCurves(BILL_IAM_SEC, CURVE_BUNDLE);
    final CurrencyAmount pvComputed = METHOD_SECURITY.presentValue(BILL_IAM_SEC, CURVE_BUNDLE);
    final double priceExpected =
        pvComputed.getAmount()
            / (NOTIONAL
                * CURVE_BUNDLE
                    .getCurve(NAME_CURVES[0])
                    .getDiscountFactor(BILL_IAM_SEC.getSettlementTime()));
    assertEquals(
        "Bill Security: discounting method - price", priceExpected, priceComputed, TOLERANCE_PRICE);
  }

  @Test
  public void yieldFromCurves() {
    final double yieldComputed = METHOD_SECURITY.yieldFromCurves(BILL_IAM_SEC, CURVE_BUNDLE);
    final double priceComputed = METHOD_SECURITY.priceFromCurves(BILL_IAM_SEC, CURVE_BUNDLE);
    final double yieldExpected = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, priceComputed);
    assertEquals(
        "Bill Security: discounting method - yield", yieldExpected, yieldComputed, TOLERANCE_YIELD);
  }

  @Test
  public void presentValueCurveSensitivity() {
    final InterestRateCurveSensitivity pvcsComputed =
        METHOD_SECURITY.presentValueCurveSensitivity(BILL_IAM_SEC, CURVE_BUNDLE);
    assertEquals(
        "Bill Security: present value curve sensitivity",
        1,
        pvcsComputed.getSensitivities().size());
    assertEquals(
        "Bill Security: present value curve sensitivity",
        1,
        pvcsComputed.getSensitivities().get(NAME_CURVES[1]).size());
    final double deltaTolerancePrice = 1.0E+2;
    // Testing note: Sensitivity is for a movement of 1. 1E+2 = 0.01 unit for a 1 bp move.
    final double deltaShift = 1.0E-6;
    // Credit curve sensitivity
    final String bumpedCurveName = "Bumped Curve";
    final BillSecurity billBumped =
        BILL_IAM_SEC_DEFINITION.toDerivative(REFERENCE_DATE, NAME_CURVES[0], bumpedCurveName);
    final double[] nodeTimes = new double[] {billBumped.getEndTime()};
    final double[] sensi =
        SensitivityFiniteDifference.curveSensitivity(
            billBumped,
            CURVE_BUNDLE,
            NAME_CURVES[1],
            bumpedCurveName,
            nodeTimes,
            deltaShift,
            METHOD_SECURITY);
    final List<DoublesPair> sensiPv = pvcsComputed.getSensitivities().get(NAME_CURVES[1]);
    for (int loopnode = 0; loopnode < sensi.length; loopnode++) {
      final DoublesPair pairPv = sensiPv.get(loopnode);
      assertEquals(
          "Bill Security: curve sensitivity - Node " + loopnode,
          nodeTimes[loopnode],
          pairPv.getFirst(),
          1E-8);
      AssertJUnit.assertEquals(
          "Bill Security: curve sensitivity", pairPv.second, sensi[loopnode], deltaTolerancePrice);
    }
  }

  @Test
  public void presentValueCurveSensitivityMethodVsCalculator() {
    final InterestRateCurveSensitivity pvcsMethod =
        METHOD_SECURITY.presentValueCurveSensitivity(BILL_IAM_SEC, CURVE_BUNDLE);
    final InterestRateCurveSensitivity pvcsCalculator =
        new InterestRateCurveSensitivity(BILL_IAM_SEC.accept(PVCSC, CURVE_BUNDLE));
    AssertSensivityObjects.assertEquals(
        "Bill Security: discounting method - curve sensitivity",
        pvcsMethod,
        pvcsCalculator,
        TOLERANCE_PV);
  }

  @Test
  public void methodVsCalculator() {
    double yield1 = METHOD_SECURITY.yieldFromCurves(BILL_IAM_SEC, CURVE_BUNDLE);
    double yield2 = BILL_IAM_SEC.accept(YFCC, CURVE_BUNDLE);
    assertEquals(
        "Bill Security: discounting method - yield from curves", yield1, yield2, TOLERANCE_YIELD);
    yield1 = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE);
    yield2 = BILL_IAM_SEC.accept(YFPC, PRICE);
    assertEquals(
        "Bill Security: discounting method - yield from price", yield1, yield2, TOLERANCE_YIELD);
  }
}
/**
 * Tests related to the pricing method for digital Forex option transactions with Black function and
 * a volatility provider.
 */
public class ForexOptionDigitalCallSpreadMethodTest {
  // General
  private static final Calendar CALENDAR = new MondayToFridayCalendar("A");
  private static final BusinessDayConvention BUSINESS_DAY =
      BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("Modified Following");
  private static final int SETTLEMENT_DAYS = 2;
  // Smile data
  private static final Currency EUR = Currency.EUR;
  private static final Currency USD = Currency.USD;
  private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2011, 6, 13);
  private static final FXMatrix FX_MATRIX = TestsDataSetsForex.fxMatrix();
  private static final double SPOT = FX_MATRIX.getFxRate(EUR, USD);
  private static final SmileDeltaTermStructureParameter SMILE_TERM =
      TestsDataSetsForex.smile(REFERENCE_DATE);
  private static final SmileDeltaTermStructureParameter SMILE_TERM_FLAT =
      TestsDataSetsForex.smileFlat(REFERENCE_DATE);
  // Methods and curves
  private static final YieldCurveBundle CURVES = TestsDataSetsForex.createCurvesForex();
  private static final String[] CURVES_NAME = TestsDataSetsForex.curveNames();
  private static final Map<String, Currency> CURVE_CURRENCY = TestsDataSetsForex.curveCurrency();
  private static final SmileDeltaTermStructureDataBundle SMILE_BUNDLE =
      new SmileDeltaTermStructureDataBundle(
          FX_MATRIX, CURVE_CURRENCY, CURVES, SMILE_TERM, Pair.of(EUR, USD));
  private static final SmileDeltaTermStructureDataBundle SMILE_BUNDLE_FLAT =
      new SmileDeltaTermStructureDataBundle(
          FX_MATRIX, CURVE_CURRENCY, CURVES, SMILE_TERM_FLAT, Pair.of(EUR, USD));
  private static final ForexOptionVanillaBlackMethod METHOD_VANILLA_BLACK =
      ForexOptionVanillaBlackMethod.getInstance();
  private static final ForexOptionDigitalBlackMethod METHOD_DIGITAL_BLACK =
      ForexOptionDigitalBlackMethod.getInstance();
  private static final double STANDARD_SPREAD = 0.0001;
  private static final ForexOptionDigitalCallSpreadBlackMethod METHOD_DIGITAL_SPREAD =
      new ForexOptionDigitalCallSpreadBlackMethod(STANDARD_SPREAD);
  private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance();
  // option
  private static final double STRIKE = 1.45;
  private static final boolean IS_CALL = true;
  private static final boolean IS_LONG = true;
  private static final double NOTIONAL = 100000000;
  private static final ZonedDateTime OPTION_PAY_DATE =
      ScheduleCalculator.getAdjustedDate(
          REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR);
  private static final ZonedDateTime OPTION_EXP_DATE =
      ScheduleCalculator.getAdjustedDate(OPTION_PAY_DATE, -SETTLEMENT_DAYS, CALENDAR);
  private static final ForexDefinition FOREX_DEFINITION =
      new ForexDefinition(EUR, USD, OPTION_PAY_DATE, NOTIONAL, STRIKE);
  private static final Forex FOREX = FOREX_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
  private static final ForexOptionDigitalDefinition FOREX_OPTION_CALL_DEFINITION =
      new ForexOptionDigitalDefinition(FOREX_DEFINITION, OPTION_EXP_DATE, IS_CALL, IS_LONG);
  private static final ForexOptionDigital FOREX_CALL_OPTION =
      FOREX_OPTION_CALL_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME);
  private static final double TOLERANCE_PRICE = 1.0E-2;
  private static final double TOLERANCE_PRICE_FLAT =
      1.0E+1; // The spread size will create a discrepancy.
  private static final double TOLERANCE_CE_FLAT =
      1.0E+2; // The spread size will create a discrepancy.
  private static final double TOLERANCE_DELTA = 1.0E+2; // 0.01 currency unit for 1 bp

  @Test
  /** Tests the present value in a flat smile case. */
  public void presentValueFlat() {
    MultipleCurrencyAmount pvSpread =
        METHOD_DIGITAL_SPREAD.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE_FLAT);
    MultipleCurrencyAmount pvBlack =
        METHOD_DIGITAL_BLACK.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE_FLAT);
    assertEquals(
        "Forex Digital option: call spread method - present value",
        pvBlack.getAmount(USD),
        pvSpread.getAmount(USD),
        TOLERANCE_PRICE_FLAT);
  }

  @Test
  /** Tests the present value with an explicit computation. */
  public void presentValue() {
    double strikeM = STRIKE * (1 - STANDARD_SPREAD);
    double strikeP = STRIKE * (1 + STANDARD_SPREAD);
    Forex forexM =
        new Forex(
            FOREX.getPaymentCurrency1().withAmount(1.0),
            FOREX.getPaymentCurrency2().withAmount(-strikeM));
    Forex forexP =
        new Forex(
            FOREX.getPaymentCurrency1().withAmount(1.0),
            FOREX.getPaymentCurrency2().withAmount(-strikeP));
    ForexOptionVanilla vanillaM =
        new ForexOptionVanilla(forexM, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG);
    ForexOptionVanilla vanillaP =
        new ForexOptionVanilla(forexP, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG);
    MultipleCurrencyAmount pvP = METHOD_VANILLA_BLACK.presentValue(vanillaP, SMILE_BUNDLE);
    MultipleCurrencyAmount pvM = METHOD_VANILLA_BLACK.presentValue(vanillaM, SMILE_BUNDLE);
    MultipleCurrencyAmount pvExpected =
        pvM.plus(pvP.multipliedBy(-1.0))
            .multipliedBy(
                1.0 / (strikeP - strikeM) * Math.abs(FOREX.getPaymentCurrency2().getAmount()));
    MultipleCurrencyAmount pvComputed =
        METHOD_DIGITAL_SPREAD.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertEquals(
        "Forex Digital option: call spread method - present value",
        pvExpected.getAmount(USD),
        pvComputed.getAmount(USD),
        TOLERANCE_PRICE);
  }

  @Test
  /** Tests put call parity. */
  public void presentValuePutCallParity() {
    final double strike = 1.45;
    final boolean isCall = true;
    final boolean isLong = true;
    final double notional = 100000000;
    final ZonedDateTime payDate =
        ScheduleCalculator.getAdjustedDate(
            REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR);
    final ZonedDateTime expDate =
        ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR);
    final ForexDefinition forexUnderlyingDefinition =
        new ForexDefinition(EUR, USD, payDate, notional, strike);
    final ForexOptionDigitalDefinition callDefinition =
        new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong);
    final ForexOptionDigitalDefinition putDefinition =
        new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, !isCall, isLong);
    final ForexOptionDigital call = callDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME);
    final ForexOptionDigital put = putDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME);
    final MultipleCurrencyAmount pvCall = METHOD_DIGITAL_SPREAD.presentValue(call, SMILE_BUNDLE);
    final MultipleCurrencyAmount pvPut = METHOD_DIGITAL_SPREAD.presentValue(put, SMILE_BUNDLE);
    final Double pvCash = PVC.visit(put.getUnderlyingForex().getPaymentCurrency2(), CURVES);
    assertEquals(
        "Forex Digital option: call spread method - present value",
        pvCall.getAmount(USD) + pvPut.getAmount(USD),
        Math.abs(pvCash),
        TOLERANCE_PRICE_FLAT);
  }

  @Test
  /** Tests the present value long/short parity. */
  public void presentValueLongShort() {
    final ForexOptionDigitalDefinition forexOptionShortDefinition =
        new ForexOptionDigitalDefinition(FOREX_DEFINITION, OPTION_EXP_DATE, IS_CALL, !IS_LONG);
    final InstrumentDerivative forexOptionShort =
        forexOptionShortDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME);
    final MultipleCurrencyAmount pvShort =
        METHOD_DIGITAL_SPREAD.presentValue(forexOptionShort, SMILE_BUNDLE);
    final MultipleCurrencyAmount pvLong =
        METHOD_DIGITAL_SPREAD.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertEquals(
        "Forex Digital option: present value long/short parity",
        pvLong.getAmount(USD),
        -pvShort.getAmount(USD),
        1E-2);
    final MultipleCurrencyAmount ceShort =
        METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionShort, SMILE_BUNDLE);
    final MultipleCurrencyAmount ceLong =
        METHOD_DIGITAL_SPREAD.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertEquals(
        "Forex Digital option: currency exposure long/short parity",
        ceLong.getAmount(USD),
        -ceShort.getAmount(USD),
        1E-2);
    assertEquals(
        "Forex Digital option: currency exposure long/short parity",
        ceLong.getAmount(EUR),
        -ceShort.getAmount(EUR),
        1E-2);
  }

  @Test
  /** Tests the currency exposure in a flat smile case. */
  public void currencyExposureFlat() {
    MultipleCurrencyAmount ceSpread =
        METHOD_DIGITAL_SPREAD.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE_FLAT);
    MultipleCurrencyAmount ceBlack =
        METHOD_DIGITAL_BLACK.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE_FLAT);
    assertEquals(
        "Forex Digital option: call spread method - currency exposure",
        ceBlack.getAmount(USD),
        ceSpread.getAmount(USD),
        TOLERANCE_CE_FLAT);
    assertEquals(
        "Forex Digital option: call spread method - currency exposure",
        ceBlack.getAmount(EUR),
        ceSpread.getAmount(EUR),
        TOLERANCE_CE_FLAT);
  }

  @Test
  /** Tests the currency exposure with an explicit computation. */
  public void currencyExposure() {
    double spread = 0.0001; // Relative spread.
    double strikeM = STRIKE * (1 - spread);
    double strikeP = STRIKE * (1 + spread);
    Forex forexM =
        new Forex(
            FOREX.getPaymentCurrency1().withAmount(1.0),
            FOREX.getPaymentCurrency2().withAmount(-strikeM));
    Forex forexP =
        new Forex(
            FOREX.getPaymentCurrency1().withAmount(1.0),
            FOREX.getPaymentCurrency2().withAmount(-strikeP));
    ForexOptionVanilla vanillaM =
        new ForexOptionVanilla(forexM, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG);
    ForexOptionVanilla vanillaP =
        new ForexOptionVanilla(forexP, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG);
    MultipleCurrencyAmount ceP = METHOD_VANILLA_BLACK.currencyExposure(vanillaP, SMILE_BUNDLE);
    MultipleCurrencyAmount ceM = METHOD_VANILLA_BLACK.currencyExposure(vanillaM, SMILE_BUNDLE);
    MultipleCurrencyAmount ceExpected =
        ceM.plus(ceP.multipliedBy(-1.0))
            .multipliedBy(
                1.0 / (strikeP - strikeM) * Math.abs(FOREX.getPaymentCurrency2().getAmount()));
    MultipleCurrencyAmount ceComputed =
        METHOD_DIGITAL_SPREAD.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertEquals(
        "Forex Digital option: call spread method - currency exposure",
        ceExpected.getAmount(USD),
        ceComputed.getAmount(USD),
        TOLERANCE_PRICE);
    assertEquals(
        "Forex Digital option: call spread method - currency exposure",
        ceExpected.getAmount(EUR),
        ceComputed.getAmount(EUR),
        TOLERANCE_PRICE);
  }

  @Test
  /** Tests the currency exposure against the present value. */
  public void currencyExposureVsPresentValue() {
    MultipleCurrencyAmount pv = METHOD_DIGITAL_SPREAD.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE);
    MultipleCurrencyAmount ce =
        METHOD_DIGITAL_SPREAD.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertEquals(
        "Forex Digital option: call spread method - currency exposure vs present value",
        ce.getAmount(USD) + ce.getAmount(EUR) * SPOT,
        pv.getAmount(USD),
        TOLERANCE_PRICE);
  }

  @Test
  /** Tests the put/call parity currency exposure. */
  public void currencyExposurePutCallParity() {
    final double strike = 1.45;
    final boolean isCall = true;
    final boolean isLong = true;
    final double notional = 100000000;
    final ZonedDateTime payDate =
        ScheduleCalculator.getAdjustedDate(
            REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR);
    final ZonedDateTime expDate =
        ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR);
    final ForexDefinition forexUnderlyingDefinition =
        new ForexDefinition(EUR, USD, payDate, notional, strike);
    final ForexOptionDigitalDefinition forexOptionDefinitionCall =
        new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong);
    final ForexOptionDigitalDefinition forexOptionDefinitionPut =
        new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, !isCall, isLong);
    final ForexOptionDigital forexOptionCall =
        forexOptionDefinitionCall.toDerivative(REFERENCE_DATE, CURVES_NAME);
    final ForexOptionDigital forexOptionPut =
        forexOptionDefinitionPut.toDerivative(REFERENCE_DATE, CURVES_NAME);
    final MultipleCurrencyAmount currencyExposureCall =
        METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionCall, SMILE_BUNDLE);
    final MultipleCurrencyAmount currencyExposurePut =
        METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionPut, SMILE_BUNDLE);
    final Double pvCash =
        PVC.visit(forexOptionPut.getUnderlyingForex().getPaymentCurrency2(), CURVES);
    assertEquals(
        "Forex Digital option: currency exposure put/call parity foreign",
        0,
        currencyExposureCall.getAmount(EUR) + currencyExposurePut.getAmount(EUR),
        TOLERANCE_PRICE);
    assertEquals(
        "Forex Digital option: currency exposure put/call parity domestic",
        Math.abs(pvCash),
        currencyExposureCall.getAmount(USD) + currencyExposurePut.getAmount(USD),
        TOLERANCE_PRICE);
  }

  @Test
  /** Tests the present value curve sensitivity. */
  public void presentValueCurveSensitivity() {
    double spread = 0.0001; // Relative spread.
    double strikeM = STRIKE * (1 - spread);
    double strikeP = STRIKE * (1 + spread);
    Forex forexM =
        new Forex(
            FOREX.getPaymentCurrency1().withAmount(1.0),
            FOREX.getPaymentCurrency2().withAmount(-strikeM));
    Forex forexP =
        new Forex(
            FOREX.getPaymentCurrency1().withAmount(1.0),
            FOREX.getPaymentCurrency2().withAmount(-strikeP));
    ForexOptionVanilla vanillaM =
        new ForexOptionVanilla(forexM, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG);
    ForexOptionVanilla vanillaP =
        new ForexOptionVanilla(forexP, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG);
    MultipleCurrencyInterestRateCurveSensitivity pvcsP =
        METHOD_VANILLA_BLACK.presentValueCurveSensitivity(vanillaP, SMILE_BUNDLE);
    MultipleCurrencyInterestRateCurveSensitivity pvcsM =
        METHOD_VANILLA_BLACK.presentValueCurveSensitivity(vanillaM, SMILE_BUNDLE);
    MultipleCurrencyInterestRateCurveSensitivity pvcsExpected =
        pvcsM
            .plus(pvcsP.multipliedBy(-1.0))
            .multipliedBy(
                1.0 / (strikeP - strikeM) * Math.abs(FOREX.getPaymentCurrency2().getAmount()));
    MultipleCurrencyInterestRateCurveSensitivity pvcsComputed =
        METHOD_DIGITAL_SPREAD.presentValueCurveSensitivity(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertTrue(
        "Forex Digital option: call spread method - present value",
        MultipleCurrencyInterestRateCurveSensitivity.compare(
            pvcsExpected, pvcsComputed, TOLERANCE_DELTA));
  }

  @Test
  /** Tests the present value curve sensitivity. */
  public void presentValueVolatilitySensitivity() {
    double strikeM = STRIKE * (1 - STANDARD_SPREAD);
    double strikeP = STRIKE * (1 + STANDARD_SPREAD);
    Forex forexM =
        new Forex(
            FOREX.getPaymentCurrency1().withAmount(1.0),
            FOREX.getPaymentCurrency2().withAmount(-strikeM));
    Forex forexP =
        new Forex(
            FOREX.getPaymentCurrency1().withAmount(1.0),
            FOREX.getPaymentCurrency2().withAmount(-strikeP));
    ForexOptionVanilla vanillaM =
        new ForexOptionVanilla(forexM, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG);
    ForexOptionVanilla vanillaP =
        new ForexOptionVanilla(forexP, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG);
    PresentValueForexBlackVolatilitySensitivity pvbvP =
        METHOD_VANILLA_BLACK.presentValueVolatilitySensitivity(vanillaP, SMILE_BUNDLE);
    PresentValueForexBlackVolatilitySensitivity pvbvM =
        METHOD_VANILLA_BLACK.presentValueVolatilitySensitivity(vanillaM, SMILE_BUNDLE);
    PresentValueForexBlackVolatilitySensitivity pvbvExpected =
        pvbvM
            .plus(pvbvP.multipliedBy(-1.0))
            .multipliedBy(
                1.0 / (strikeP - strikeM) * Math.abs(FOREX.getPaymentCurrency2().getAmount()));
    PresentValueForexBlackVolatilitySensitivity pvbvComputed =
        METHOD_DIGITAL_SPREAD.presentValueVolatilitySensitivity(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertEquals(
        "Forex Digital option: call spread method - present value volatility sensitivity",
        pvbvComputed.getVega().getMap().size(),
        2);
    assertTrue(
        "Forex Digital option: call spread method - present value volatility sensitivity",
        PresentValueForexBlackVolatilitySensitivity.compare(
            pvbvExpected, pvbvComputed, TOLERANCE_DELTA));
  }

  @Test
  /** Tests the present value. Spread change. */
  public void presentValueSpreadChange() {
    double spread1 = 0.0001;
    double spread2 = 0.0002;
    ForexOptionDigitalCallSpreadBlackMethod methodCallSpreadBlack1 =
        new ForexOptionDigitalCallSpreadBlackMethod(spread1);
    ForexOptionDigitalCallSpreadBlackMethod methodCallSpreadBlack2 =
        new ForexOptionDigitalCallSpreadBlackMethod(spread2);
    MultipleCurrencyAmount pv1 =
        methodCallSpreadBlack1.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE);
    MultipleCurrencyAmount pv2 =
        methodCallSpreadBlack2.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertEquals(
        "Forex Digital option: call spread method - present value",
        pv1.getAmount(USD),
        pv2.getAmount(USD),
        10.0);
    //    MultipleCurrencyAmount pvBlack = METHOD_DIGITAL_BLACK.presentValue(FOREX_CALL_OPTION,
    // SMILE_BUNDLE);
    //    assertEquals("Forex Digital option: call spread method - present value",
    // pvBlack.getAmount(USD), pv2.getAmount(USD), 10.0); // Should fail
  }

  @Test
  /** Tests the present value. Method vs Calculator. */
  public void presentValueMethodVCalculator() {
    MultipleCurrencyAmount pv1 =
        METHOD_DIGITAL_SPREAD.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE);
    PresentValueCallSpreadBlackForexCalculator calculator =
        new PresentValueCallSpreadBlackForexCalculator(STANDARD_SPREAD);
    MultipleCurrencyAmount pvCalculator = calculator.visit(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertEquals(
        "Forex Digital option: call spread method - present value",
        pv1.getAmount(USD),
        pvCalculator.getAmount(USD),
        TOLERANCE_PRICE);
  }

  @Test
  /** Tests the currency exposure. Method vs Calculator. */
  public void currencyExposureMethodVCalculator() {
    MultipleCurrencyAmount ceMethod =
        METHOD_DIGITAL_SPREAD.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE);
    CurrencyExposureCallSpreadBlackForexCalculator calculator =
        new CurrencyExposureCallSpreadBlackForexCalculator(STANDARD_SPREAD);
    MultipleCurrencyAmount ceCalculator = calculator.visit(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertEquals(
        "Forex Digital option: call spread method - currency exposure",
        ceMethod.getAmount(USD),
        ceCalculator.getAmount(USD),
        TOLERANCE_PRICE);
    assertEquals(
        "Forex Digital option: call spread method - currency exposure",
        ceMethod.getAmount(EUR),
        ceCalculator.getAmount(EUR),
        TOLERANCE_PRICE);
  }

  @Test
  /** Tests the present value curve sensitivity. Method vs Calculator. */
  public void presentValueCurveSensitivityMethodVCalculator() {
    MultipleCurrencyInterestRateCurveSensitivity pvcsMethod =
        METHOD_DIGITAL_SPREAD.presentValueCurveSensitivity(FOREX_CALL_OPTION, SMILE_BUNDLE);
    PresentValueCurveSensitivityCallSpreadBlackForexCalculator calculator =
        new PresentValueCurveSensitivityCallSpreadBlackForexCalculator(STANDARD_SPREAD);
    MultipleCurrencyInterestRateCurveSensitivity pvcsCalculator =
        calculator.visit(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertTrue(
        "Forex Digital option: call spread method - present value",
        MultipleCurrencyInterestRateCurveSensitivity.compare(
            pvcsMethod, pvcsCalculator, TOLERANCE_DELTA));
  }

  @Test
  /** Tests the present value volatility sensitivity. Method vs Calculator. */
  public void presentValueVolatilitySensitivityMethodVCalculator() {
    PresentValueForexBlackVolatilitySensitivity pvbvMethod =
        METHOD_DIGITAL_SPREAD.presentValueVolatilitySensitivity(FOREX_CALL_OPTION, SMILE_BUNDLE);
    PresentValueVolatilitySensitivityCallSpreadBlackForexCalculator calculator =
        new PresentValueVolatilitySensitivityCallSpreadBlackForexCalculator(STANDARD_SPREAD);
    PresentValueForexBlackVolatilitySensitivity pvbvCalculator =
        calculator.visit(FOREX_CALL_OPTION, SMILE_BUNDLE);
    assertTrue(
        "Forex Digital option: call spread method - present value volatility sensitivity",
        PresentValueForexBlackVolatilitySensitivity.compare(
            pvbvMethod, pvbvCalculator, TOLERANCE_DELTA));
  }
}