-
Notifications
You must be signed in to change notification settings - Fork 292
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pricers for overnight in-arrears cap/floor. (#2410)
* Pricer for overnight in-arrears cap/floor. Present value in pricer SABR and volatility based. * PR comments Co-authored-by: Guillaume Santoro <31656059+santorog@users.noreply.github.com>
- Loading branch information
1 parent
3e2500f
commit a7d9f20
Showing
9 changed files
with
1,734 additions
and
11 deletions.
There are no files selected for viewing
242 changes: 242 additions & 0 deletions
242
...om/opengamma/strata/pricer/capfloor/SabrOvernightInArrearsCapletFloorletPeriodPricer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
/* | ||
* Copyright (C) 2022 - present by OpenGamma Inc. and the OpenGamma group of companies | ||
* | ||
* Please see distribution for license. | ||
*/ | ||
package com.opengamma.strata.pricer.capfloor; | ||
|
||
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.LocalDate; | ||
import java.time.ZoneOffset; | ||
import java.util.List; | ||
|
||
import com.opengamma.strata.basics.currency.Currency; | ||
import com.opengamma.strata.basics.currency.CurrencyAmount; | ||
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.ZeroRateSensitivity; | ||
import com.opengamma.strata.pricer.impl.option.BlackFormulaRepository; | ||
import com.opengamma.strata.pricer.impl.rate.ForwardOvernightCompoundedRateComputationFn; | ||
import com.opengamma.strata.pricer.impl.volatility.smile.SabrFormulaData; | ||
import com.opengamma.strata.pricer.impl.volatility.smile.SabrInArrearsVolatilityFunction; | ||
import com.opengamma.strata.pricer.rate.RatesProvider; | ||
import com.opengamma.strata.product.capfloor.OvernightInArrearsCapletFloorletPeriod; | ||
import com.opengamma.strata.product.common.PutCall; | ||
import com.opengamma.strata.product.rate.OvernightCompoundedRateComputation; | ||
|
||
/** | ||
* Pricer for in-arrears caplets and floorlets (Asian style options) in the SABR with effective parameters approach. | ||
*/ | ||
public class SabrOvernightInArrearsCapletFloorletPeriodPricer { | ||
|
||
/** | ||
* The default function for Asian in-arrears effective parameters. | ||
*/ | ||
private final SabrInArrearsVolatilityFunction sabrInArrearsFunction; | ||
|
||
/** | ||
* Default implementation. | ||
*/ | ||
public static final SabrOvernightInArrearsCapletFloorletPeriodPricer DEFAULT = | ||
new SabrOvernightInArrearsCapletFloorletPeriodPricer(SabrInArrearsVolatilityFunction.DEFAULT); | ||
|
||
/** | ||
* Creates an instance. | ||
* | ||
* @param sabrInarrearsFunction the function for Asian in-arrears effective parameters | ||
*/ | ||
private SabrOvernightInArrearsCapletFloorletPeriodPricer(SabrInArrearsVolatilityFunction sabrInarrearsFunction) { | ||
this.sabrInArrearsFunction = sabrInarrearsFunction; | ||
} | ||
|
||
/** | ||
* Creates an instance. | ||
* | ||
* @param sabrInarrearsFunction the function for Asian in-arrears effective parameters | ||
* @return the instance | ||
*/ | ||
public static SabrOvernightInArrearsCapletFloorletPeriodPricer | ||
of(SabrInArrearsVolatilityFunction sabrInarrearsFunction) { | ||
return new SabrOvernightInArrearsCapletFloorletPeriodPricer(sabrInarrearsFunction); | ||
} | ||
|
||
/** | ||
* The function to compute overnight rates, including if necessary the past composition from time series. | ||
*/ | ||
private static final ForwardOvernightCompoundedRateComputationFn ON_FUNCT = | ||
ForwardOvernightCompoundedRateComputationFn.DEFAULT; | ||
|
||
/** | ||
* Computes the present value in the SABR model with effective parameters. | ||
* | ||
* @param period the caplet/floorlet period | ||
* @param ratesProvider the rates provider | ||
* @param sabrVolatilities the SABR volatilities | ||
* @return the present value | ||
*/ | ||
public CurrencyAmount presentValue( | ||
OvernightInArrearsCapletFloorletPeriod period, | ||
RatesProvider ratesProvider, | ||
SabrParametersIborCapletFloorletVolatilities sabrVolatilities) { | ||
|
||
Currency currency = period.getCurrency(); | ||
if (ratesProvider.getValuationDate().isAfter(period.getPaymentDate())) { | ||
return CurrencyAmount.of(currency, 0d); | ||
} | ||
OvernightCompoundedRateComputation onComputation = period.getOvernightRate(); | ||
LocalDate startDate = onComputation.getStartDate(); | ||
LocalDate endDate = onComputation.getEndDate(); | ||
double startTime = sabrVolatilities.relativeTime(startDate.atStartOfDay(ZoneOffset.UTC)); // The ON rates don't have an exact fixing time | ||
double endTime = sabrVolatilities.relativeTime(endDate.atStartOfDay(ZoneOffset.UTC)); | ||
double df = ratesProvider.discountFactor(currency, period.getPaymentDate()); | ||
PutCall putCall = period.getPutCall(); | ||
double strike = period.getStrike(); | ||
double forward = ON_FUNCT | ||
.rate(onComputation, onComputation.getStartDate(), onComputation.getEndDate(), ratesProvider); | ||
if (!ratesProvider.getValuationDate().isBefore(period.getEndDate())) { // Between end compounding and payment date | ||
double dfPayment = ratesProvider.discountFactor(currency, period.getPaymentDate()); | ||
return period.payoff(forward).multipliedBy(dfPayment); | ||
} | ||
double alpha = sabrVolatilities.alpha(startTime); // parameters at start of composition period, for coherence with term rate caplets | ||
double beta = sabrVolatilities.beta(startTime); | ||
double rho = sabrVolatilities.rho(startTime); | ||
double nu = sabrVolatilities.nu(startTime); | ||
double shift = sabrVolatilities.shift(startTime); | ||
SabrFormulaData sabr = SabrFormulaData.of(alpha, beta, rho, nu); | ||
SabrFormulaData sabrAdjusted = sabrInArrearsFunction.effectiveSabr(sabr, startTime, endTime); | ||
double volatility = sabrVolatilities.getParameters().getSabrVolatilityFormula() | ||
.volatility(forward + shift, strike + shift, endTime, | ||
sabrAdjusted.getAlpha(), sabrAdjusted.getBeta(), sabrAdjusted.getRho(), sabrAdjusted.getNu()); | ||
double price = df * period.getYearFraction() * | ||
BlackFormulaRepository.price(forward + shift, strike + shift, endTime, volatility, putCall.isCall()); | ||
return CurrencyAmount.of(currency, price * period.getNotional()); | ||
} | ||
|
||
/** | ||
* Computes the present value sensitivity to the rate with "sticky SABR model parameters". | ||
* | ||
* @param period the caplet/floorlet period | ||
* @param ratesProvider the rates provider | ||
* @param sabrVolatilities the SABR volatilities | ||
* @return the present value rate sensitivity | ||
*/ | ||
public PointSensitivityBuilder presentValueSensitivityRatesStickyModel( | ||
OvernightInArrearsCapletFloorletPeriod period, | ||
RatesProvider ratesProvider, | ||
SabrParametersIborCapletFloorletVolatilities sabrVolatilities) { | ||
|
||
Currency currency = period.getCurrency(); | ||
if (ratesProvider.getValuationDate().isAfter(period.getPaymentDate())) { | ||
return PointSensitivityBuilder.none(); | ||
} | ||
OvernightCompoundedRateComputation onComputation = period.getOvernightRate(); | ||
LocalDate startDate = onComputation.getStartDate(); | ||
LocalDate endDate = onComputation.getEndDate(); | ||
double startTime = sabrVolatilities.relativeTime(startDate.atStartOfDay(ZoneOffset.UTC)); // The ON rates don't have an exact fixing time | ||
double endTime = sabrVolatilities.relativeTime(endDate.atStartOfDay(ZoneOffset.UTC)); | ||
double df = ratesProvider.discountFactor(currency, period.getPaymentDate()); | ||
PutCall putCall = period.getPutCall(); | ||
double strike = period.getStrike(); | ||
double forward = ON_FUNCT | ||
.rate(onComputation, onComputation.getStartDate(), onComputation.getEndDate(), ratesProvider); | ||
if (!ratesProvider.getValuationDate().isBefore(period.getEndDate())) { // Between end compounding and payment date | ||
double pvForward = period.payoff(forward).getAmount(); | ||
// Backward sweep | ||
double dfPaymentBar = pvForward; | ||
ZeroRateSensitivity ddfPaymentdr = ratesProvider | ||
.discountFactors(currency).zeroRatePointSensitivity(period.getPaymentDate()); | ||
return ddfPaymentdr.multipliedBy(dfPaymentBar); | ||
} | ||
double alpha = sabrVolatilities.alpha(startTime); | ||
double beta = sabrVolatilities.beta(startTime); | ||
double rho = sabrVolatilities.rho(startTime); | ||
double nu = sabrVolatilities.nu(startTime); | ||
double shift = sabrVolatilities.shift(startTime); | ||
SabrFormulaData sabr = SabrFormulaData.of(alpha, beta, rho, nu); | ||
SabrFormulaData sabrAdjusted = sabrInArrearsFunction.effectiveSabr(sabr, startTime, endTime); | ||
ValueDerivatives volatility = sabrVolatilities.getParameters().getSabrVolatilityFormula() | ||
.volatilityAdjoint(forward + shift, strike + shift, endTime, | ||
sabrAdjusted.getAlpha(), sabrAdjusted.getBeta(), sabrAdjusted.getRho(), sabrAdjusted.getNu()); | ||
ValueDerivatives price = BlackFormulaRepository | ||
.priceAdjoint(forward + shift, strike + shift, endTime, volatility.getValue(), putCall.isCall()); | ||
double pv = price.getValue() * df * period.getYearFraction() * period.getNotional(); | ||
// Backward sweep | ||
double pvBar = 1.0; | ||
double priceBar = df * period.getYearFraction() * period.getNotional() * pvBar; | ||
double dfBar = pv / df * pvBar; | ||
double forwardBar = price.getDerivative(0) * priceBar; | ||
double volatilityBar = price.getDerivative(3) * priceBar; | ||
forwardBar += volatility.getDerivative(0) * volatilityBar; | ||
PointSensitivityBuilder dforwarddr = ON_FUNCT | ||
.rateSensitivity(onComputation, onComputation.getStartDate(), onComputation.getEndDate(), ratesProvider); | ||
ZeroRateSensitivity ddfdr = ratesProvider | ||
.discountFactors(currency).zeroRatePointSensitivity(period.getPaymentDate()); | ||
return ddfdr.multipliedBy(dfBar).combinedWith(dforwarddr.multipliedBy(forwardBar)); | ||
} | ||
|
||
/** | ||
* Computes the present value sensitivity to the SABR model parameters. | ||
* | ||
* @param period the caplet/floorlet period | ||
* @param ratesProvider the rates provider | ||
* @param sabrVolatilities the SABR volatilities | ||
* @return the present value model parameters sensitivity | ||
*/ | ||
public PointSensitivityBuilder presentValueSensitivityModelParamsSabr( | ||
OvernightInArrearsCapletFloorletPeriod period, | ||
RatesProvider ratesProvider, | ||
SabrParametersIborCapletFloorletVolatilities sabrVolatilities) { | ||
|
||
Currency currency = period.getCurrency(); | ||
if (!ratesProvider.getValuationDate().isBefore(period.getEndDate())) { | ||
return PointSensitivityBuilder.none(); | ||
} | ||
OvernightCompoundedRateComputation onComputation = period.getOvernightRate(); | ||
LocalDate startDate = onComputation.getStartDate(); | ||
LocalDate endDate = onComputation.getEndDate(); | ||
double startTime = sabrVolatilities.relativeTime(startDate.atStartOfDay(ZoneOffset.UTC)); | ||
double endTime = sabrVolatilities.relativeTime(endDate.atStartOfDay(ZoneOffset.UTC)); | ||
double df = ratesProvider.discountFactor(currency, period.getPaymentDate()); | ||
PutCall putCall = period.getPutCall(); | ||
double strike = period.getStrike(); | ||
double forward = ON_FUNCT | ||
.rate(onComputation, onComputation.getStartDate(), onComputation.getEndDate(), ratesProvider); | ||
double alpha = sabrVolatilities.alpha(startTime); | ||
double beta = sabrVolatilities.beta(startTime); | ||
double rho = sabrVolatilities.rho(startTime); | ||
double nu = sabrVolatilities.nu(startTime); | ||
double shift = sabrVolatilities.shift(startTime); | ||
SabrFormulaData sabr = SabrFormulaData.of(alpha, beta, rho, nu); | ||
List<ValueDerivatives> sabrAdjusted = sabrInArrearsFunction.effectiveSabrAd(sabr, startTime, endTime); | ||
ValueDerivatives volatility = sabrVolatilities.getParameters().getSabrVolatilityFormula() | ||
.volatilityAdjoint(forward + shift, strike + shift, endTime, | ||
sabrAdjusted.get(0).getValue(), sabrAdjusted.get(1).getValue(), | ||
sabrAdjusted.get(2).getValue(), sabrAdjusted.get(3).getValue()); | ||
ValueDerivatives price = BlackFormulaRepository | ||
.priceAdjoint(forward + shift, strike + shift, endTime, volatility.getValue(), putCall.isCall()); | ||
// Backward sweep | ||
double pvBar = 1.0; | ||
double priceBar = df * period.getYearFraction() * period.getNotional() * pvBar; | ||
double volatilityBar = price.getDerivative(3) * priceBar; | ||
double alphaHatBar = volatility.getDerivative(2) * volatilityBar; | ||
double betaHatBar = volatility.getDerivative(3) * volatilityBar; | ||
double rhoHatBar = volatility.getDerivative(4) * volatilityBar; | ||
double nuHatBar = volatility.getDerivative(5) * volatilityBar; | ||
DoubleArray paramHat = sabrAdjusted.get(0).getDerivatives().multipliedBy(alphaHatBar); | ||
paramHat = paramHat.plus(sabrAdjusted.get(1).getDerivatives().multipliedBy(betaHatBar)); | ||
paramHat = paramHat.plus(sabrAdjusted.get(2).getDerivatives().multipliedBy(rhoHatBar)); | ||
paramHat = paramHat.plus(sabrAdjusted.get(3).getDerivatives().multipliedBy(nuHatBar)); | ||
IborCapletFloorletVolatilitiesName name = sabrVolatilities.getName(); | ||
return PointSensitivityBuilder.of( | ||
IborCapletFloorletSabrSensitivity.of(name, startTime, ALPHA, currency, paramHat.get(0)), | ||
IborCapletFloorletSabrSensitivity.of(name, startTime, BETA, currency, paramHat.get(1)), | ||
IborCapletFloorletSabrSensitivity.of(name, startTime, RHO, currency, paramHat.get(2)), | ||
IborCapletFloorletSabrSensitivity.of(name, startTime, NU, currency, paramHat.get(3))); | ||
} | ||
|
||
} |
Oops, something went wrong.