/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.swaption; import static com.opengamma.strata.market.model.SabrParameterType.ALPHA; import static com.opengamma.strata.market.model.SabrParameterType.BETA; import static com.opengamma.strata.market.model.SabrParameterType.NU; import static com.opengamma.strata.market.model.SabrParameterType.RHO; import java.time.ZonedDateTime; import com.opengamma.strata.basics.currency.Currency; import com.opengamma.strata.basics.value.ValueDerivatives; import com.opengamma.strata.collect.array.DoubleArray; import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder; import com.opengamma.strata.pricer.impl.option.BlackFormulaRepository; import com.opengamma.strata.pricer.rate.RatesProvider; import com.opengamma.strata.pricer.swap.DiscountingSwapProductPricer; import com.opengamma.strata.product.swap.ResolvedSwap; import com.opengamma.strata.product.swap.ResolvedSwapLeg; import com.opengamma.strata.product.swap.Swap; import com.opengamma.strata.product.swaption.ResolvedSwaption; /** * Pricer for swaption with physical settlement in SABR model on the swap rate. * <p> * The swap underlying the swaption must have a fixed leg on which the forward rate is computed. * The underlying swap must be single currency. * <p> * The volatility parameters are not adjusted for the underlying swap convention. * The volatilities from the provider are taken as such. * <p> * The value of the swaption after expiry is 0. For a swaption which already expired, negative number is returned by * the method, {@link SabrSwaptionVolatilities#relativeTime(ZonedDateTime)}. */ public class SabrSwaptionPhysicalProductPricer extends VolatilitySwaptionPhysicalProductPricer { /** * Default implementation. */ public static final SabrSwaptionPhysicalProductPricer DEFAULT = new SabrSwaptionPhysicalProductPricer(DiscountingSwapProductPricer.DEFAULT); /** * Creates an instance. * * @param swapPricer the pricer for {@link Swap} */ public SabrSwaptionPhysicalProductPricer(DiscountingSwapProductPricer swapPricer) { super(swapPricer); } //------------------------------------------------------------------------- /** * Calculates the present value sensitivity of the swaption product to the rate curves. * <p> * The present value sensitivity is computed in a "sticky model parameter" style, i.e. the sensitivity to the * curve nodes with the SABR model parameters unchanged. This sensitivity does not include a potential * re-calibration of the model parameters to the raw market data. * * @param swaption the swaption product * @param ratesProvider the rates provider * @param swaptionVolatilities the volatilities * @return the point sensitivity to the rate curves */ public PointSensitivityBuilder presentValueSensitivityRatesStickyModel( ResolvedSwaption swaption, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); ZonedDateTime expiryDateTime = swaption.getExpiry(); double expiry = swaptionVolatilities.relativeTime(expiryDateTime); ResolvedSwap underlying = swaption.getUnderlying(); ResolvedSwapLeg fixedLeg = fixedLeg(underlying); if (expiry < 0d) { // Option has expired already return PointSensitivityBuilder.none(); } double forward = getSwapPricer().parRate(underlying, ratesProvider); double pvbp = getSwapPricer().getLegPricer().pvbp(fixedLeg, ratesProvider); double strike = getSwapPricer().getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = swaptionVolatilities.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate()); double shift = swaptionVolatilities.shift(expiry, tenor); ValueDerivatives volatilityAdj = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward); boolean isCall = fixedLeg.getPayReceive().isPay(); // Payer at strike is exercise when rate > strike, i.e. call on rate // Backward sweep PointSensitivityBuilder pvbpDr = getSwapPricer().getLegPricer().pvbpSensitivity(fixedLeg, ratesProvider); PointSensitivityBuilder forwardDr = getSwapPricer().parRateSensitivity(underlying, ratesProvider); double shiftedForward = forward + shift; double shiftedStrike = strike + shift; double price = BlackFormulaRepository.price(shiftedForward, shiftedStrike, expiry, volatilityAdj.getValue(), isCall); double delta = BlackFormulaRepository.delta(shiftedForward, shiftedStrike, expiry, volatilityAdj.getValue(), isCall); double vega = BlackFormulaRepository.vega(shiftedForward, shiftedStrike, expiry, volatilityAdj.getValue()); double sign = swaption.getLongShort().sign(); return pvbpDr.multipliedBy(price * sign * Math.signum(pvbp)) .combinedWith(forwardDr.multipliedBy((delta + vega * volatilityAdj.getDerivative(0)) * Math.abs(pvbp) * sign)); } //------------------------------------------------------------------------- /** * Calculates the present value sensitivity to the SABR model parameters of the swaption product. * <p> * The sensitivity of the present value to the SABR model parameters, alpha, beta, rho and nu. * * @param swaption the swaption product * @param ratesProvider the rates provider * @param swaptionVolatilities the volatilities * @return the point sensitivity to the SABR model parameters */ public PointSensitivityBuilder presentValueSensitivityModelParamsSabr( ResolvedSwaption swaption, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.getExpiry()); ResolvedSwap underlying = swaption.getUnderlying(); ResolvedSwapLeg fixedLeg = fixedLeg(underlying); double tenor = swaptionVolatilities.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate()); double shift = swaptionVolatilities.shift(expiry, tenor); double pvbp = getSwapPricer().getLegPricer().pvbp(fixedLeg, ratesProvider); double strike = getSwapPricer().getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); if (expiry < 0d) { // Option has expired already return PointSensitivityBuilder.none(); } double forward = getSwapPricer().parRate(underlying, ratesProvider); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); DoubleArray derivative = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward).getDerivatives(); // Backward sweep double vega = Math.abs(pvbp) * BlackFormulaRepository.vega(forward + shift, strike + shift, expiry, volatility) * swaption.getLongShort().sign(); // sensitivities Currency ccy = fixedLeg.getCurrency(); SwaptionVolatilitiesName name = swaptionVolatilities.getName(); return PointSensitivityBuilder.of( SwaptionSabrSensitivity.of(name, expiry, tenor, ALPHA, ccy, vega * derivative.get(2)), SwaptionSabrSensitivity.of(name, expiry, tenor, BETA, ccy, vega * derivative.get(3)), SwaptionSabrSensitivity.of(name, expiry, tenor, RHO, ccy, vega * derivative.get(4)), SwaptionSabrSensitivity.of(name, expiry, tenor, NU, ccy, vega * derivative.get(5))); } }