/**
  * Computes the option security price curve sensitivity. The future price is computed without
  * convexity adjustment.
  *
  * @param security The future option security.
  * @param sabrData The SABR data bundle.
  * @return The security price curve sensitivity.
  */
 public InterestRateCurveSensitivity priceCurveSensitivity(
     final InterestRateFutureOptionMarginSecurity security,
     final SABRInterestRateDataBundle sabrData) {
   // Forward sweep
   final double priceFuture = METHOD_FUTURE.price(security.getUnderlyingFuture(), sabrData);
   final double rateStrike = 1.0 - security.getStrike();
   final EuropeanVanillaOption option =
       new EuropeanVanillaOption(rateStrike, security.getExpirationTime(), !security.isCall());
   final double forward = 1 - priceFuture;
   final double delay =
       security.getUnderlyingFuture().getLastTradingTime() - security.getExpirationTime();
   final double[] volatilityAdjoint =
       sabrData
           .getSABRParameter()
           .getVolatilityAdjoint(security.getExpirationTime(), delay, rateStrike, forward);
   final BlackFunctionData dataBlack = new BlackFunctionData(forward, 1.0, volatilityAdjoint[0]);
   final double[] priceAdjoint = BLACK_FUNCTION.getPriceAdjoint(option, dataBlack);
   // Backward sweep
   final double priceBar = 1.0;
   final double volatilityBar = priceAdjoint[2] * priceBar;
   final double forwardBar = priceAdjoint[1] * priceBar + volatilityAdjoint[1] * volatilityBar;
   final double priceFutureBar = -forwardBar;
   final InterestRateCurveSensitivity priceFutureDerivative =
       METHOD_FUTURE.priceCurveSensitivity(security.getUnderlyingFuture(), sabrData);
   return priceFutureDerivative.multipliedBy(priceFutureBar);
 }
 /**
  * Computes the present value curve sensitivity of a transaction.
  *
  * @param transaction The future option transaction.
  * @param curves The yield curve bundle.
  * @return The present value curve sensitivity.
  */
 public InterestRateCurveSensitivity presentValueCurveSensitivity(
     final InterestRateFutureOptionMarginTransaction transaction, final YieldCurveBundle curves) {
   InterestRateCurveSensitivity securitySensitivity =
       _securityMethod.priceCurveSensitivity(transaction.getUnderlyingOption(), curves);
   return securitySensitivity.multipliedBy(
       transaction.getQuantity()
           * transaction.getUnderlyingOption().getUnderlyingFuture().getNotional()
           * transaction.getUnderlyingOption().getUnderlyingFuture().getPaymentAccrualFactor());
 }
 /**
  * Computes the present value curve sensitivity of a transaction.
  *
  * @param transaction The future option transaction.
  * @param curves The yield curve bundle.
  * @return The present value curve sensitivity.
  */
 public InterestRateCurveSensitivity presentValueCurveSensitivity(
     final BondFutureOptionPremiumTransaction transaction, final YieldCurveBundle curves) {
   ArgumentChecker.notNull(transaction, "transaction");
   ArgumentChecker.notNull(curves, "curves");
   final InterestRateCurveSensitivity premiumSensitivity =
       PVCSC.visit(transaction.getPremium(), curves);
   final InterestRateCurveSensitivity securitySensitivity =
       METHOD_SECURITY.priceCurveSensitivity(transaction.getUnderlyingOption(), curves);
   return premiumSensitivity.plus(
       securitySensitivity.multipliedBy(
           transaction.getQuantity()
               * transaction.getUnderlyingOption().getUnderlyingFuture().getNotional()));
 }
 @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);
   }
 }
 /**
  * Computes the present value sensitivity to the yield curves of a CMS cap/floor by replication in
  * the SABR framework with extrapolation on the right.
  *
  * @param cmsCapFloor The CMS cap/floor.
  * @param sabrData The SABR data bundle. The SABR function need to be the Hagan function.
  * @return The present value sensitivity to curves.
  */
 @Override
 public InterestRateCurveSensitivity presentValueCurveSensitivity(
     final CapFloorCMS cmsCapFloor, final SABRInterestRateDataBundle sabrData) {
   Validate.notNull(cmsCapFloor);
   Validate.notNull(sabrData);
   final SABRInterestRateParameters sabrParameter = sabrData.getSABRParameter();
   final SwapFixedCoupon<? extends Payment> underlyingSwap = cmsCapFloor.getUnderlyingSwap();
   final double forward = underlyingSwap.accept(PRC, sabrData);
   final double discountFactor =
       sabrData
           .getCurve(underlyingSwap.getFixedLeg().getNthPayment(0).getFundingCurveName())
           .getDiscountFactor(cmsCapFloor.getPaymentTime());
   final double strike = cmsCapFloor.getStrike();
   final double maturity =
       underlyingSwap
               .getFixedLeg()
               .getNthPayment(underlyingSwap.getFixedLeg().getNumberOfPayments() - 1)
               .getPaymentTime()
           - cmsCapFloor.getSettlementTime();
   final DoublesPair expiryMaturity = new DoublesPair(cmsCapFloor.getFixingTime(), maturity);
   final double alpha = sabrParameter.getAlpha(expiryMaturity);
   final double beta = sabrParameter.getBeta(expiryMaturity);
   final double rho = sabrParameter.getRho(expiryMaturity);
   final double nu = sabrParameter.getNu(expiryMaturity);
   final SABRFormulaData sabrPoint = new SABRFormulaData(alpha, beta, rho, nu);
   // Common
   final CMSIntegrant integrantPrice =
       new CMSIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu);
   final CMSDeltaIntegrant integrantDelta =
       new CMSDeltaIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu);
   final double factor = discountFactor / integrantDelta.h(forward) * integrantDelta.g(forward);
   final double absoluteTolerance =
       1.0 / (factor * Math.abs(cmsCapFloor.getNotional()) * cmsCapFloor.getPaymentYearFraction());
   final double relativeTolerance = 1E-10;
   final RungeKuttaIntegrator1D integrator =
       new RungeKuttaIntegrator1D(absoluteTolerance, relativeTolerance, getNbIteration());
   // Price
   final double[] bs = integrantDelta.bsbsp(strike);
   @SuppressWarnings("synthetic-access")
   final double[] n = integrantDelta.nnp(forward);
   final double strikePartPrice = discountFactor * integrantDelta.k(strike) * n[0] * bs[0];
   double integralPartPrice;
   try {
     if (cmsCapFloor.isCap()) {
       integralPartPrice =
           discountFactor
               * integrator.integrate(integrantPrice, strike, strike + getIntegrationInterval());
     } else {
       integralPartPrice = discountFactor * integrator.integrate(integrantPrice, 0.0, strike);
     }
   } catch (final Exception e) {
     throw new RuntimeException(e);
   }
   final double price =
       (strikePartPrice + integralPartPrice)
           * cmsCapFloor.getNotional()
           * cmsCapFloor.getPaymentYearFraction();
   // Delta
   final double strikePart =
       discountFactor * integrantDelta.k(strike) * (n[1] * bs[0] + n[0] * bs[1]);
   double integralPart;
   try {
     if (cmsCapFloor.isCap()) {
       integralPart =
           discountFactor
               * integrator.integrate(integrantDelta, strike, strike + getIntegrationInterval());
     } else {
       integralPart = discountFactor * integrator.integrate(integrantDelta, 0.0, strike);
     }
   } catch (final Exception e) {
     throw new RuntimeException(e);
   }
   final double deltaS0 =
       (strikePart + integralPart)
           * cmsCapFloor.getNotional()
           * cmsCapFloor.getPaymentYearFraction();
   final double deltaPD = price / discountFactor;
   final double sensiDF = -cmsCapFloor.getPaymentTime() * discountFactor * deltaPD;
   final List<DoublesPair> list = new ArrayList<>();
   list.add(new DoublesPair(cmsCapFloor.getPaymentTime(), sensiDF));
   final Map<String, List<DoublesPair>> resultMap = new HashMap<>();
   resultMap.put(
       cmsCapFloor.getUnderlyingSwap().getFixedLeg().getNthPayment(0).getFundingCurveName(), list);
   InterestRateCurveSensitivity result = new InterestRateCurveSensitivity(resultMap);
   final InterestRateCurveSensitivity forwardDr =
       new InterestRateCurveSensitivity(cmsCapFloor.getUnderlyingSwap().accept(PRSC, sabrData));
   result = result.plus(forwardDr.multipliedBy(deltaS0));
   return result;
 }