/** * Calculates the present value sensitivity to the implied volatility of the swaption product. * * <p>The sensitivity to the implied normal volatility is also called normal vega. * * @param swaption the swaption product * @param ratesProvider the rates provider * @param volatilityProvider the normal volatility provider * @return the point sensitivity to the normal volatility */ public SwaptionSensitivity presentValueSensitivityNormalVolatility( 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); double tenor = volatilityProvider.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate()); double pvbp = swapPricer.getLegPricer().pvbp(fixedLeg, ratesProvider); double strike = swapPricer.getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); if (expiry < 0.0d) { // Option has expired already return SwaptionSensitivity.of( volatilityProvider.getConvention(), expiryDateTime, tenor, strike, 0.0d, fixedLeg.getCurrency(), 0.0d); } double forward = swapPricer.parRate(underlying, ratesProvider); 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). // Backward sweep double vega = NORMAL.getVega(option, normalData) * ((expanded.getLongShort() == LongShort.LONG) ? 1.0 : -1.0); return SwaptionSensitivity.of( volatilityProvider.getConvention(), expiryDateTime, tenor, strike, forward, fixedLeg.getCurrency(), vega); }
public void test_volatility_sensitivity() { double eps = 1.0e-6; int nData = TIME.size(); for (int i = 0; i < NB_TEST; i++) { SwaptionSensitivity point = SwaptionSensitivity.of( CONVENTION, TEST_OPTION_EXPIRY[i], TENOR.get(i), TEST_STRIKE, TEST_FORWARD, GBP, TEST_SENSITIVITY[i]); SurfaceCurrencyParameterSensitivity sensi = PROVIDER_WITH_PARAM.surfaceCurrencyParameterSensitivity(point); Map<DoublesPair, Double> map = new HashMap<DoublesPair, Double>(); for (int j = 0; j < nData; ++j) { DoubleArray volDataUp = VOL.subArray(0, nData).with(j, VOL.get(j) + eps); DoubleArray volDataDw = VOL.subArray(0, nData).with(j, VOL.get(j) - eps); InterpolatedNodalSurface paramUp = InterpolatedNodalSurface.of( METADATA_WITH_PARAM, TIME, TENOR, volDataUp, INTERPOLATOR_2D); InterpolatedNodalSurface paramDw = InterpolatedNodalSurface.of( METADATA_WITH_PARAM, TIME, TENOR, volDataDw, INTERPOLATOR_2D); BlackVolatilityExpiryTenorSwaptionProvider provUp = BlackVolatilityExpiryTenorSwaptionProvider.of( paramUp, CONVENTION, ACT_365F, VALUATION_DATE_TIME); BlackVolatilityExpiryTenorSwaptionProvider provDw = BlackVolatilityExpiryTenorSwaptionProvider.of( paramDw, CONVENTION, ACT_365F, VALUATION_DATE_TIME); double volUp = provUp.getVolatility(TEST_OPTION_EXPIRY[i], TEST_TENOR[i], TEST_STRIKE, TEST_FORWARD); double volDw = provDw.getVolatility(TEST_OPTION_EXPIRY[i], TEST_TENOR[i], TEST_STRIKE, TEST_FORWARD); double fd = 0.5 * (volUp - volDw) / eps; map.put(DoublesPair.of(TIME.get(j), TENOR.get(j)), fd); } SurfaceCurrencyParameterSensitivity sensiFromNoMetadata = PROVIDER.surfaceCurrencyParameterSensitivity(point); List<SurfaceParameterMetadata> list = sensi.getMetadata().getParameterMetadata().get(); DoubleArray computed = sensi.getSensitivity(); assertEquals(computed.size(), nData); for (int j = 0; j < list.size(); ++j) { SwaptionSurfaceExpiryTenorNodeMetadata metadata = (SwaptionSurfaceExpiryTenorNodeMetadata) list.get(i); double expected = map.get(DoublesPair.of(metadata.getYearFraction(), metadata.getTenor())); assertEquals(computed.get(i), expected, eps); assertTrue( sensiFromNoMetadata.getMetadata().getParameterMetadata().get().contains(metadata)); } } }