com.opengamma.strata.pricer.model.SabrParameters Maven / Gradle / Ivy
/*
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.model;
import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import org.joda.beans.ImmutableBean;
import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaBean;
import org.joda.beans.TypedMetaBean;
import org.joda.beans.gen.BeanDefinition;
import org.joda.beans.gen.ImmutableConstructor;
import org.joda.beans.gen.PropertyDefinition;
import org.joda.beans.impl.light.LightMetaBean;
import com.opengamma.strata.basics.date.DayCount;
import com.opengamma.strata.basics.value.ValueDerivatives;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.market.ValueType;
import com.opengamma.strata.market.curve.ConstantCurve;
import com.opengamma.strata.market.curve.Curve;
import com.opengamma.strata.market.curve.CurveInfoType;
import com.opengamma.strata.market.curve.Curves;
import com.opengamma.strata.market.curve.InterpolatedNodalCurve;
import com.opengamma.strata.market.param.ParameterMetadata;
import com.opengamma.strata.market.param.ParameterPerturbation;
import com.opengamma.strata.market.param.ParameterizedData;
import com.opengamma.strata.market.param.ParameterizedDataCombiner;
/**
* The volatility surface description under SABR model.
*
* This is used in interest rate modeling.
* Each SABR parameter is a {@link Curve} defined by expiry.
*
* The implementation allows for shifted SABR model.
* The shift parameter is also {@link Curve} defined by expiry.
*/
@BeanDefinition(style = "light")
public final class SabrParameters
implements ParameterizedData, ImmutableBean, Serializable {
/**
* A Curve used to apply no shift.
*/
private static final ConstantCurve ZERO_SHIFT = ConstantCurve.of("Zero shift", 0d);
/**
* The alpha (volatility level) curve.
*
* The x value of the curve is the expiry.
*/
@PropertyDefinition(validate = "notNull")
private final Curve alphaCurve;
/**
* The beta (elasticity) curve.
*
* The x value of the curve is the expiry.
*/
@PropertyDefinition(validate = "notNull")
private final Curve betaCurve;
/**
* The rho (correlation) curve.
*
* The x value of the curve is the expiry.
*/
@PropertyDefinition(validate = "notNull")
private final Curve rhoCurve;
/**
* The nu (volatility of volatility) curve.
*
* The x value of the curve is the expiry.
*/
@PropertyDefinition(validate = "notNull")
private final Curve nuCurve;
/**
* The shift parameter of shifted SABR model.
*
* The x value of the curve is the expiry.
* The shift is set to be 0 unless specified.
*/
@PropertyDefinition(validate = "notNull")
private final Curve shiftCurve;
/**
* The SABR volatility formula.
*/
@PropertyDefinition(validate = "notNull")
private final SabrVolatilityFormula sabrVolatilityFormula;
/**
* The day count convention of the curves.
*/
private final transient DayCount dayCount; // cached, not a property
/**
* The parameter combiner.
*/
private final transient ParameterizedDataCombiner paramCombiner; // cached, not a property
//-------------------------------------------------------------------------
/**
* Obtains an instance without shift from nodal curves and volatility function provider.
*
* Each curve is specified by an instance of {@link Curve}, such as {@link InterpolatedNodalCurve}.
* The curves must contain the correct metadata:
*
* - The x-value type must be {@link ValueType#YEAR_FRACTION}
*
- The y-value type must be {@link ValueType#SABR_ALPHA}, {@link ValueType#SABR_BETA},
* {@link ValueType#SABR_RHO} or {@link ValueType#SABR_NU}
*
- The day count must be set in the additional information of the Alpha curve using
* {@link CurveInfoType#DAY_COUNT}, if present on other curves it must match that on the Alpha
*
* Suitable curve metadata can be created using
* {@link Curves#sabrParameterByExpiry(String, DayCount, ValueType)}.
*
* @param alphaCurve the alpha curve
* @param betaCurve the beta curve
* @param rhoCurve the rho curve
* @param nuCurve the nu curve
* @param sabrFormula the SABR formula
* @return {@code SabrParameters}
*/
public static SabrParameters of(
Curve alphaCurve,
Curve betaCurve,
Curve rhoCurve,
Curve nuCurve,
SabrVolatilityFormula sabrFormula) {
return new SabrParameters(alphaCurve, betaCurve, rhoCurve, nuCurve, ZERO_SHIFT, sabrFormula);
}
/**
* Obtains an instance with shift from nodal curves and volatility function provider.
*
* Each curve is specified by an instance of {@link Curve}, such as {@link InterpolatedNodalCurve}.
* The curves must contain the correct metadata:
*
* - The x-value type must be {@link ValueType#YEAR_FRACTION}
*
- The y-value type must be {@link ValueType#YEAR_FRACTION}
*
- The z-value type must be {@link ValueType#SABR_ALPHA}, {@link ValueType#SABR_BETA},
* {@link ValueType#SABR_RHO} or {@link ValueType#SABR_NU} as appropriate
*
- The day count must be set in the additional information of the alpha curve using
* {@link CurveInfoType#DAY_COUNT}, if present on other curves it must match that on the alpha
*
* The shift curve does not have to contain any metadata.
* If it does, the day count and convention must match that on the alpha curve.
*
* Suitable curve metadata can be created using
* {@link Curves#sabrParameterByExpiry(String, DayCount, ValueType)}.
*
* @param alphaCurve the alpha curve
* @param betaCurve the beta curve
* @param rhoCurve the rho curve
* @param nuCurve the nu curve
* @param shiftCurve the shift curve
* @param sabrFormula the SABR formula
* @return {@code SabrParameters}
*/
public static SabrParameters of(
Curve alphaCurve,
Curve betaCurve,
Curve rhoCurve,
Curve nuCurve,
Curve shiftCurve,
SabrVolatilityFormula sabrFormula) {
return new SabrParameters(alphaCurve, betaCurve, rhoCurve, nuCurve, shiftCurve, sabrFormula);
}
@ImmutableConstructor
private SabrParameters(
Curve alphaCurve,
Curve betaCurve,
Curve rhoCurve,
Curve nuCurve,
Curve shiftCurve,
SabrVolatilityFormula sabrFormula) {
validate(alphaCurve, "alphaCurve", ValueType.SABR_ALPHA);
validate(betaCurve, "betaCurve", ValueType.SABR_BETA);
validate(rhoCurve, "rhoCurve", ValueType.SABR_RHO);
validate(nuCurve, "nuCurve", ValueType.SABR_NU);
ArgChecker.notNull(shiftCurve, "shiftCurve");
ArgChecker.notNull(sabrFormula, "sabrFormula");
DayCount dayCount = alphaCurve.getMetadata().findInfo(CurveInfoType.DAY_COUNT)
.orElseThrow(() -> new IllegalArgumentException("Incorrect curve metadata, missing DayCount"));
validate(betaCurve, dayCount);
validate(rhoCurve, dayCount);
validate(nuCurve, dayCount);
validate(shiftCurve, dayCount);
this.alphaCurve = alphaCurve;
this.betaCurve = betaCurve;
this.rhoCurve = rhoCurve;
this.nuCurve = nuCurve;
this.shiftCurve = shiftCurve;
this.sabrVolatilityFormula = sabrFormula;
this.dayCount = dayCount;
this.paramCombiner = ParameterizedDataCombiner.of(alphaCurve, betaCurve, rhoCurve, nuCurve, shiftCurve);
}
// basic value tpe checks
private static void validate(Curve curve, String name, ValueType yType) {
ArgChecker.notNull(curve, name);
curve.getMetadata().getXValueType().checkEquals(
ValueType.YEAR_FRACTION, "Incorrect x-value type for SABR volatilities");
ValueType yValueType = curve.getMetadata().getYValueType();
yValueType.checkEquals(yType, "Incorrect y-value type for SABR volatilities");
}
// ensure all curves that specify convention or day count are consistent
private static void validate(Curve curve, DayCount dayCount) {
if (!curve.getMetadata().findInfo(CurveInfoType.DAY_COUNT).orElse(dayCount).equals(dayCount)) {
throw new IllegalArgumentException("SABR curves must have the same day count");
}
}
// ensure standard constructor is invoked
private Object readResolve() {
return new SabrParameters(alphaCurve, betaCurve, rhoCurve, nuCurve, shiftCurve, sabrVolatilityFormula);
}
//-------------------------------------------------------------------------
/**
* Gets the day count used to calculate the expiry year fraction.
*
* @return the day count
*/
public DayCount getDayCount() {
return dayCount;
}
//-------------------------------------------------------------------------
@Override
public int getParameterCount() {
return paramCombiner.getParameterCount();
}
@Override
public double getParameter(int parameterIndex) {
return paramCombiner.getParameter(parameterIndex);
}
@Override
public ParameterMetadata getParameterMetadata(int parameterIndex) {
return paramCombiner.getParameterMetadata(parameterIndex);
}
@Override
public SabrParameters withParameter(int parameterIndex, double newValue) {
return new SabrParameters(
paramCombiner.underlyingWithParameter(0, Curve.class, parameterIndex, newValue),
paramCombiner.underlyingWithParameter(1, Curve.class, parameterIndex, newValue),
paramCombiner.underlyingWithParameter(2, Curve.class, parameterIndex, newValue),
paramCombiner.underlyingWithParameter(3, Curve.class, parameterIndex, newValue),
paramCombiner.underlyingWithParameter(4, Curve.class, parameterIndex, newValue),
sabrVolatilityFormula);
}
@Override
public SabrParameters withPerturbation(ParameterPerturbation perturbation) {
return new SabrParameters(
paramCombiner.underlyingWithPerturbation(0, Curve.class, perturbation),
paramCombiner.underlyingWithPerturbation(1, Curve.class, perturbation),
paramCombiner.underlyingWithPerturbation(2, Curve.class, perturbation),
paramCombiner.underlyingWithPerturbation(3, Curve.class, perturbation),
paramCombiner.underlyingWithPerturbation(4, Curve.class, perturbation),
sabrVolatilityFormula);
}
//-------------------------------------------------------------------------
/**
* Calculates the alpha parameter for time to expiry.
*
* @param expiry the time to expiry as a year fraction
* @return the alpha parameter
*/
public double alpha(double expiry) {
return alphaCurve.yValue(expiry);
}
/**
* Calculates the beta parameter for time to expiry.
*
* @param expiry the time to expiry as a year fraction
* @return the beta parameter
*/
public double beta(double expiry) {
return betaCurve.yValue(expiry);
}
/**
* Calculates the rho parameter for time to expiry.
*
* @param expiry the time to expiry as a year fraction
* @return the rho parameter
*/
public double rho(double expiry) {
return rhoCurve.yValue(expiry);
}
/**
* Calculates the nu parameter for time to expiry.
*
* @param expiry the time to expiry as a year fraction
* @return the nu parameter
*/
public double nu(double expiry) {
return nuCurve.yValue(expiry);
}
/**
* Calculates the shift parameter for time to expiry.
*
* @param expiry the time to expiry as a year fraction
* @return the shift parameter
*/
public double shift(double expiry) {
return shiftCurve.yValue(expiry);
}
//-------------------------------------------------------------------------
/**
* Calculates the volatility for given expiry, strike and forward rate.
*
* @param expiry the time to expiry as a year fraction
* @param strike the strike
* @param forward the forward
* @return the volatility
*/
public double volatility(double expiry, double strike, double forward) {
double alpha = alpha(expiry);
double beta = beta(expiry);
double rho = rho(expiry);
double nu = nu(expiry);
double shift = shift(expiry);
return sabrVolatilityFormula.volatility(forward + shift, strike + shift, expiry, alpha, beta, rho, nu);
}
/**
* Calculates the volatility and associated sensitivities.
*
* The derivatives are stored in an array with:
*
* - [0] derivative with respect to the forward
*
- [1] derivative with respect to the forward strike
*
- [2] derivative with respect to the alpha
*
- [3] derivative with respect to the beta
*
- [4] derivative with respect to the rho
*
- [5] derivative with respect to the nu
*
*
* @param expiry the time to expiry as a year fraction
* @param strike the strike
* @param forward the forward
* @return the volatility and associated derivatives
*/
public ValueDerivatives volatilityAdjoint(double expiry, double strike, double forward) {
double alpha = alpha(expiry);
double beta = beta(expiry);
double rho = rho(expiry);
double nu = nu(expiry);
double shift = shift(expiry);
return sabrVolatilityFormula.volatilityAdjoint(forward + shift, strike + shift, expiry, alpha, beta, rho, nu);
}
//------------------------- AUTOGENERATED START -------------------------
/**
* The meta-bean for {@code SabrParameters}.
*/
private static final TypedMetaBean META_BEAN =
LightMetaBean.of(
SabrParameters.class,
MethodHandles.lookup(),
new String[] {
"alphaCurve",
"betaCurve",
"rhoCurve",
"nuCurve",
"shiftCurve",
"sabrVolatilityFormula"},
new Object[0]);
/**
* The meta-bean for {@code SabrParameters}.
* @return the meta-bean, not null
*/
public static TypedMetaBean meta() {
return META_BEAN;
}
static {
MetaBean.register(META_BEAN);
}
/**
* The serialization version id.
*/
private static final long serialVersionUID = 1L;
@Override
public TypedMetaBean metaBean() {
return META_BEAN;
}
//-----------------------------------------------------------------------
/**
* Gets the alpha (volatility level) curve.
*
* The x value of the curve is the expiry.
* @return the value of the property, not null
*/
public Curve getAlphaCurve() {
return alphaCurve;
}
//-----------------------------------------------------------------------
/**
* Gets the beta (elasticity) curve.
*
* The x value of the curve is the expiry.
* @return the value of the property, not null
*/
public Curve getBetaCurve() {
return betaCurve;
}
//-----------------------------------------------------------------------
/**
* Gets the rho (correlation) curve.
*
* The x value of the curve is the expiry.
* @return the value of the property, not null
*/
public Curve getRhoCurve() {
return rhoCurve;
}
//-----------------------------------------------------------------------
/**
* Gets the nu (volatility of volatility) curve.
*
* The x value of the curve is the expiry.
* @return the value of the property, not null
*/
public Curve getNuCurve() {
return nuCurve;
}
//-----------------------------------------------------------------------
/**
* Gets the shift parameter of shifted SABR model.
*
* The x value of the curve is the expiry.
* The shift is set to be 0 unless specified.
* @return the value of the property, not null
*/
public Curve getShiftCurve() {
return shiftCurve;
}
//-----------------------------------------------------------------------
/**
* Gets the SABR volatility formula.
* @return the value of the property, not null
*/
public SabrVolatilityFormula getSabrVolatilityFormula() {
return sabrVolatilityFormula;
}
//-----------------------------------------------------------------------
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj.getClass() == this.getClass()) {
SabrParameters other = (SabrParameters) obj;
return JodaBeanUtils.equal(alphaCurve, other.alphaCurve) &&
JodaBeanUtils.equal(betaCurve, other.betaCurve) &&
JodaBeanUtils.equal(rhoCurve, other.rhoCurve) &&
JodaBeanUtils.equal(nuCurve, other.nuCurve) &&
JodaBeanUtils.equal(shiftCurve, other.shiftCurve) &&
JodaBeanUtils.equal(sabrVolatilityFormula, other.sabrVolatilityFormula);
}
return false;
}
@Override
public int hashCode() {
int hash = getClass().hashCode();
hash = hash * 31 + JodaBeanUtils.hashCode(alphaCurve);
hash = hash * 31 + JodaBeanUtils.hashCode(betaCurve);
hash = hash * 31 + JodaBeanUtils.hashCode(rhoCurve);
hash = hash * 31 + JodaBeanUtils.hashCode(nuCurve);
hash = hash * 31 + JodaBeanUtils.hashCode(shiftCurve);
hash = hash * 31 + JodaBeanUtils.hashCode(sabrVolatilityFormula);
return hash;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(224);
buf.append("SabrParameters{");
buf.append("alphaCurve").append('=').append(JodaBeanUtils.toString(alphaCurve)).append(',').append(' ');
buf.append("betaCurve").append('=').append(JodaBeanUtils.toString(betaCurve)).append(',').append(' ');
buf.append("rhoCurve").append('=').append(JodaBeanUtils.toString(rhoCurve)).append(',').append(' ');
buf.append("nuCurve").append('=').append(JodaBeanUtils.toString(nuCurve)).append(',').append(' ');
buf.append("shiftCurve").append('=').append(JodaBeanUtils.toString(shiftCurve)).append(',').append(' ');
buf.append("sabrVolatilityFormula").append('=').append(JodaBeanUtils.toString(sabrVolatilityFormula));
buf.append('}');
return buf.toString();
}
//-------------------------- AUTOGENERATED END --------------------------
}