/** * Calculates the present value of the swaption product. * * <p>The result is expressed using the currency of the swapion. * * @param swaption the product to price * @param ratesProvider the rates provider * @param volatilityProvider the normal volatility provider * @return the present value of the swaption product */ public CurrencyAmount presentValue( SwaptionProduct swaption, RatesProvider ratesProvider, NormalVolatilitySwaptionProvider volatilityProvider) { ExpandedSwaption expanded = swaption.expand(); validate(ratesProvider, expanded, volatilityProvider); ZonedDateTime expiryDateTime = expanded.getExpiryDateTime(); double expiry = volatilityProvider.relativeTime(expiryDateTime); ExpandedSwap underlying = expanded.getUnderlying(); ExpandedSwapLeg fixedLeg = fixedLeg(underlying); if (expiry < 0.0d) { // Option has expired already return CurrencyAmount.of(fixedLeg.getCurrency(), 0.0d); } double forward = swapPricer.parRate(underlying, ratesProvider); double pvbp = swapPricer.getLegPricer().pvbp(fixedLeg, ratesProvider); double strike = swapPricer.getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = volatilityProvider.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate()); double volatility = volatilityProvider.getVolatility(expiryDateTime, tenor, strike, forward); NormalFunctionData normalData = NormalFunctionData.of(forward, Math.abs(pvbp), volatility); boolean isCall = (fixedLeg.getPayReceive() == PayReceive.PAY); // Payer at strike is exercise when rate > strike, i.e. call on rate EuropeanVanillaOption option = EuropeanVanillaOption.of(strike, expiry, isCall ? PutCall.CALL : PutCall.PUT); // option required to pass the strike (in case the swap has non-constant coupon). Function1D<NormalFunctionData, Double> func = NORMAL.getPriceFunction(option); double pv = func.evaluate(normalData) * ((expanded.getLongShort() == LongShort.LONG) ? 1.0 : -1.0); return CurrencyAmount.of(fixedLeg.getCurrency(), pv); }
/** * Calculates the present value sensitivity of the swaption product. * * <p>The present value sensitivity of the product is the sensitivity of the present value to the * underlying curves. * * @param swaption the swaption product * @param ratesProvider the rates provider * @param volatilityProvider the normal volatility provider * @return the present value curve sensitivity of the swap product */ public PointSensitivityBuilder presentValueSensitivityStickyStrike( SwaptionProduct swaption, RatesProvider ratesProvider, NormalVolatilitySwaptionProvider volatilityProvider) { ExpandedSwaption expanded = swaption.expand(); validate(ratesProvider, expanded, volatilityProvider); ZonedDateTime expiryDateTime = expanded.getExpiryDateTime(); double expiry = volatilityProvider.relativeTime(expiryDateTime); ExpandedSwap underlying = expanded.getUnderlying(); ExpandedSwapLeg fixedLeg = fixedLeg(underlying); if (expiry < 0.0d) { // Option has expired already return PointSensitivityBuilder.none(); } double forward = swapPricer.parRate(underlying, ratesProvider); double pvbp = swapPricer.getLegPricer().pvbp(fixedLeg, ratesProvider); double strike = swapPricer.getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = volatilityProvider.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate()); double volatility = volatilityProvider.getVolatility(expiryDateTime, tenor, strike, forward); NormalFunctionData normalData = NormalFunctionData.of(forward, 1.0d, volatility); boolean isCall = (fixedLeg.getPayReceive() == PayReceive.PAY); // Payer at strike is exercise when rate > strike, i.e. call on rate EuropeanVanillaOption option = EuropeanVanillaOption.of(strike, expiry, isCall ? PutCall.CALL : PutCall.PUT); // option required to pass the strike (in case the swap has non-constant coupon). // Backward sweep PointSensitivityBuilder pvbpDr = swapPricer.getLegPricer().pvbpSensitivity(fixedLeg, ratesProvider); PointSensitivityBuilder forwardDr = swapPricer.parRateSensitivity(underlying, ratesProvider); ValueDerivatives pv = NORMAL.getPriceAdjoint(option, normalData); double sign = (expanded.getLongShort() == LongShort.LONG) ? 1.0 : -1.0; return pvbpDr .multipliedBy(pv.getValue() * sign * Math.signum(pvbp)) .combinedWith(forwardDr.multipliedBy(pv.getDerivative(0) * Math.abs(pvbp) * sign)); }
// validate that the rates and volatilities providers are coherent private void validate( RatesProvider ratesProvider, ExpandedSwaption swaption, NormalVolatilitySwaptionProvider volatilityProvider) { ArgChecker.isTrue( volatilityProvider .getValuationDateTime() .toLocalDate() .equals(ratesProvider.getValuationDate()), "volatility and rate data should be for the same date"); ArgChecker.isFalse( swaption.getUnderlying().isCrossCurrency(), "underlying swap should be single currency"); ArgChecker.isTrue( swaption.getSwaptionSettlement().getSettlementType().equals(SettlementType.PHYSICAL), "swaption should be physical settlement"); }
/** * Computes the implied Normal volatility of the swaption. * * @param swaption the product to price * @param ratesProvider the rates provider * @param volatilityProvider the normal volatility provider * @return the present value of the swap product */ public double impliedVolatility( SwaptionProduct swaption, RatesProvider ratesProvider, NormalVolatilitySwaptionProvider volatilityProvider) { ExpandedSwaption expanded = swaption.expand(); validate(ratesProvider, expanded, volatilityProvider); ZonedDateTime expiryDateTime = expanded.getExpiryDateTime(); double expiry = volatilityProvider.relativeTime(expiryDateTime); ExpandedSwap underlying = expanded.getUnderlying(); ExpandedSwapLeg fixedLeg = fixedLeg(underlying); ArgChecker.isTrue( expiry >= 0.0d, "option should be before expiry to compute an implied volatility"); double forward = swapPricer.parRate(underlying, ratesProvider); double pvbp = swapPricer.getLegPricer().pvbp(fixedLeg, ratesProvider); double strike = swapPricer.getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = volatilityProvider.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate()); return volatilityProvider.getVolatility(expiryDateTime, tenor, strike, forward); }