/** * 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 PresentValueSABRSensitivityDataBundle priceSABRSensitivity( final InterestRateFutureOptionMarginSecurity security, final SABRInterestRateDataBundle sabrData) { final PresentValueSABRSensitivityDataBundle sensi = new PresentValueSABRSensitivityDataBundle(); // 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 DoublesPair expiryDelay = new DoublesPair(security.getExpirationTime(), delay); sensi.addAlpha(expiryDelay, volatilityAdjoint[3] * volatilityBar); sensi.addBeta(expiryDelay, volatilityAdjoint[4] * volatilityBar); sensi.addRho(expiryDelay, volatilityAdjoint[5] * volatilityBar); sensi.addNu(expiryDelay, volatilityAdjoint[6] * volatilityBar); return sensi; }
/** * 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); }
/** * 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); }
/** * Computes the option security price from future price. * * @param security The future option security. * @param sabrData The SABR data bundle. * @param priceFuture The price of the underlying future. * @return The security price. */ public double optionPriceFromFuturePrice( final InterestRateFutureOptionMarginSecurity security, final SABRInterestRateDataBundle sabrData, final double priceFuture) { 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 volatility = sabrData .getSABRParameter() .getVolatility(new double[] {security.getExpirationTime(), delay, rateStrike, forward}); final BlackFunctionData dataBlack = new BlackFunctionData(forward, 1.0, volatility); final double priceSecurity = BLACK_FUNCTION.getPriceFunction(option).evaluate(dataBlack); return priceSecurity; }
@Override protected Object getResult( final InstrumentDerivative derivative, final SABRInterestRateDataBundle data, final ValueRequirement desiredValue) { final Double cutoff = Double.parseDouble( desiredValue.getConstraint( SABRRightExtrapolationFunctionDeprecated.PROPERTY_CUTOFF_STRIKE)); final Double mu = Double.parseDouble( desiredValue.getConstraint( SABRRightExtrapolationFunctionDeprecated.PROPERTY_TAIL_THICKNESS_PARAMETER)); final PresentValueSABRSensitivitySABRRightExtrapolationCalculator calculator = new PresentValueSABRSensitivitySABRRightExtrapolationCalculator(cutoff, mu); final PresentValueSABRSensitivityDataBundle result = derivative.accept(calculator, data); return getResultAsMatrix( SABRSensitivityNodeCalculator.calculateNodeSensitivities(result, data.getSABRParameter())); }
/** * 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; }