All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opengamma.strata.pricer.swaption.VolatilitySwaptionPhysicalProductPricer Maven / Gradle / Ivy

The newest version!
/*
 * 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 java.time.ZonedDateTime;
import java.util.List;

import com.opengamma.strata.basics.currency.CurrencyAmount;
import com.opengamma.strata.basics.currency.MultiCurrencyAmount;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder;
import com.opengamma.strata.pricer.rate.RatesProvider;
import com.opengamma.strata.pricer.swap.DiscountingSwapProductPricer;
import com.opengamma.strata.product.common.PutCall;
import com.opengamma.strata.product.common.SettlementType;
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.swap.SwapLegType;
import com.opengamma.strata.product.swaption.ResolvedSwaption;

/**
 * Pricer for swaption with physical settlement based on volatilities.
 * 

* The swap underlying the swaption must have a fixed leg on which the forward rate is computed. * The underlying swap must be single currency. *

* The volatility parameters are not adjusted for the underlying swap convention. *

* The value of the swaption after expiry is 0. * For a swaption which has already expired, a negative number is returned by * {@link SwaptionVolatilities#relativeTime(ZonedDateTime)}. */ public class VolatilitySwaptionPhysicalProductPricer { /** * Default implementation. */ public static final VolatilitySwaptionPhysicalProductPricer DEFAULT = new VolatilitySwaptionPhysicalProductPricer(DiscountingSwapProductPricer.DEFAULT); /** * Pricer for {@link ResolvedSwap}. */ private final DiscountingSwapProductPricer swapPricer; /** * Creates an instance. * * @param swapPricer the pricer for {@link Swap} */ public VolatilitySwaptionPhysicalProductPricer(DiscountingSwapProductPricer swapPricer) { this.swapPricer = ArgChecker.notNull(swapPricer, "swapPricer"); } //------------------------------------------------------------------------- /** * Gets the swap pricer. * * @return the swap pricer */ protected DiscountingSwapProductPricer getSwapPricer() { return swapPricer; } //------------------------------------------------------------------------- /** * Calculates the present value of the swaption. *

* The result is expressed using the currency of the swaption. * * @param swaption the swaption * @param ratesProvider the rates provider * @param swaptionVolatilities the volatilities * @return the present value */ public CurrencyAmount presentValue( ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.getExpiry()); ResolvedSwap underlying = swaption.getUnderlying(); ResolvedSwapLeg fixedLeg = fixedLeg(underlying); if (expiry < 0d) { // Option has expired already return CurrencyAmount.of(fixedLeg.getCurrency(), 0d); } double forward = forwardRate(swaption, ratesProvider); double pvbp = swapPricer.getLegPricer().pvbp(fixedLeg, ratesProvider); double strike = swapPricer.getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = swaptionVolatilities.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate()); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); PutCall putCall = PutCall.ofPut(fixedLeg.getPayReceive().isReceive()); double price = Math.abs(pvbp) * swaptionVolatilities.price(expiry, tenor, putCall, strike, forward, volatility); return CurrencyAmount.of(fixedLeg.getCurrency(), price * swaption.getLongShort().sign()); } //------------------------------------------------------------------------- /** * Computes the currency exposure of the swaption. *

* This is equivalent to the present value of the swaption. * * @param swaption the swaption * @param ratesProvider the rates provider * @param swaptionVolatilities the volatilities * @return the currency exposure */ public MultiCurrencyAmount currencyExposure( ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { return MultiCurrencyAmount.of(presentValue(swaption, ratesProvider, swaptionVolatilities)); } //------------------------------------------------------------------------- /** * Computes the implied volatility of the swaption. * * @param swaption the swaption * @param ratesProvider the rates provider * @param swaptionVolatilities the volatilities * @return the implied volatility associated with the swaption */ public double impliedVolatility( ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.getExpiry()); ResolvedSwap underlying = swaption.getUnderlying(); ResolvedSwapLeg fixedLeg = fixedLeg(underlying); ArgChecker.isTrue(expiry >= 0d, "Option must be before expiry to compute an implied volatility"); double forward = forwardRate(swaption, ratesProvider); double pvbp = getSwapPricer().getLegPricer().pvbp(fixedLeg, ratesProvider); double strike = getSwapPricer().getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = swaptionVolatilities.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate()); return swaptionVolatilities.volatility(expiry, tenor, strike, forward); } //------------------------------------------------------------------------- /** * Provides the forward rate. *

* This is the par rate for the forward starting swap that is the underlying of the swaption. * * @param swaption the swaption * @param ratesProvider the rates provider * @return the forward rate */ public double forwardRate(ResolvedSwaption swaption, RatesProvider ratesProvider) { return swapPricer.parRate(swaption.getUnderlying(), ratesProvider); } //------------------------------------------------------------------------- /** * Calculates the present value delta of the swaption. *

* The present value delta is given by {@code pvbp * priceDelta} where {@code priceDelta} * is the first derivative of the price with respect to forward. The derivative is computed in the formula * underlying the volatility (Black or Normal), it does not take into account the potential change of implied * volatility induced by the change of forward. The number computed by this method is closely related * to the {@link VolatilitySwaptionPhysicalProductPricer#presentValueSensitivityRatesStickyStrike} method. *

* Related methods: Some concrete classes to this interface also implement a {@code presentValueSensitivity} * method which take into account the change of implied volatility. *

* The result is expressed using the currency of the swaption. * * @param swaption the swaption * @param ratesProvider the rates provider * @param swaptionVolatilities the volatilities * @return the present value delta of the swaption */ public CurrencyAmount presentValueDelta( ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.getExpiry()); ResolvedSwap underlying = swaption.getUnderlying(); ResolvedSwapLeg fixedLeg = fixedLeg(underlying); if (expiry < 0d) { // Option has expired already return CurrencyAmount.of(fixedLeg.getCurrency(), 0d); } double forward = forwardRate(swaption, ratesProvider); double pvbp = getSwapPricer().getLegPricer().pvbp(fixedLeg, ratesProvider); double numeraire = Math.abs(pvbp); double strike = getSwapPricer().getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = swaptionVolatilities.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate()); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); PutCall putCall = PutCall.ofPut(fixedLeg.getPayReceive().isReceive()); double delta = numeraire * swaptionVolatilities.priceDelta(expiry, tenor, putCall, strike, forward, volatility); return CurrencyAmount.of(fixedLeg.getCurrency(), delta * swaption.getLongShort().sign()); } //------------------------------------------------------------------------- /** * Calculates the present value gamma of the swaption. *

* The present value gamma is given by {@code pvbp * priceGamma} where {@code priceGamma} * is the second derivative of the price with respect to forward. *

* The result is expressed using the currency of the swaption. * * @param swaption the swaption * @param ratesProvider the rates provider * @param swaptionVolatilities the volatilities * @return the present value gamma of the swaption */ public CurrencyAmount presentValueGamma( ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.getExpiry()); ResolvedSwap underlying = swaption.getUnderlying(); ResolvedSwapLeg fixedLeg = fixedLeg(underlying); if (expiry < 0d) { // Option has expired already return CurrencyAmount.of(fixedLeg.getCurrency(), 0d); } double forward = forwardRate(swaption, ratesProvider); double pvbp = getSwapPricer().getLegPricer().pvbp(fixedLeg, ratesProvider); double numeraire = Math.abs(pvbp); double strike = getSwapPricer().getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = swaptionVolatilities.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate()); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); PutCall putCall = PutCall.ofPut(fixedLeg.getPayReceive().isReceive()); double gamma = numeraire * swaptionVolatilities.priceGamma(expiry, tenor, putCall, strike, forward, volatility); return CurrencyAmount.of(fixedLeg.getCurrency(), gamma * swaption.getLongShort().sign()); } //------------------------------------------------------------------------- /** * Calculates the present value of the swaption. *

* The present value theta is given by {@code pvbp * priceTheta} where {@code priceTheta} * is the minus of the price sensitivity to {@code timeToExpiry}. *

* The result is expressed using the currency of the swaption. * * @param swaption the swaption * @param ratesProvider the rates provider * @param swaptionVolatilities the volatilities * @return the present value theta of the swaption */ public CurrencyAmount presentValueTheta( ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.getExpiry()); ResolvedSwap underlying = swaption.getUnderlying(); ResolvedSwapLeg fixedLeg = fixedLeg(underlying); if (expiry < 0d) { // Option has expired already return CurrencyAmount.of(fixedLeg.getCurrency(), 0d); } double forward = forwardRate(swaption, ratesProvider); double pvbp = getSwapPricer().getLegPricer().pvbp(fixedLeg, ratesProvider); double numeraire = Math.abs(pvbp); double strike = getSwapPricer().getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = swaptionVolatilities.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate()); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); PutCall putCall = PutCall.ofPut(fixedLeg.getPayReceive().isReceive()); double theta = numeraire * swaptionVolatilities.priceTheta(expiry, tenor, putCall, strike, forward, volatility); return CurrencyAmount.of(fixedLeg.getCurrency(), theta * swaption.getLongShort().sign()); } //------------------------------------------------------------------------- /** * Calculates the present value sensitivity of the swaption to the rate curves. *

* The present value sensitivity is computed in a "sticky strike" style, i.e. the sensitivity to the * curve nodes with the volatility at the swaption strike unchanged. This sensitivity does not include a potential * change of volatility due to the implicit change of forward rate or moneyness. * * @param swaption the swaption * @param ratesProvider the rates provider * @param swaptionVolatilities the volatilities * @return the point sensitivity to the rate curves */ public PointSensitivityBuilder presentValueSensitivityRatesStickyStrike( ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.getExpiry()); ResolvedSwap underlying = swaption.getUnderlying(); ResolvedSwapLeg fixedLeg = fixedLeg(underlying); if (expiry < 0d) { // Option has expired already return PointSensitivityBuilder.none(); } double forward = forwardRate(swaption, 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 volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); PutCall putCall = PutCall.ofPut(fixedLeg.getPayReceive().isReceive()); double price = swaptionVolatilities.price(expiry, tenor, putCall, strike, forward, volatility); double delta = swaptionVolatilities.priceDelta(expiry, tenor, putCall, strike, forward, volatility); // Backward sweep PointSensitivityBuilder pvbpDr = getSwapPricer().getLegPricer().pvbpSensitivity(fixedLeg, ratesProvider); PointSensitivityBuilder forwardDr = getSwapPricer().parRateSensitivity(underlying, ratesProvider); double sign = swaption.getLongShort().sign(); return pvbpDr.multipliedBy(price * sign * Math.signum(pvbp)) .combinedWith(forwardDr.multipliedBy(delta * Math.abs(pvbp) * sign)); } //------------------------------------------------------------------------- /** * Calculates the present value sensitivity to the implied volatility of the swaption. *

* The sensitivity to the implied volatility is also called vega. * * @param swaption the swaption * @param ratesProvider the rates provider * @param swaptionVolatilities the volatilities * @return the point sensitivity to the volatility */ public SwaptionSensitivity presentValueSensitivityModelParamsVolatility( ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities 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 pvbp = getSwapPricer().getLegPricer().pvbp(fixedLeg, ratesProvider); double strike = getSwapPricer().getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp); if (expiry < 0d) { // Option has expired already return SwaptionSensitivity.of( swaptionVolatilities.getName(), expiry, tenor, strike, 0d, fixedLeg.getCurrency(), 0d); } double forward = forwardRate(swaption, ratesProvider); double numeraire = Math.abs(pvbp); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); PutCall putCall = PutCall.ofPut(fixedLeg.getPayReceive().isReceive()); double vega = numeraire * swaptionVolatilities.priceVega(expiry, tenor, putCall, strike, forward, volatility); return SwaptionSensitivity.of( swaptionVolatilities.getName(), expiry, tenor, strike, forward, fixedLeg.getCurrency(), vega * swaption.getLongShort().sign()); } //------------------------------------------------------------------------- /** * Checks that there is exactly one fixed leg and returns it. * * @param swap the swap * @return the fixed leg */ protected ResolvedSwapLeg fixedLeg(ResolvedSwap swap) { ArgChecker.isFalse(swap.isCrossCurrency(), "Swap must be single currency"); // find fixed leg List fixedLegs = swap.getLegs(SwapLegType.FIXED); if (fixedLegs.isEmpty()) { throw new IllegalArgumentException("Swap must contain a fixed leg"); } return fixedLegs.get(0); } /** * Validates that the rates and volatilities providers are coherent * and that the swaption is single currency physical. * * @param swaption the swaption * @param ratesProvider the rates provider * @param swaptionVolatilities the volatilities */ protected void validate( ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { ArgChecker.isTrue(swaptionVolatilities.getValuationDate().equals(ratesProvider.getValuationDate()), "Volatility and rate data must be for the same date"); validateSwaption(swaption); } /** * Validates that the swaption is single currency physical. * * @param swaption the swaption */ protected void validateSwaption(ResolvedSwaption swaption) { ArgChecker.isFalse(swaption.getUnderlying().isCrossCurrency(), "Underlying swap must be single currency"); ArgChecker.isTrue(swaption.getSwaptionSettlement().getSettlementType().equals(SettlementType.PHYSICAL), "Swaption must be physical settlement"); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy