/**
  * Computes the swap convention-modified par rate for a fixed coupon swap with a PVBP externally
  * provided.
  *
  * <p>Reference: Swaption pricing - v 1.3, OpenGamma Quantitative Research, June 2012.
  *
  * @param swap The swap.
  * @param pvbp The present value of a basis point.
  * @param curves The curves.
  * @return The modified rate.
  */
 public Double visitFixedCouponSwap(
     final SwapFixedCoupon<?> swap, final double pvbp, final YieldCurveBundle curves) {
   final double pvSecond =
       -swap.getSecondLeg().accept(PVC, curves)
           * Math.signum(swap.getSecondLeg().getNthPayment(0).getNotional());
   return -pvSecond / pvbp;
 }
 /**
  * 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);
  }
 @Test
 /** Approximation analysis. */
 public void presentValueApproximationAnalysis() {
   final NormalImpliedVolatilityFormula implied = new NormalImpliedVolatilityFormula();
   final int nbStrike = 20;
   final double[] pvExplicit = new double[nbStrike + 1];
   final double[] pvApproximation = new double[nbStrike + 1];
   final double[] strike = new double[nbStrike + 1];
   final double[] volExplicit = new double[nbStrike + 1];
   final double[] volApprox = new double[nbStrike + 1];
   final double strikeRange = 0.010;
   final SwapFixedCoupon<Coupon> swap = SWAP_PAYER_DEFINITION.toDerivative(REFERENCE_DATE);
   final double forward = swap.accept(PRDC, MULTICURVES);
   final double pvbp = METHOD_SWAP.presentValueBasisPoint(swap, MULTICURVES);
   for (int loopstrike = 0; loopstrike <= nbStrike; loopstrike++) {
     strike[loopstrike] =
         forward
             - strikeRange
             + 3
                 * strikeRange
                 * loopstrike
                 / nbStrike; // From forward-strikeRange to forward+2*strikeRange
     final SwapFixedIborDefinition swapDefinition =
         SwapFixedIborDefinition.from(
             SETTLEMENT_DATE,
             SWAP_TENOR,
             EUR1YEURIBOR6M,
             NOTIONAL,
             strike[loopstrike],
             FIXED_IS_PAYER);
     final SwaptionPhysicalFixedIborDefinition swaptionDefinition =
         SwaptionPhysicalFixedIborDefinition.from(EXPIRY_DATE, swapDefinition, IS_LONG);
     final SwaptionPhysicalFixedIbor swaption = swaptionDefinition.toDerivative(REFERENCE_DATE);
     pvExplicit[loopstrike] = METHOD_HW.presentValue(swaption, HW_MULTICURVES).getAmount(EUR);
     pvApproximation[loopstrike] =
         METHOD_HW_APPROXIMATION.presentValue(swaption, HW_MULTICURVES).getAmount(EUR);
     final NormalFunctionData data = new NormalFunctionData(forward, pvbp, 0.01);
     volExplicit[loopstrike] =
         implied.getImpliedVolatility(data, swaption, pvExplicit[loopstrike]);
     volApprox[loopstrike] =
         implied.getImpliedVolatility(data, swaption, pvApproximation[loopstrike]);
     assertEquals(
         "Swaption physical - Hull-White - implied volatility - explicit/approximation",
         volExplicit[loopstrike],
         volApprox[loopstrike],
         1.0E-3); // 0.10%
   }
 }
 @Test
 /** Tests payer/receiver/swap parity. */
 public void presentValueCurveSensitivityPayerReceiverParityExplicit() {
   final MultipleCurrencyMulticurveSensitivity pvhwsReceiverLong =
       METHOD_HW.presentValueCurveSensitivity(SWAPTION_LONG_RECEIVER, HW_MULTICURVES);
   final MultipleCurrencyMulticurveSensitivity pvhwsPayerShort =
       METHOD_HW.presentValueCurveSensitivity(SWAPTION_SHORT_PAYER, HW_MULTICURVES);
   final MultipleCurrencyMulticurveSensitivity pvSwap = SWAP_RECEIVER.accept(PVCSDC, MULTICURVES);
   AssertSensivityObjects.assertEquals(
       "Swaption physical - Hull-White - presentValueCurveSensitivity - payer/receiver/swap parity",
       pvSwap.cleaned(TOLERANCE_PV_DELTA),
       pvhwsReceiverLong.plus(pvhwsPayerShort).cleaned(TOLERANCE_PV_DELTA),
       TOLERANCE_PV_DELTA);
 }
 @Test
 /** Tests payer/receiver/swap parity. */
 public void payerReceiverParityExplicit() {
   final MultipleCurrencyAmount pvReceiverLong =
       METHOD_HW.presentValue(SWAPTION_LONG_RECEIVER, HW_MULTICURVES);
   final MultipleCurrencyAmount pvPayerShort =
       METHOD_HW.presentValue(SWAPTION_SHORT_PAYER, HW_MULTICURVES);
   final MultipleCurrencyAmount pvSwap = SWAP_RECEIVER.accept(PVDC, MULTICURVES);
   assertEquals(
       "Swaption physical - Hull-White - present value - payer/receiver/swap parity",
       pvReceiverLong.getAmount(EUR) + pvPayerShort.getAmount(EUR),
       pvSwap.getAmount(EUR),
       TOLERANCE_PV);
 }
 @Test(enabled = true)
 /** Compare explicit formula with Monte-Carlo and long/short and payer/receiver parities. */
 public void presentValueMonteCarlo() {
   HullWhiteMonteCarloMethod methodMC;
   methodMC =
       new HullWhiteMonteCarloMethod(
           new NormalRandomNumberGenerator(0.0, 1.0, new MersenneTwister()), NB_PATH);
   // Seed fixed to the DEFAULT_SEED for testing purposes.
   final MultipleCurrencyAmount pvPayerLongExplicit =
       METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES);
   final MultipleCurrencyAmount pvPayerLongMC =
       methodMC.presentValue(SWAPTION_LONG_PAYER, EUR, HW_MULTICURVES);
   assertEquals(
       "Swaption physical - Hull-White - Monte Carlo",
       pvPayerLongExplicit.getAmount(EUR),
       pvPayerLongMC.getAmount(EUR),
       1.0E+4);
   final double pvMCPreviousRun = 4221400.891;
   assertEquals(
       "Swaption physical - Hull-White - Monte Carlo",
       pvMCPreviousRun,
       pvPayerLongMC.getAmount(EUR),
       TOLERANCE_PV);
   methodMC =
       new HullWhiteMonteCarloMethod(
           new NormalRandomNumberGenerator(0.0, 1.0, new MersenneTwister()), NB_PATH);
   final MultipleCurrencyAmount pvPayerShortMC =
       methodMC.presentValue(SWAPTION_SHORT_PAYER, EUR, HW_MULTICURVES);
   assertEquals(
       "Swaption physical - Hull-White - Monte Carlo",
       -pvPayerLongMC.getAmount(EUR),
       pvPayerShortMC.getAmount(EUR),
       TOLERANCE_PV);
   final MultipleCurrencyAmount pvReceiverLongMC =
       methodMC.presentValue(SWAPTION_LONG_RECEIVER, EUR, HW_MULTICURVES);
   final MultipleCurrencyAmount pvSwap = SWAP_RECEIVER.accept(PVDC, MULTICURVES);
   assertEquals(
       "Swaption physical - Hull-White - Monte Carlo - payer/receiver/swap parity",
       pvReceiverLongMC.getAmount(EUR) + pvPayerShortMC.getAmount(EUR),
       pvSwap.getAmount(EUR),
       1.0E+5);
 }
 /**
  * Computes the par rate of a swap with one fixed leg.
  *
  * @param swap The Fixed coupon swap.
  * @param curves The curves.
  * @return The par swap rate. If the fixed leg has been set up with some fixed payments these are
  *     ignored for the purposes of finding the swap rate
  */
 @Override
 public Double visitFixedCouponSwap(final SwapFixedCoupon<?> swap, final YieldCurveBundle curves) {
   final double pvSecond = swap.getSecondLeg().accept(PVC, curves);
   final double pvbp = swap.getFixedLeg().withUnitCoupon().accept(PVC, curves);
   return -pvSecond / pvbp;
 }
 /**
  * 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;
 }