/** * 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); }
/** * 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 of the Physical delivery swaption. * * @param swaption The swaption. * @param hwData The Hull-White parameters and the curves. * @return The present value. */ public CurrencyAmount presentValue( final SwaptionPhysicalFixedIbor swaption, final HullWhiteOneFactorPiecewiseConstantDataBundle hwData) { Validate.notNull(swaption); Validate.notNull(hwData); final double expiryTime = swaption.getTimeToExpiry(); final AnnuityPaymentFixed cfe = swaption.getUnderlyingSwap().accept(CFEC, hwData); final double[] alpha = new double[cfe.getNumberOfPayments()]; final double[] df = new double[cfe.getNumberOfPayments()]; final double[] discountedCashFlow = new double[cfe.getNumberOfPayments()]; for (int loopcf = 0; loopcf < cfe.getNumberOfPayments(); loopcf++) { alpha[loopcf] = MODEL.alpha( hwData.getHullWhiteParameter(), 0.0, expiryTime, expiryTime, cfe.getNthPayment(loopcf).getPaymentTime()); df[loopcf] = hwData .getCurve(cfe.getDiscountCurve()) .getDiscountFactor(cfe.getNthPayment(loopcf).getPaymentTime()); discountedCashFlow[loopcf] = df[loopcf] * cfe.getNthPayment(loopcf).getAmount(); } // Integration final SwaptionIntegrant integrant = new SwaptionIntegrant(discountedCashFlow, alpha); final double limit = 10.0; final double absoluteTolerance = 1.0E-2; final double relativeTolerance = 1.0E-6; final RungeKuttaIntegrator1D integrator = new RungeKuttaIntegrator1D(absoluteTolerance, relativeTolerance, NB_INTEGRATION); double pv = 0.0; try { pv = 1.0 / Math.sqrt(2.0 * Math.PI) * integrator.integrate(integrant, -limit, limit) * (swaption.isLong() ? 1.0 : -1.0); } catch (final Exception e) { throw new RuntimeException(e); } return CurrencyAmount.of(swaption.getCurrency(), pv); }
/** * Computes the future price from the curves used to price the underlying bonds and a Hull-White * one factor model. Computation by numerical integration. * * @param futures The future security. * @param data The curve and Hull-White parameters. * @return The future price. */ public double price( final BondFuturesSecurity futures, final HullWhiteIssuerProviderInterface data) { ArgumentChecker.notNull(futures, "Futures"); ArgumentChecker.notNull(data, "Hull-White/Issuer provider"); final Currency ccy = futures.getCurrency(); final LegalEntity issuer = futures.getDeliveryBasketAtDeliveryDate()[0].getIssuerEntity(); final double expiryTime = futures.getNoticeLastTime(); final double deliveryTime = futures.getDeliveryLastTime(); final int nbBonds = futures.getDeliveryBasketAtDeliveryDate().length; final int[] nbPayments = new int[nbBonds]; final AnnuityPaymentFixed[] cfe = new AnnuityPaymentFixed[nbBonds]; for (int loopb = 0; loopb < nbBonds; loopb++) { cfe[loopb] = futures.getDeliveryBasketAtDeliveryDate()[loopb].accept( CFEC, data.getMulticurveProvider()); nbPayments[loopb] = cfe[loopb].getNumberOfPayments(); final PaymentFixed[] payments = new PaymentFixed[nbPayments[loopb] + 1]; payments[0] = new PaymentFixed( ccy, deliveryTime, -futures.getDeliveryBasketAtDeliveryDate()[loopb].getAccruedInterest()); System.arraycopy(cfe[loopb].getPayments(), 0, payments, 1, nbPayments[loopb]); cfe[loopb] = new AnnuityPaymentFixed(payments); } final double[][] alpha = new double[nbBonds][]; final double[][] beta = new double[nbBonds][]; final double[][] df = new double[nbBonds][]; final double[][] discountedCashFlow = new double[nbBonds][]; for (int loopb = 0; loopb < nbBonds; loopb++) { alpha[loopb] = new double[nbPayments[loopb] + 1]; beta[loopb] = new double[nbPayments[loopb] + 1]; df[loopb] = new double[nbPayments[loopb] + 1]; discountedCashFlow[loopb] = new double[nbPayments[loopb] + 1]; for (int loopcf = 0; loopcf < cfe[loopb].getNumberOfPayments(); loopcf++) { alpha[loopb][loopcf] = MODEL.alpha( data.getHullWhiteParameters(), 0.0, expiryTime, deliveryTime, cfe[loopb].getNthPayment(loopcf).getPaymentTime()); beta[loopb][loopcf] = MODEL.futuresConvexityFactor( data.getHullWhiteParameters(), expiryTime, cfe[loopb].getNthPayment(loopcf).getPaymentTime(), deliveryTime); df[loopb][loopcf] = data.getIssuerProvider() .getDiscountFactor(issuer, cfe[loopb].getNthPayment(loopcf).getPaymentTime()); discountedCashFlow[loopb][loopcf] = df[loopb][loopcf] / df[loopb][0] * cfe[loopb].getNthPayment(loopcf).getAmount() * beta[loopb][loopcf] / futures.getConversionFactor()[loopb]; } } // Integration final FuturesIntegrant integrant = new FuturesIntegrant(discountedCashFlow, alpha); final double limit = 10.0; final double absoluteTolerance = 1.0E-2; final double relativeTolerance = 1.0E-6; final RungeKuttaIntegrator1D integrator = new RungeKuttaIntegrator1D(absoluteTolerance, relativeTolerance, NB_INTEGRATION); double price = 0.0; try { price = 1.0 / Math.sqrt(2.0 * Math.PI) * integrator.integrate(integrant, -limit, limit); } catch (final Exception e) { throw new RuntimeException(e); } return price; }
/** * 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; }
/** * Computes the present value of an Ibor cap/floor in arrears by replication based on the paper, * "Swap and Cap/Floors with Fixing in Arrears or Payment Delay," OpenGamma Quantitative * Documentation * http://developers.opengamma.com/quantitative-research/In-Arrears-and-Payment-Delay-Swaps-and-Caps-OpenGamma.pdf * * @param cap The cap/floor * @param curves The curves * @return The present value */ public MultipleCurrencyAmount presentValue( final CapFloorIbor cap, final MulticurveProviderInterface curves) { ArgumentChecker.notNull(cap, "The cap/floor shoud not be null"); ArgumentChecker.notNull(curves, "curves"); final Currency ccy = cap.getCurrency(); // Construct a "standard" CapFloorIbor whose paymentTime is set to be fixingPeriodEndTime CapFloorIbor capStandard = new CapFloorIbor( cap.getCurrency(), cap.getFixingPeriodEndTime(), cap.getPaymentYearFraction(), cap.getNotional(), cap.getFixingTime(), cap.getIndex(), cap.getFixingPeriodStartTime(), cap.getFixingPeriodEndTime(), cap.getFixingAccrualFactor(), cap.getStrike(), cap.isCap()); final double forward = curves.getSimplyCompoundForwardRate( cap.getIndex(), cap.getFixingPeriodStartTime(), cap.getFixingPeriodEndTime(), cap.getFixingAccrualFactor()); final double beta = (1.0 + cap.getFixingAccrualFactor() * forward) * curves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime()) / curves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime()); final double df = curves.getDiscountFactor(capStandard.getCurrency(), capStandard.getPaymentTime()); final double strikePart = (1.0 + cap.getFixingAccrualFactor() * capStandard.getStrike()) * presentValueStandard( forward, capStandard.getStrike(), capStandard.getFixingTime(), capStandard.isCap(), df, capStandard.getNotional(), capStandard.getPaymentYearFraction()); final InArrearsIntegrant integrant = new InArrearsIntegrant(capStandard, curves); double integralPart; try { if (cap.isCap()) { double atmVol = _smileFunction.getVolatility(forward); double upper = forward * Math.exp(6.0 * atmVol * Math.sqrt(cap.getFixingTime())); double strike = cap.getStrike(); integralPart = INTEGRATOR.integrate(integrant, strike, upper); double reminder = integrant.evaluate(upper) * upper; double error = reminder / integralPart; int count = 0; while (Math.abs(error) > REL_ERROR && count < MAX_COUNT) { integralPart += INTEGRATOR.integrate(integrant, upper, 2.0 * upper); upper *= 2.0; // The increase of integralPart in the next loop is bounded by reminder reminder = integrant.evaluate(upper) * upper; error = reminder / integralPart; ++count; if (count == MAX_COUNT) { LOGGER.info( "Maximum iteration count, " + MAX_COUNT + ", has been reached. Relative error is greater than " + REL_ERROR); } } } else { double strike = cap.getStrike(); integralPart = INTEGRATOR.integrate(integrant, REL_TOL * strike, strike); } } catch (final Exception e) { throw new MathException(e); } integralPart *= 2.0 * cap.getFixingAccrualFactor(); final double pv = (strikePart + integralPart) / beta; return MultipleCurrencyAmount.of(cap.getCurrency(), pv); }
/** * Computes the pv sensitivity of an Ibor cap/floor in arrears * * @param cap The cap/floor * @param curves The curves * @return The sensitivity */ public MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity( final CapFloorIbor cap, final MulticurveProviderInterface curves) { ArgumentChecker.notNull(cap, "The cap/floor shoud not be null"); ArgumentChecker.notNull(curves, "curves"); final Currency ccy = cap.getCurrency(); // Construct a "standard" CapFloorIbor whose paymentTime is set to be fixingPeriodEndTime CapFloorIbor capStandard = new CapFloorIbor( cap.getCurrency(), cap.getFixingPeriodEndTime(), cap.getPaymentYearFraction(), cap.getNotional(), cap.getFixingTime(), cap.getIndex(), cap.getFixingPeriodStartTime(), cap.getFixingPeriodEndTime(), cap.getFixingAccrualFactor(), cap.getStrike(), cap.isCap()); final double forward = curves.getSimplyCompoundForwardRate( cap.getIndex(), cap.getFixingPeriodStartTime(), cap.getFixingPeriodEndTime(), cap.getFixingAccrualFactor()); final double beta = (1.0 + cap.getFixingAccrualFactor() * forward) * curves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime()) / curves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime()); double df = curves.getDiscountFactor(capStandard.getCurrency(), capStandard.getPaymentTime()); double strikePart = (1.0 + cap.getFixingAccrualFactor() * capStandard.getStrike()) * presentValueStandard( forward, capStandard.getStrike(), capStandard.getFixingTime(), capStandard.isCap(), df, capStandard.getNotional(), capStandard.getPaymentYearFraction()); double strikePartDelta = (1.0 + cap.getFixingAccrualFactor() * capStandard.getStrike()) * presentValueDeltaStandard( forward, capStandard.getStrike(), capStandard.getFixingTime(), capStandard.isCap(), df, capStandard.getNotional(), capStandard.getPaymentYearFraction()); final InArrearsIntegrant integrant = new InArrearsIntegrant(capStandard, curves); double integralPart; double upper = 0.0; try { if (cap.isCap()) { double atmVol = _smileFunction.getVolatility(forward); upper = forward * Math.exp(6.0 * atmVol * Math.sqrt(cap.getFixingTime())); double strike = cap.getStrike(); integralPart = INTEGRATOR.integrate(integrant, strike, upper); double reminder = integrant.evaluate(upper) * upper; double error = reminder / integralPart; int count = 0; while (Math.abs(error) > REL_ERROR && count < MAX_COUNT) { integralPart += INTEGRATOR.integrate(integrant, upper, 2.0 * upper); upper *= 2.0; // The increase of integralPart in the next loop is bounded by reminder reminder = integrant.evaluate(upper) * upper; error = reminder / integralPart; ++count; if (count == MAX_COUNT) { LOGGER.info( "Maximum iteration count, " + MAX_COUNT + ", has been reached. Relative error is greater than " + REL_ERROR); } } } else { double strike = cap.getStrike(); integralPart = INTEGRATOR.integrate(integrant, REL_TOL * strike, strike); } } catch (final Exception e) { throw new MathException(e); } integralPart *= 2.0 * cap.getFixingAccrualFactor(); double pv = (strikePart + integralPart) / beta; double betaFwd = cap.getFixingAccrualFactor() * curves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime()) / curves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime()); double betaDscStart = (1.0 + cap.getFixingAccrualFactor() * forward) * curves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime()) * cap.getFixingPeriodStartTime() / curves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime()); double betaDscEnd = -(1.0 + cap.getFixingAccrualFactor() * forward) * curves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime()) * cap.getFixingPeriodEndTime() / curves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime()); List<DoublesPair> listDiscounting = new ArrayList<>(); double strikePartDsc = -capStandard.getPaymentTime() * strikePart; double integralPartDsc = -capStandard.getPaymentTime() * integralPart; listDiscounting.add( DoublesPair.of(capStandard.getPaymentTime(), (strikePartDsc + integralPartDsc) / beta)); listDiscounting.add(DoublesPair.of(cap.getFixingPeriodStartTime(), -pv * betaDscStart / beta)); listDiscounting.add(DoublesPair.of(cap.getFixingPeriodEndTime(), -pv * betaDscEnd / beta)); Map<String, List<DoublesPair>> mapDsc = new HashMap<>(); mapDsc.put(curves.getName(capStandard.getCurrency()), listDiscounting); final List<ForwardSensitivity> listForward = new ArrayList<>(); double strikePartFwd = strikePartDelta; double integralPartFwd = 0.0; final InArrearsDeltaIntegrant integrantFwd = new InArrearsDeltaIntegrant(capStandard, curves); try { if (cap.isCap()) { double strike = cap.getStrike(); integralPartFwd = INTEGRATOR.integrate(integrantFwd, strike, upper); } else { double strike = cap.getStrike(); integralPartFwd = INTEGRATOR.integrate(integrantFwd, REL_TOL * strike, strike); } } catch (final Exception e) { throw new MathException(e); } integralPartFwd *= 2.0 * cap.getFixingAccrualFactor(); listForward.add( new SimplyCompoundedForwardSensitivity( capStandard.getFixingPeriodStartTime(), capStandard.getFixingPeriodEndTime(), capStandard.getFixingAccrualFactor(), (strikePartFwd + integralPartFwd - pv * betaFwd) / beta)); Map<String, List<ForwardSensitivity>> mapFwd = new HashMap<>(); mapFwd.put(curves.getName(capStandard.getIndex()), listForward); return MultipleCurrencyMulticurveSensitivity.of( cap.getCurrency(), MulticurveSensitivity.of(mapDsc, mapFwd)); }