/**
  * Constructor.
  *
  * @param cmsCap The CMS cap/floor.
  * @param sabrParameter The SABR parameters.
  * @param forward The forward.
  * @param cutOffStrike The cut-off strike.
  * @param mu The tail thickness parameter.
  */
 public CMSIntegrant(
     final CapFloorCMS cmsCap,
     final SABRFormulaData sabrPoint,
     final double forward,
     final double cutOffStrike,
     final double mu) {
   _nbFixedPeriod = cmsCap.getUnderlyingSwap().getFixedLeg().getPayments().length;
   _nbFixedPaymentYear =
       (int)
           Math.round(
               1.0
                   / cmsCap
                       .getUnderlyingSwap()
                       .getFixedLeg()
                       .getNthPayment(0)
                       .getPaymentYearFraction());
   _tau = 1.0 / _nbFixedPaymentYear;
   _delta = cmsCap.getPaymentTime() - cmsCap.getSettlementTime();
   _eta = -_delta;
   _timeToExpiry = cmsCap.getFixingTime();
   _forward = forward;
   _sabrExtrapolation =
       new SABRExtrapolationRightFunction(forward, sabrPoint, cutOffStrike, _timeToExpiry, mu);
   _isCall = cmsCap.isCap();
   _strike = cmsCap.getStrike();
   _factor = g(_forward) / h(_forward);
 }
 /**
  * Compute the present value of a CMS cap/floor by replication in SABR framework with
  * extrapolation on the right.
  *
  * @param cmsCapFloor The CMS cap/floor.
  * @param sabrData The SABR data bundle.
  * @return The present value.
  */
 @Override
 public CurrencyAmount presentValue(
     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 discountFactorTp =
       sabrData
           .getCurve(underlyingSwap.getFixedLeg().getNthPayment(0).getFundingCurveName())
           .getDiscountFactor(cmsCapFloor.getPaymentTime());
   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);
   final CMSIntegrant integrant =
       new CMSIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu);
   final double strike = cmsCapFloor.getStrike();
   final double factor = discountFactorTp / integrant.h(forward) * integrant.g(forward);
   final double strikePart = factor * integrant.k(strike) * integrant.bs(strike);
   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());
   double integralPart;
   try {
     if (cmsCapFloor.isCap()) {
       integralPart =
           discountFactorTp
               * integrator.integrate(integrant, strike, strike + getIntegrationInterval());
     } else {
       integralPart = discountFactorTp * integrator.integrate(integrant, 0.0, strike);
     }
   } catch (final Exception e) {
     throw new RuntimeException(e);
   }
   final double priceCMS =
       (strikePart + integralPart)
           * cmsCapFloor.getNotional()
           * cmsCapFloor.getPaymentYearFraction();
   return CurrencyAmount.of(cmsCapFloor.getCurrency(), priceCMS);
 }
  /**
   * Computes the present value sensitivity to the strike of a CMS cap/floor by replication in 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 strike.
   */
  @Override
  public double presentValueStrikeSensitivity(
      final CapFloorCMS cmsCapFloor, final SABRInterestRateDataBundle 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);
    final CMSStrikeIntegrant integrant =
        new CMSStrikeIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu);
    final double factor = discountFactor * integrant.g(forward) / integrant.h(forward);
    final double absoluteTolerance = 1.0E-9;
    final double relativeTolerance = 1.0E-5;
    final RungeKuttaIntegrator1D integrator =
        new RungeKuttaIntegrator1D(absoluteTolerance, relativeTolerance, getNbIteration());
    final SABRExtrapolationRightFunction sabrExtrapolation =
        new SABRExtrapolationRightFunction(
            forward, sabrPoint, _cutOffStrike, cmsCapFloor.getFixingTime(), _mu);
    final EuropeanVanillaOption option =
        new EuropeanVanillaOption(strike, cmsCapFloor.getFixingTime(), cmsCapFloor.isCap());
    final double[] kpkpp = integrant.kpkpp(strike);
    double firstPart;
    double thirdPart;
    if (cmsCapFloor.isCap()) {
      firstPart = -kpkpp[0] * integrant.bs(strike);
      thirdPart = integrator.integrate(integrant, strike, strike + getIntegrationInterval());
    } else {
      firstPart = 3 * kpkpp[0] * integrant.bs(strike);
      thirdPart = integrator.integrate(integrant, 0.0, strike);
    }
    final double secondPart = integrant.k(strike) * sabrExtrapolation.priceDerivativeStrike(option);

    return cmsCapFloor.getNotional()
        * cmsCapFloor.getPaymentYearFraction()
        * factor
        * (firstPart + secondPart + thirdPart);
  }
 /** Tests the cap present value SABR parameters sensitivity vs finite difference. */
 public void presentValueSABRSensitivity() {
   final double pv =
       METHOD_EXTRAPOLATION_CAP
           .presentValue(CMS_CAP_LONG, SABR_MULTICURVES)
           .getAmount(EUR)
           .getAmount();
   final PresentValueSABRSensitivityDataBundle pvsCapLong =
       METHOD_EXTRAPOLATION_CAP.presentValueSABRSensitivity(CMS_CAP_LONG, SABR_MULTICURVES);
   // SABR sensitivity vs finite difference
   final double shift = 0.0001;
   final double shiftAlpha = 0.00001;
   final double maturity =
       CMS_CAP_LONG
               .getUnderlyingSwap()
               .getFixedLeg()
               .getNthPayment(
                   CMS_CAP_LONG.getUnderlyingSwap().getFixedLeg().getNumberOfPayments() - 1)
               .getPaymentTime()
           - CMS_CAP_LONG.getSettlementTime();
   final DoublesPair expectedExpiryTenor = DoublesPair.of(CMS_CAP_LONG.getFixingTime(), maturity);
   // Alpha sensitivity vs finite difference computation
   final SABRInterestRateParameters sabrParameterAlphaBumped =
       SABRDataSets.createSABR1AlphaBumped(shiftAlpha);
   final SABRSwaptionProviderDiscount sabrBundleAlphaBumped =
       new SABRSwaptionProviderDiscount(MULTICURVES, sabrParameterAlphaBumped, EUR1YEURIBOR6M);
   final double pvLongPayerAlphaBumped =
       METHOD_EXTRAPOLATION_CAP
           .presentValue(CMS_CAP_LONG, sabrBundleAlphaBumped)
           .getAmount(EUR)
           .getAmount();
   final double expectedAlphaSensi = (pvLongPayerAlphaBumped - pv) / 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),
       TOLERANCE_PV_DELTA);
   // Rho sensitivity vs finite difference computation
   final SABRInterestRateParameters sabrParameterRhoBumped = SABRDataSets.createSABR1RhoBumped();
   final SABRSwaptionProviderDiscount sabrBundleRhoBumped =
       new SABRSwaptionProviderDiscount(MULTICURVES, sabrParameterRhoBumped, EUR1YEURIBOR6M);
   final double pvLongPayerRhoBumped =
       METHOD_EXTRAPOLATION_CAP
           .presentValue(CMS_CAP_LONG, sabrBundleRhoBumped)
           .getAmount(EUR)
           .getAmount();
   final double expectedRhoSensi = (pvLongPayerRhoBumped - pv) / 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",
       expectedRhoSensi,
       pvsCapLong.getRho().getMap().get(expectedExpiryTenor),
       TOLERANCE_PV_DELTA);
   // Alpha sensitivity vs finite difference computation
   final SABRInterestRateParameters sabrParameterNuBumped = SABRDataSets.createSABR1NuBumped();
   final SABRSwaptionProviderDiscount sabrBundleNuBumped =
       new SABRSwaptionProviderDiscount(MULTICURVES, sabrParameterNuBumped, EUR1YEURIBOR6M);
   final double pvLongPayerNuBumped =
       METHOD_EXTRAPOLATION_CAP
           .presentValue(CMS_CAP_LONG, sabrBundleNuBumped)
           .getAmount(EUR)
           .getAmount();
   final double expectedNuSensi = (pvLongPayerNuBumped - pv) / shift;
   assertEquals("Number of nu sensitivity", pvsCapLong.getNu().getMap().keySet().size(), 1);
   assertTrue(
       "Nu sensitivity expiry/tenor",
       pvsCapLong.getNu().getMap().keySet().contains(expectedExpiryTenor));
   assertEquals(
       "Nu sensitivity value",
       expectedNuSensi,
       pvsCapLong.getNu().getMap().get(expectedExpiryTenor),
       TOLERANCE_PV_DELTA);
 }
 /**
  * Computes the present value sensitivity to the SABR parameters of a CMS cap/floor by replication
  * in 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 SABR parameters.
  */
 @Override
 public PresentValueSABRSensitivityDataBundle presentValueSABRSensitivity(
     final CapFloorCMS cmsCapFloor, final SABRInterestRateDataBundle sabrData) {
   final SABRInterestRateParameters sabrParameter = sabrData.getSABRParameter();
   final SwapFixedCoupon<? extends Payment> underlyingSwap = cmsCapFloor.getUnderlyingSwap();
   final double forward = underlyingSwap.accept(PRC, sabrData);
   final double discountFactorTp =
       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);
   final CMSVegaIntegrant integrantVega =
       new CMSVegaIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu);
   final double factor = discountFactorTp / integrantVega.h(forward) * integrantVega.g(forward);
   final SABRExtrapolationRightFunction sabrExtrapolation =
       new SABRExtrapolationRightFunction(
           forward, sabrPoint, _cutOffStrike, cmsCapFloor.getFixingTime(), _mu);
   final EuropeanVanillaOption option =
       new EuropeanVanillaOption(strike, cmsCapFloor.getFixingTime(), cmsCapFloor.isCap());
   final double factor2 = factor * integrantVega.k(strike);
   final double[] strikePartPrice = new double[4];
   sabrExtrapolation.priceAdjointSABR(option, strikePartPrice);
   for (int loopvega = 0; loopvega < 4; loopvega++) {
     strikePartPrice[loopvega] *= factor2;
   }
   final double absoluteTolerance =
       1.0 / (factor * Math.abs(cmsCapFloor.getNotional()) * cmsCapFloor.getPaymentYearFraction());
   final double relativeTolerance = 1E-3;
   final RungeKuttaIntegrator1D integrator =
       new RungeKuttaIntegrator1D(absoluteTolerance, relativeTolerance, getNbIteration());
   final double[] integralPart = new double[4];
   final double[] totalSensi = new double[4];
   for (int loopparameter = 0; loopparameter < 4; loopparameter++) {
     integrantVega.setParameterIndex(loopparameter);
     try {
       if (cmsCapFloor.isCap()) {
         integralPart[loopparameter] =
             discountFactorTp
                 * integrator.integrate(integrantVega, strike, strike + getIntegrationInterval());
       } else {
         integralPart[loopparameter] =
             discountFactorTp * integrator.integrate(integrantVega, 0.0, strike);
       }
     } catch (final Exception e) {
       throw new RuntimeException(e);
     }
     totalSensi[loopparameter] =
         (strikePartPrice[loopparameter] + integralPart[loopparameter])
             * cmsCapFloor.getNotional()
             * cmsCapFloor.getPaymentYearFraction();
   }
   final PresentValueSABRSensitivityDataBundle sensi = new PresentValueSABRSensitivityDataBundle();
   sensi.addAlpha(expiryMaturity, totalSensi[0]);
   sensi.addBeta(expiryMaturity, totalSensi[1]);
   sensi.addRho(expiryMaturity, totalSensi[2]);
   sensi.addNu(expiryMaturity, totalSensi[3]);
   return sensi;
 }
 /**
  * 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;
 }