com.opengamma.strata.pricer.fxopt.InterpolatedStrikeSmileDeltaTermStructure Maven / Gradle / Ivy
/*
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.fxopt;
import static com.opengamma.strata.collect.Guavate.toImmutableList;
import static com.opengamma.strata.market.curve.interpolator.CurveExtrapolators.FLAT;
import static com.opengamma.strata.market.curve.interpolator.CurveInterpolators.LINEAR;
import static com.opengamma.strata.market.curve.interpolator.CurveInterpolators.TIME_SQUARE;
import static java.util.stream.Collectors.toList;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import org.joda.beans.Bean;
import org.joda.beans.BeanBuilder;
import org.joda.beans.ImmutableBean;
import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaBean;
import org.joda.beans.MetaProperty;
import org.joda.beans.gen.BeanDefinition;
import org.joda.beans.gen.ImmutableConstructor;
import org.joda.beans.gen.PropertyDefinition;
import org.joda.beans.impl.direct.DirectMetaBean;
import org.joda.beans.impl.direct.DirectMetaProperty;
import org.joda.beans.impl.direct.DirectMetaPropertyMap;
import org.joda.beans.impl.direct.DirectPrivateBeanBuilder;
import com.google.common.collect.ImmutableList;
import com.opengamma.strata.basics.date.DayCount;
import com.opengamma.strata.basics.date.Tenor;
import com.opengamma.strata.basics.value.ValueDerivatives;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.array.DoubleMatrix;
import com.opengamma.strata.market.curve.interpolator.BoundCurveInterpolator;
import com.opengamma.strata.market.curve.interpolator.CurveExtrapolator;
import com.opengamma.strata.market.curve.interpolator.CurveInterpolator;
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;
/**
* An interpolated term structure of smiles as used in Forex market.
*
* The term structure defined here is composed of smile descriptions at different times.
* The data of each smile contains delta and volatility in {@link SmileDeltaParameters}.
* The delta values must be common to all of the smiles.
*
* Time interpolation and extrapolation are used to obtain a smile for the objective time.
* Strike interpolation and extrapolation are used in the expiry-strike space where the
* delta values are converted to strikes using the Black formula.
*
* The default for the time direction is time squire interpolation with flat extrapolation.
* The default for the strike direction is linear interpolation with flat extrapolation.
*/
@BeanDefinition(builderScope = "private")
public final class InterpolatedStrikeSmileDeltaTermStructure
implements SmileDeltaTermStructure, ParameterizedData, ImmutableBean, Serializable {
/**
* The smile description at the different time to expiry. All item should have the same deltas.
*/
@PropertyDefinition(validate = "notNull", overrideGet = true)
private final ImmutableList volatilityTerm;
/**
* The day count convention used for the expiry.
*/
@PropertyDefinition(validate = "notNull", overrideGet = true)
private final DayCount dayCount;
/**
* The interpolator used in the time dimension.
*/
@PropertyDefinition(validate = "notNull")
private final CurveInterpolator timeInterpolator;
/**
* The left extrapolator used in the time dimension.
*/
@PropertyDefinition(validate = "notNull")
private final CurveExtrapolator timeExtrapolatorLeft;
/**
* The right extrapolator used in the time dimension.
*/
@PropertyDefinition(validate = "notNull")
private final CurveExtrapolator timeExtrapolatorRight;
/**
* The interpolator used in the strike dimension.
*/
@PropertyDefinition(validate = "notNull")
private final CurveInterpolator strikeInterpolator;
/**
* The left extrapolator used in the strike dimension.
*/
@PropertyDefinition(validate = "notNull")
private final CurveExtrapolator strikeExtrapolatorLeft;
/**
* The right extrapolator used in the strike dimension.
*/
@PropertyDefinition(validate = "notNull")
private final CurveExtrapolator strikeExtrapolatorRight;
/**
* A set of expiry times for the smile descriptions.
*
* This set must be consistent with the expiry values in {@code volatilityTerm},
* thus can be derived if {@code volatilityTerm} is the primary input.
*/
private final transient DoubleArray expiries; // derived
/**
* The parameter combiner.
*/
private final transient ParameterizedDataCombiner paramCombiner; // not a property
//-------------------------------------------------------------------------
/**
* Obtains volatility term structure from a set of smile descriptions.
*
* The time dimension will use 'TimeSquare' interpolation with flat extrapolation.
* The strike dimension will use 'Linear' interpolation with flat extrapolation.
*
* @param volatilityTerm the volatility descriptions
* @param dayCount the day count used for the expiry year-fraction
* @return the instance
*/
public static InterpolatedStrikeSmileDeltaTermStructure of(
List volatilityTerm,
DayCount dayCount) {
return of(volatilityTerm, dayCount, TIME_SQUARE, FLAT, FLAT, LINEAR, FLAT, FLAT);
}
/**
* Obtains volatility term structure from a set of smile descriptions
* with strike interpolator and extrapolators specified.
*
* The time dimension will use 'TimeSquare' interpolation with flat extrapolation.
*
* @param volatilityTerm the volatility descriptions
* @param dayCount the day count used for the expiry year-fraction
* @param strikeInterpolator interpolator used in the strike dimension
* @param strikeExtrapolatorLeft left extrapolator used in the strike dimension
* @param strikeExtrapolatorRight right extrapolator used in the strike dimension
* @return the instance
*/
public static InterpolatedStrikeSmileDeltaTermStructure of(
List volatilityTerm,
DayCount dayCount,
CurveInterpolator strikeInterpolator,
CurveExtrapolator strikeExtrapolatorLeft,
CurveExtrapolator strikeExtrapolatorRight) {
return of(
volatilityTerm,
dayCount,
TIME_SQUARE,
FLAT,
FLAT,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight);
}
/**
* Obtains volatility term structure from a set of smile descriptions
* with interpolator and extrapolators fully specified.
*
* @param volatilityTerm the volatility descriptions
* @param dayCount the day count used for the expiry year-fraction
* @param timeExtrapolatorLeft left extrapolator used in the time dimension
* @param timeInterpolator interpolator used in the time dimension
* @param timeExtrapolatorRight right extrapolator used in the time dimension
* @param strikeExtrapolatorLeft left extrapolator used in the strike dimension
* @param strikeInterpolator interpolator used in the strike dimension
* @param strikeExtrapolatorRight right extrapolator used in the strike dimension
* @return the instance
* @deprecated Use variant with correct interpolator/extrapolator order
*/
@Deprecated
public static InterpolatedStrikeSmileDeltaTermStructure of(
List volatilityTerm,
DayCount dayCount,
CurveExtrapolator timeExtrapolatorLeft,
CurveInterpolator timeInterpolator,
CurveExtrapolator timeExtrapolatorRight,
CurveExtrapolator strikeExtrapolatorLeft,
CurveInterpolator strikeInterpolator,
CurveExtrapolator strikeExtrapolatorRight) {
return of(
volatilityTerm,
dayCount,
timeInterpolator,
timeExtrapolatorLeft,
timeExtrapolatorRight,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight);
}
/**
* Obtains volatility term structure from a set of smile descriptions
* with interpolator and extrapolators fully specified.
*
* @param volatilityTerm the volatility descriptions
* @param dayCount the day count used for the expiry year-fraction
* @param timeInterpolator interpolator used in the time dimension
* @param timeExtrapolatorLeft left extrapolator used in the time dimension
* @param timeExtrapolatorRight right extrapolator used in the time dimension
* @param strikeInterpolator interpolator used in the strike dimension
* @param strikeExtrapolatorLeft left extrapolator used in the strike dimension
* @param strikeExtrapolatorRight right extrapolator used in the strike dimension
* @return the instance
*/
public static InterpolatedStrikeSmileDeltaTermStructure of(
List volatilityTerm,
DayCount dayCount,
CurveInterpolator timeInterpolator,
CurveExtrapolator timeExtrapolatorLeft,
CurveExtrapolator timeExtrapolatorRight,
CurveInterpolator strikeInterpolator,
CurveExtrapolator strikeExtrapolatorLeft,
CurveExtrapolator strikeExtrapolatorRight) {
ArgChecker.notEmpty(volatilityTerm, "volatilityTerm");
ArgChecker.notNull(dayCount, "dayCount");
int nSmiles = volatilityTerm.size();
DoubleArray deltaBase = volatilityTerm.get(0).getDelta();
for (int i = 1; i < nSmiles; ++i) {
ArgChecker.isTrue(deltaBase.equals(volatilityTerm.get(i).getDelta()), "delta must be common to all smiles");
}
return new InterpolatedStrikeSmileDeltaTermStructure(
volatilityTerm,
dayCount,
timeInterpolator,
timeExtrapolatorLeft,
timeExtrapolatorRight,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight);
}
//-------------------------------------------------------------------------
/**
* Obtains volatility term structure from expiry times, delta values and volatilities.
*
* The market date consists of time to expiry, delta and volatility.
* The delta must be positive and sorted in ascending order.
* The range of delta is common to all time to expiry.
*
* {@code volatility} should be {@code n * (2 * m + 1)}, where {@code n} is the length of {@code expiry}
* and {@code m} is the length of {@code delta}.
*
* The time dimension will use 'TimeSquare' interpolation with flat extrapolation.
* The strike dimension will use 'Linear' interpolation with flat extrapolation.
*
* @param expiries the expiry times of individual volatility smiles
* @param delta the delta values
* @param volatility the volatilities
* @param dayCount the day count used for the expiry year-fraction
* @return the instance
*/
public static InterpolatedStrikeSmileDeltaTermStructure of(
DoubleArray expiries,
DoubleArray delta,
DoubleMatrix volatility,
DayCount dayCount) {
return of(expiries, delta, volatility, dayCount, TIME_SQUARE, FLAT, FLAT, LINEAR, FLAT, FLAT);
}
/**
* Obtains volatility term structure from expiry times, delta values and volatilities
* with strike interpolator and extrapolators specified.
*
* The market date consists of time to expiry, delta and volatility.
* The delta must be positive and sorted in ascending order.
* The range of delta is common to all time to expiry.
*
* {@code volatility} should be {@code n * (2 * m + 1)}, where {@code n} is the length of {@code expiry}
* and {@code m} is the length of {@code delta}.
*
* The time dimension will use 'TimeSquare' interpolation with flat extrapolation.
*
* @param expiries the expiry times of individual volatility smiles
* @param delta the delta values
* @param volatility the volatilities
* @param dayCount the day count used for the expiry year-fraction
* @param strikeInterpolator interpolator used in the strike dimension
* @param strikeExtrapolatorLeft left extrapolator used in the strike dimension
* @param strikeExtrapolatorRight right extrapolator used in the strike dimension
* @return the instance
*/
public static InterpolatedStrikeSmileDeltaTermStructure of(
DoubleArray expiries,
DoubleArray delta,
DoubleMatrix volatility,
DayCount dayCount,
CurveInterpolator strikeInterpolator,
CurveExtrapolator strikeExtrapolatorLeft,
CurveExtrapolator strikeExtrapolatorRight) {
return of(
expiries,
delta,
volatility,
dayCount,
TIME_SQUARE,
FLAT,
FLAT,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight);
}
/**
* Obtains volatility term structure from expiry times, delta values and volatilities
* with interpolator and extrapolators fully specified.
*
* The market date consists of time to expiry, delta and volatility.
* The delta must be positive and sorted in ascending order.
* The range of delta is common to all time to expiry.
*
* {@code volatility} should be {@code n * (2 * m + 1)}, where {@code n} is the length of {@code expiry}
* and {@code m} is the length of {@code delta}.
*
* @param expiries the expiry times of individual volatility smiles
* @param delta the delta values
* @param volatility the volatilities
* @param dayCount the day count used for the expiry year-fraction
* @param timeInterpolator interpolator used in the time dimension
* @param timeExtrapolatorLeft left extrapolator used in the time dimension
* @param timeExtrapolatorRight right extrapolator used in the time dimension
* @param strikeInterpolator interpolator used in the strike dimension
* @param strikeExtrapolatorLeft left extrapolator used in the strike dimension
* @param strikeExtrapolatorRight right extrapolator used in the strike dimension
* @return the instance
*/
public static InterpolatedStrikeSmileDeltaTermStructure of(
DoubleArray expiries,
DoubleArray delta,
DoubleMatrix volatility,
DayCount dayCount,
CurveInterpolator timeInterpolator,
CurveExtrapolator timeExtrapolatorLeft,
CurveExtrapolator timeExtrapolatorRight,
CurveInterpolator strikeInterpolator,
CurveExtrapolator strikeExtrapolatorLeft,
CurveExtrapolator strikeExtrapolatorRight) {
ArgChecker.notNull(delta, "delta");
ArgChecker.notNull(volatility, "volatility");
ArgChecker.notNull(expiries, "expiries");
ArgChecker.notNull(dayCount, "dayCount");
ArgChecker.isTrue(delta.size() > 0,
"Need more than one volatility value to perform strike interpolation");
int nbExp = expiries.size();
ArgChecker.isTrue(volatility.rowCount() == nbExp,
"Volatility array length {} should be equal to the number of expiries {}", volatility.rowCount(), nbExp);
ArgChecker.isTrue(volatility.columnCount() == 2 * delta.size() + 1,
"Volatility array {} should be equal to (2 * number of deltas) + 1, have {}",
volatility.columnCount(), 2 * delta.size() + 1);
ImmutableList.Builder vt = ImmutableList.builder();
for (int loopexp = 0; loopexp < nbExp; loopexp++) {
vt.add(SmileDeltaParameters.of(expiries.get(loopexp), delta, volatility.row(loopexp)));
}
return new InterpolatedStrikeSmileDeltaTermStructure(
vt.build(),
dayCount,
timeInterpolator,
timeExtrapolatorLeft,
timeExtrapolatorRight,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight,
expiries);
}
//-------------------------------------------------------------------------
/**
* Obtains volatility term structure from expiry times, delta values, ATM volatilities, risk reversal figures and
* strangle figures.
*
* The range of delta is common to all time to expiry.
* {@code riskReversal} and {@code strangle} should be {@code n * m}, and the length of {@code atm} should {@code n},
* where {@code n} is the length of {@code expiry} and {@code m} is the length of {@code delta}.
*
* The time dimension will use 'TimeSquare' interpolation with flat extrapolation.
* The strike dimension will use 'Linear' interpolation with flat extrapolation.
*
* @param expiries the expiry times of individual volatility smiles
* @param delta the delta values
* @param atm the ATM volatilities
* @param riskReversal the risk reversal figures
* @param strangle the strangle figures
* @param dayCount the day count used for the expiry year-fraction
* @return the instance
*/
public static InterpolatedStrikeSmileDeltaTermStructure of(
DoubleArray expiries,
DoubleArray delta,
DoubleArray atm,
DoubleMatrix riskReversal,
DoubleMatrix strangle,
DayCount dayCount) {
return of(expiries, delta, atm, riskReversal, strangle, dayCount, TIME_SQUARE, FLAT, FLAT, LINEAR, FLAT, FLAT);
}
/**
* Obtains volatility term structure from expiry times, delta values, ATM volatilities, risk reversal figures and
* strangle figures with strike interpolator and extrapolators specified.
*
* The range of delta is common to all time to expiry.
* {@code riskReversal} and {@code strangle} should be {@code n * m}, and the length of {@code atm} should {@code n},
* where {@code n} is the length of {@code expiry} and {@code m} is the length of {@code delta}.
*
* The time dimension will use 'TimeSquare' interpolation with flat extrapolation.
*
* @param expiries the expiry times of individual volatility smiles
* @param delta the delta values
* @param atm the ATM volatilities
* @param riskReversal the risk reversal figures
* @param strangle the strangle figures
* @param dayCount the day count used for the expiry year-fraction
* @param strikeInterpolator interpolator used in the strike dimension
* @param strikeExtrapolatorLeft left extrapolator used in the strike dimension
* @param strikeExtrapolatorRight right extrapolator used in the strike dimension
* @return the instance
*/
public static InterpolatedStrikeSmileDeltaTermStructure of(
DoubleArray expiries,
DoubleArray delta,
DoubleArray atm,
DoubleMatrix riskReversal,
DoubleMatrix strangle,
DayCount dayCount,
CurveInterpolator strikeInterpolator,
CurveExtrapolator strikeExtrapolatorLeft,
CurveExtrapolator strikeExtrapolatorRight) {
return of(
expiries,
delta,
atm,
riskReversal,
strangle,
dayCount,
TIME_SQUARE,
FLAT,
FLAT,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight);
}
/**
* Obtains volatility term structure from expiry times, delta values, ATM volatilities, risk reversal figures and
* strangle figures with interpolator and extrapolators fully specified.
*
* The range of delta is common to all time to expiry.
* {@code riskReversal} and {@code strangle} should be {@code n * m}, and the length of {@code atm} should {@code n},
* where {@code n} is the length of {@code expiry} and {@code m} is the length of {@code delta}.
*
* @param expiries the expiry times of individual volatility smiles
* @param delta the delta values
* @param atm the ATM volatilities
* @param riskReversal the risk reversal figures
* @param strangle the strangle figures
* @param dayCount the day count used for the expiry year-fraction
* @param timeInterpolator interpolator used in the time dimension
* @param timeExtrapolatorLeft left extrapolator used in the time dimension
* @param timeExtrapolatorRight right extrapolator used in the time dimension
* @param strikeInterpolator interpolator used in the strike dimension
* @param strikeExtrapolatorLeft left extrapolator used in the strike dimension
* @param strikeExtrapolatorRight right extrapolator used in the strike dimension
* @return the instance
*/
public static InterpolatedStrikeSmileDeltaTermStructure of(
DoubleArray expiries,
DoubleArray delta,
DoubleArray atm,
DoubleMatrix riskReversal,
DoubleMatrix strangle,
DayCount dayCount,
CurveInterpolator timeInterpolator,
CurveExtrapolator timeExtrapolatorLeft,
CurveExtrapolator timeExtrapolatorRight,
CurveInterpolator strikeInterpolator,
CurveExtrapolator strikeExtrapolatorLeft,
CurveExtrapolator strikeExtrapolatorRight) {
ArgChecker.notNull(expiries, "expiries");
ArgChecker.notNull(delta, "delta");
ArgChecker.notNull(atm, "ATM");
ArgChecker.notNull(riskReversal, "risk reversal");
ArgChecker.notNull(strangle, "strangle");
ArgChecker.notNull(dayCount, "dayCount");
int nbExp = expiries.size();
ArgChecker.isTrue(atm.size() == nbExp, "ATM length should be coherent with time to expiry length");
ArgChecker.isTrue(riskReversal.rowCount() == nbExp,
"Risk reversal length should be coherent with time to expiry length");
ArgChecker.isTrue(strangle.rowCount() == nbExp, "Strangle length should be coherent with time to expiry length");
ArgChecker.isTrue(riskReversal.columnCount() == delta.size(),
"Risk reversal size should be coherent with time to delta length");
ArgChecker.isTrue(strangle.columnCount() == delta.size(),
"Strangle size should be coherent with time to delta length");
ImmutableList.Builder vt = ImmutableList.builder();
for (int loopexp = 0; loopexp < nbExp; loopexp++) {
vt.add(SmileDeltaParameters.of(
expiries.get(loopexp),
atm.get(loopexp),
delta,
riskReversal.row(loopexp),
strangle.row(loopexp)));
}
return new InterpolatedStrikeSmileDeltaTermStructure(
vt.build(),
dayCount,
timeInterpolator,
timeExtrapolatorLeft,
timeExtrapolatorRight,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight,
expiries);
}
//-------------------------------------------------------------------------
@ImmutableConstructor
private InterpolatedStrikeSmileDeltaTermStructure(
List volatilityTerm,
DayCount dayCount,
CurveInterpolator timeInterpolator,
CurveExtrapolator timeExtrapolatorLeft,
CurveExtrapolator timeExtrapolatorRight,
CurveInterpolator strikeInterpolator,
CurveExtrapolator strikeExtrapolatorLeft,
CurveExtrapolator strikeExtrapolatorRight) {
this(volatilityTerm,
dayCount,
timeInterpolator,
timeExtrapolatorLeft,
timeExtrapolatorRight,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight,
DoubleArray.copyOf(volatilityTerm.stream().map(vt -> vt.getExpiry()).collect(toList())));
}
private InterpolatedStrikeSmileDeltaTermStructure(
List volatilityTerm,
DayCount dayCount,
CurveInterpolator timeInterpolator,
CurveExtrapolator timeExtrapolatorLeft,
CurveExtrapolator timeExtrapolatorRight,
CurveInterpolator strikeInterpolator,
CurveExtrapolator strikeExtrapolatorLeft,
CurveExtrapolator strikeExtrapolatorRight,
DoubleArray expiries) {
JodaBeanUtils.notNull(volatilityTerm, "volatilityTerm");
JodaBeanUtils.notNull(dayCount, "dayCount");
JodaBeanUtils.notNull(timeInterpolator, "timeInterpolator");
JodaBeanUtils.notNull(timeExtrapolatorLeft, "timeExtrapolatorLeft");
JodaBeanUtils.notNull(timeExtrapolatorRight, "timeExtrapolatorRight");
JodaBeanUtils.notNull(strikeInterpolator, "strikeInterpolator");
JodaBeanUtils.notNull(strikeExtrapolatorLeft, "strikeExtrapolatorLeft");
JodaBeanUtils.notNull(strikeExtrapolatorRight, "strikeExtrapolatorRight");
this.volatilityTerm = ImmutableList.copyOf(volatilityTerm);
this.dayCount = dayCount;
this.timeExtrapolatorLeft = timeExtrapolatorLeft;
this.timeInterpolator = timeInterpolator;
this.timeExtrapolatorRight = timeExtrapolatorRight;
this.strikeExtrapolatorLeft = strikeExtrapolatorLeft;
this.strikeInterpolator = strikeInterpolator;
this.strikeExtrapolatorRight = strikeExtrapolatorRight;
this.expiries = expiries;
this.paramCombiner = ParameterizedDataCombiner.of(volatilityTerm);
}
private Object readResolve() {
return new InterpolatedStrikeSmileDeltaTermStructure(
volatilityTerm,
dayCount,
timeInterpolator,
timeExtrapolatorLeft,
timeExtrapolatorRight,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight);
}
//-------------------------------------------------------------------------
@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 InterpolatedStrikeSmileDeltaTermStructure withParameter(int parameterIndex, double newValue) {
List updated = paramCombiner.withParameter(SmileDeltaParameters.class, parameterIndex, newValue);
return new InterpolatedStrikeSmileDeltaTermStructure(
updated,
dayCount,
timeInterpolator,
timeExtrapolatorLeft,
timeExtrapolatorRight,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight);
}
@Override
public InterpolatedStrikeSmileDeltaTermStructure withPerturbation(ParameterPerturbation perturbation) {
List updated = paramCombiner.withPerturbation(SmileDeltaParameters.class, perturbation);
return new InterpolatedStrikeSmileDeltaTermStructure(
updated,
dayCount,
timeInterpolator,
timeExtrapolatorLeft,
timeExtrapolatorRight,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight);
}
//-------------------------------------------------------------------------
@Override
public DoubleArray getExpiries() {
return expiries;
}
@Override
public List> getExpiryTenors() {
return volatilityTerm.stream()
.map(smileDeltaParams -> smileDeltaParams.getExpiryTenor())
.collect(toImmutableList());
}
//-------------------------------------------------------------------------
@Override
public double volatility(double time, double strike, double forward) {
ArgChecker.isTrue(time >= 0, "Positive time");
SmileDeltaParameters smile = smileForExpiry(time);
DoubleArray strikes = smile.strike(forward);
BoundCurveInterpolator bound = strikeInterpolator.bind(
strikes, smile.getVolatility(), strikeExtrapolatorLeft, strikeExtrapolatorRight);
return bound.interpolate(strike);
}
@Override
public VolatilityAndBucketedSensitivities volatilityAndSensitivities(double time, double strike, double forward) {
ArgChecker.isTrue(time >= 0, "Positive time");
SmileDeltaParameters smile = smileForExpiry(time);
DoubleArray strikes = smile.strike(forward);
BoundCurveInterpolator bound = strikeInterpolator.bind(
strikes, smile.getVolatility(), strikeExtrapolatorLeft, strikeExtrapolatorRight);
double volatility = bound.interpolate(strike);
DoubleArray smileVolatilityBar = bound.parameterSensitivity(strike);
SmileAndBucketedSensitivities smileAndSensitivities = smileAndSensitivitiesForExpiry(time, smileVolatilityBar);
return VolatilityAndBucketedSensitivities.of(volatility, smileAndSensitivities.getSensitivities());
}
@Override
public ValueDerivatives partialFirstDerivatives(double expiry, double strike, double forward) {
ArgChecker.isTrue(expiry >= 0, "Positive time");
SmileDeltaParameters smile = smileForExpiry(expiry);
DoubleArray strikes = smile.strike(forward);
BoundCurveInterpolator volBound = strikeInterpolator.bind(
strikes, smile.getVolatility(), strikeExtrapolatorLeft, strikeExtrapolatorRight);
double vol = volBound.interpolate(strike);
// vol derivative to strike
double dVoldStrike = volBound.firstDerivative(strike);
DoubleArray smileVolsDerivativeToExpiry = smileVolsDerivativeToExpiry(expiry);
BoundCurveInterpolator smileVolDerivativeBound = strikeInterpolator.bind(
strikes, smileVolsDerivativeToExpiry, strikeExtrapolatorLeft, strikeExtrapolatorRight);
double dSmileVoldExpiry = smileVolDerivativeBound.interpolate(strike);
// strike derivative to time
DoubleArray impliedStrikesDerivativeToTime = smile.impliedStrikesDerivativeToExpiry(forward);
DoubleArray impliedStrikesDerivativeToSmileVols = smile.impliedStrikesDerivativeToSmileVols(forward);
BoundCurveInterpolator impliedVolDerivativeTimeBound = strikeInterpolator.bind(
strikes, impliedStrikesDerivativeToTime, strikeExtrapolatorLeft, strikeExtrapolatorRight);
BoundCurveInterpolator impliedVolDerivativeSmileVolBound = strikeInterpolator.bind(
strikes, impliedStrikesDerivativeToSmileVols, strikeExtrapolatorLeft, strikeExtrapolatorRight);
double dImpliedStrikedExpiry = impliedVolDerivativeTimeBound.interpolate(strike);
double dImpliedStrikedSmileVol = impliedVolDerivativeSmileVolBound.interpolate(strike);
double dStrikedExpiry = dImpliedStrikedSmileVol * dSmileVoldExpiry + dImpliedStrikedExpiry;
// take negative of dVoldStrike as increase in x when plotted with same y values is equivalent of negative x shift
double negativedVoldStrike = -1d * dVoldStrike;
double dVoldExpiry = dStrikedExpiry * negativedVoldStrike + dSmileVoldExpiry;
return ValueDerivatives.of(vol, DoubleArray.of(dVoldExpiry, dVoldStrike));
}
//-------------------------------------------------------------------------
@Override
public SmileDeltaParameters smileForExpiry(double expiry) {
int nbVol = getStrikeCount();
int nbTime = getSmileCount();
ArgChecker.isTrue(nbTime > 1, "Need more than one time value to perform interpolation");
double[] volatilityT = new double[nbVol];
for (int loopvol = 0; loopvol < nbVol; loopvol++) {
double[] volDelta = new double[nbTime];
for (int looptime = 0; looptime < nbTime; looptime++) {
volDelta[looptime] = volatilityTerm.get(looptime).getVolatility().get(loopvol);
}
BoundCurveInterpolator bound = timeInterpolator.bind(
getExpiries(), DoubleArray.ofUnsafe(volDelta), timeExtrapolatorLeft, timeExtrapolatorRight);
volatilityT[loopvol] = bound.interpolate(expiry);
}
return SmileDeltaParameters.of(expiry, getDelta(), DoubleArray.ofUnsafe(volatilityT));
}
//-------------------------------------------------------------------------
private DoubleArray smileVolsDerivativeToExpiry(double expiry) {
int nbVol = getStrikeCount();
int nbTime = getSmileCount();
ArgChecker.isTrue(nbTime > 1, "Need more than one time value to perform interpolation");
double[] derivatives = new double[nbVol];
for (int loopvol = 0; loopvol < nbVol; loopvol++) {
double[] volDelta = new double[nbTime];
for (int looptime = 0; looptime < nbTime; looptime++) {
volDelta[looptime] = volatilityTerm.get(looptime).getVolatility().get(loopvol);
}
BoundCurveInterpolator bound = timeInterpolator.bind(
getExpiries(), DoubleArray.ofUnsafe(volDelta), timeExtrapolatorLeft, timeExtrapolatorRight);
derivatives[loopvol] = bound.firstDerivative(expiry);
}
return DoubleArray.ofUnsafe(derivatives);
}
@Override
public SmileAndBucketedSensitivities smileAndSensitivitiesForExpiry(
double expiry,
DoubleArray volatilityAtTimeSensitivity) {
int nbVol = getStrikeCount();
ArgChecker.isTrue(volatilityAtTimeSensitivity.size() == nbVol, "Sensitivity with incorrect size");
ArgChecker.isTrue(nbVol > 1, "Need more than one volatility value to perform interpolation");
int nbTime = getSmileCount();
ArgChecker.isTrue(nbTime > 1, "Need more than one time value to perform interpolation");
double[] volatilityT = new double[nbVol];
double[][] volatilitySensitivity = new double[nbTime][nbVol];
for (int loopvol = 0; loopvol < nbVol; loopvol++) {
double[] volDelta = new double[nbTime];
for (int looptime = 0; looptime < nbTime; looptime++) {
volDelta[looptime] = volatilityTerm.get(looptime).getVolatility().get(loopvol);
}
BoundCurveInterpolator bound = timeInterpolator.bind(
getExpiries(), DoubleArray.ofUnsafe(volDelta), timeExtrapolatorLeft, timeExtrapolatorRight);
DoubleArray volatilitySensitivityVol = bound.parameterSensitivity(expiry);
for (int looptime = 0; looptime < nbTime; looptime++) {
volatilitySensitivity[looptime][loopvol] =
volatilitySensitivityVol.get(looptime) * volatilityAtTimeSensitivity.get(loopvol);
}
volatilityT[loopvol] = bound.interpolate(expiry);
}
SmileDeltaParameters smile = SmileDeltaParameters.of(expiry, getDelta(), DoubleArray.ofUnsafe(volatilityT));
return SmileAndBucketedSensitivities.of(smile, DoubleMatrix.ofUnsafe(volatilitySensitivity));
}
//------------------------- AUTOGENERATED START -------------------------
/**
* The meta-bean for {@code InterpolatedStrikeSmileDeltaTermStructure}.
* @return the meta-bean, not null
*/
public static InterpolatedStrikeSmileDeltaTermStructure.Meta meta() {
return InterpolatedStrikeSmileDeltaTermStructure.Meta.INSTANCE;
}
static {
MetaBean.register(InterpolatedStrikeSmileDeltaTermStructure.Meta.INSTANCE);
}
/**
* The serialization version id.
*/
private static final long serialVersionUID = 1L;
@Override
public InterpolatedStrikeSmileDeltaTermStructure.Meta metaBean() {
return InterpolatedStrikeSmileDeltaTermStructure.Meta.INSTANCE;
}
//-----------------------------------------------------------------------
/**
* Gets the smile description at the different time to expiry. All item should have the same deltas.
* @return the value of the property, not null
*/
@Override
public ImmutableList getVolatilityTerm() {
return volatilityTerm;
}
//-----------------------------------------------------------------------
/**
* Gets the day count convention used for the expiry.
* @return the value of the property, not null
*/
@Override
public DayCount getDayCount() {
return dayCount;
}
//-----------------------------------------------------------------------
/**
* Gets the interpolator used in the time dimension.
* @return the value of the property, not null
*/
public CurveInterpolator getTimeInterpolator() {
return timeInterpolator;
}
//-----------------------------------------------------------------------
/**
* Gets the left extrapolator used in the time dimension.
* @return the value of the property, not null
*/
public CurveExtrapolator getTimeExtrapolatorLeft() {
return timeExtrapolatorLeft;
}
//-----------------------------------------------------------------------
/**
* Gets the right extrapolator used in the time dimension.
* @return the value of the property, not null
*/
public CurveExtrapolator getTimeExtrapolatorRight() {
return timeExtrapolatorRight;
}
//-----------------------------------------------------------------------
/**
* Gets the interpolator used in the strike dimension.
* @return the value of the property, not null
*/
public CurveInterpolator getStrikeInterpolator() {
return strikeInterpolator;
}
//-----------------------------------------------------------------------
/**
* Gets the left extrapolator used in the strike dimension.
* @return the value of the property, not null
*/
public CurveExtrapolator getStrikeExtrapolatorLeft() {
return strikeExtrapolatorLeft;
}
//-----------------------------------------------------------------------
/**
* Gets the right extrapolator used in the strike dimension.
* @return the value of the property, not null
*/
public CurveExtrapolator getStrikeExtrapolatorRight() {
return strikeExtrapolatorRight;
}
//-----------------------------------------------------------------------
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj.getClass() == this.getClass()) {
InterpolatedStrikeSmileDeltaTermStructure other = (InterpolatedStrikeSmileDeltaTermStructure) obj;
return JodaBeanUtils.equal(volatilityTerm, other.volatilityTerm) &&
JodaBeanUtils.equal(dayCount, other.dayCount) &&
JodaBeanUtils.equal(timeInterpolator, other.timeInterpolator) &&
JodaBeanUtils.equal(timeExtrapolatorLeft, other.timeExtrapolatorLeft) &&
JodaBeanUtils.equal(timeExtrapolatorRight, other.timeExtrapolatorRight) &&
JodaBeanUtils.equal(strikeInterpolator, other.strikeInterpolator) &&
JodaBeanUtils.equal(strikeExtrapolatorLeft, other.strikeExtrapolatorLeft) &&
JodaBeanUtils.equal(strikeExtrapolatorRight, other.strikeExtrapolatorRight);
}
return false;
}
@Override
public int hashCode() {
int hash = getClass().hashCode();
hash = hash * 31 + JodaBeanUtils.hashCode(volatilityTerm);
hash = hash * 31 + JodaBeanUtils.hashCode(dayCount);
hash = hash * 31 + JodaBeanUtils.hashCode(timeInterpolator);
hash = hash * 31 + JodaBeanUtils.hashCode(timeExtrapolatorLeft);
hash = hash * 31 + JodaBeanUtils.hashCode(timeExtrapolatorRight);
hash = hash * 31 + JodaBeanUtils.hashCode(strikeInterpolator);
hash = hash * 31 + JodaBeanUtils.hashCode(strikeExtrapolatorLeft);
hash = hash * 31 + JodaBeanUtils.hashCode(strikeExtrapolatorRight);
return hash;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(288);
buf.append("InterpolatedStrikeSmileDeltaTermStructure{");
buf.append("volatilityTerm").append('=').append(JodaBeanUtils.toString(volatilityTerm)).append(',').append(' ');
buf.append("dayCount").append('=').append(JodaBeanUtils.toString(dayCount)).append(',').append(' ');
buf.append("timeInterpolator").append('=').append(JodaBeanUtils.toString(timeInterpolator)).append(',').append(' ');
buf.append("timeExtrapolatorLeft").append('=').append(JodaBeanUtils.toString(timeExtrapolatorLeft)).append(',').append(' ');
buf.append("timeExtrapolatorRight").append('=').append(JodaBeanUtils.toString(timeExtrapolatorRight)).append(',').append(' ');
buf.append("strikeInterpolator").append('=').append(JodaBeanUtils.toString(strikeInterpolator)).append(',').append(' ');
buf.append("strikeExtrapolatorLeft").append('=').append(JodaBeanUtils.toString(strikeExtrapolatorLeft)).append(',').append(' ');
buf.append("strikeExtrapolatorRight").append('=').append(JodaBeanUtils.toString(strikeExtrapolatorRight));
buf.append('}');
return buf.toString();
}
//-----------------------------------------------------------------------
/**
* The meta-bean for {@code InterpolatedStrikeSmileDeltaTermStructure}.
*/
public static final class Meta extends DirectMetaBean {
/**
* The singleton instance of the meta-bean.
*/
static final Meta INSTANCE = new Meta();
/**
* The meta-property for the {@code volatilityTerm} property.
*/
@SuppressWarnings({"unchecked", "rawtypes" })
private final MetaProperty> volatilityTerm = DirectMetaProperty.ofImmutable(
this, "volatilityTerm", InterpolatedStrikeSmileDeltaTermStructure.class, (Class) ImmutableList.class);
/**
* The meta-property for the {@code dayCount} property.
*/
private final MetaProperty dayCount = DirectMetaProperty.ofImmutable(
this, "dayCount", InterpolatedStrikeSmileDeltaTermStructure.class, DayCount.class);
/**
* The meta-property for the {@code timeInterpolator} property.
*/
private final MetaProperty timeInterpolator = DirectMetaProperty.ofImmutable(
this, "timeInterpolator", InterpolatedStrikeSmileDeltaTermStructure.class, CurveInterpolator.class);
/**
* The meta-property for the {@code timeExtrapolatorLeft} property.
*/
private final MetaProperty timeExtrapolatorLeft = DirectMetaProperty.ofImmutable(
this, "timeExtrapolatorLeft", InterpolatedStrikeSmileDeltaTermStructure.class, CurveExtrapolator.class);
/**
* The meta-property for the {@code timeExtrapolatorRight} property.
*/
private final MetaProperty timeExtrapolatorRight = DirectMetaProperty.ofImmutable(
this, "timeExtrapolatorRight", InterpolatedStrikeSmileDeltaTermStructure.class, CurveExtrapolator.class);
/**
* The meta-property for the {@code strikeInterpolator} property.
*/
private final MetaProperty strikeInterpolator = DirectMetaProperty.ofImmutable(
this, "strikeInterpolator", InterpolatedStrikeSmileDeltaTermStructure.class, CurveInterpolator.class);
/**
* The meta-property for the {@code strikeExtrapolatorLeft} property.
*/
private final MetaProperty strikeExtrapolatorLeft = DirectMetaProperty.ofImmutable(
this, "strikeExtrapolatorLeft", InterpolatedStrikeSmileDeltaTermStructure.class, CurveExtrapolator.class);
/**
* The meta-property for the {@code strikeExtrapolatorRight} property.
*/
private final MetaProperty strikeExtrapolatorRight = DirectMetaProperty.ofImmutable(
this, "strikeExtrapolatorRight", InterpolatedStrikeSmileDeltaTermStructure.class, CurveExtrapolator.class);
/**
* The meta-properties.
*/
private final Map> metaPropertyMap$ = new DirectMetaPropertyMap(
this, null,
"volatilityTerm",
"dayCount",
"timeInterpolator",
"timeExtrapolatorLeft",
"timeExtrapolatorRight",
"strikeInterpolator",
"strikeExtrapolatorLeft",
"strikeExtrapolatorRight");
/**
* Restricted constructor.
*/
private Meta() {
}
@Override
protected MetaProperty> metaPropertyGet(String propertyName) {
switch (propertyName.hashCode()) {
case 70074929: // volatilityTerm
return volatilityTerm;
case 1905311443: // dayCount
return dayCount;
case -587914188: // timeInterpolator
return timeInterpolator;
case -286652761: // timeExtrapolatorLeft
return timeExtrapolatorLeft;
case -290640004: // timeExtrapolatorRight
return timeExtrapolatorRight;
case 815202713: // strikeInterpolator
return strikeInterpolator;
case -1176196724: // strikeExtrapolatorLeft
return strikeExtrapolatorLeft;
case -2096699081: // strikeExtrapolatorRight
return strikeExtrapolatorRight;
}
return super.metaPropertyGet(propertyName);
}
@Override
public BeanBuilder extends InterpolatedStrikeSmileDeltaTermStructure> builder() {
return new InterpolatedStrikeSmileDeltaTermStructure.Builder();
}
@Override
public Class extends InterpolatedStrikeSmileDeltaTermStructure> beanType() {
return InterpolatedStrikeSmileDeltaTermStructure.class;
}
@Override
public Map> metaPropertyMap() {
return metaPropertyMap$;
}
//-----------------------------------------------------------------------
/**
* The meta-property for the {@code volatilityTerm} property.
* @return the meta-property, not null
*/
public MetaProperty> volatilityTerm() {
return volatilityTerm;
}
/**
* The meta-property for the {@code dayCount} property.
* @return the meta-property, not null
*/
public MetaProperty dayCount() {
return dayCount;
}
/**
* The meta-property for the {@code timeInterpolator} property.
* @return the meta-property, not null
*/
public MetaProperty timeInterpolator() {
return timeInterpolator;
}
/**
* The meta-property for the {@code timeExtrapolatorLeft} property.
* @return the meta-property, not null
*/
public MetaProperty timeExtrapolatorLeft() {
return timeExtrapolatorLeft;
}
/**
* The meta-property for the {@code timeExtrapolatorRight} property.
* @return the meta-property, not null
*/
public MetaProperty timeExtrapolatorRight() {
return timeExtrapolatorRight;
}
/**
* The meta-property for the {@code strikeInterpolator} property.
* @return the meta-property, not null
*/
public MetaProperty strikeInterpolator() {
return strikeInterpolator;
}
/**
* The meta-property for the {@code strikeExtrapolatorLeft} property.
* @return the meta-property, not null
*/
public MetaProperty strikeExtrapolatorLeft() {
return strikeExtrapolatorLeft;
}
/**
* The meta-property for the {@code strikeExtrapolatorRight} property.
* @return the meta-property, not null
*/
public MetaProperty strikeExtrapolatorRight() {
return strikeExtrapolatorRight;
}
//-----------------------------------------------------------------------
@Override
protected Object propertyGet(Bean bean, String propertyName, boolean quiet) {
switch (propertyName.hashCode()) {
case 70074929: // volatilityTerm
return ((InterpolatedStrikeSmileDeltaTermStructure) bean).getVolatilityTerm();
case 1905311443: // dayCount
return ((InterpolatedStrikeSmileDeltaTermStructure) bean).getDayCount();
case -587914188: // timeInterpolator
return ((InterpolatedStrikeSmileDeltaTermStructure) bean).getTimeInterpolator();
case -286652761: // timeExtrapolatorLeft
return ((InterpolatedStrikeSmileDeltaTermStructure) bean).getTimeExtrapolatorLeft();
case -290640004: // timeExtrapolatorRight
return ((InterpolatedStrikeSmileDeltaTermStructure) bean).getTimeExtrapolatorRight();
case 815202713: // strikeInterpolator
return ((InterpolatedStrikeSmileDeltaTermStructure) bean).getStrikeInterpolator();
case -1176196724: // strikeExtrapolatorLeft
return ((InterpolatedStrikeSmileDeltaTermStructure) bean).getStrikeExtrapolatorLeft();
case -2096699081: // strikeExtrapolatorRight
return ((InterpolatedStrikeSmileDeltaTermStructure) bean).getStrikeExtrapolatorRight();
}
return super.propertyGet(bean, propertyName, quiet);
}
@Override
protected void propertySet(Bean bean, String propertyName, Object newValue, boolean quiet) {
metaProperty(propertyName);
if (quiet) {
return;
}
throw new UnsupportedOperationException("Property cannot be written: " + propertyName);
}
}
//-----------------------------------------------------------------------
/**
* The bean-builder for {@code InterpolatedStrikeSmileDeltaTermStructure}.
*/
private static final class Builder extends DirectPrivateBeanBuilder {
private List volatilityTerm = ImmutableList.of();
private DayCount dayCount;
private CurveInterpolator timeInterpolator;
private CurveExtrapolator timeExtrapolatorLeft;
private CurveExtrapolator timeExtrapolatorRight;
private CurveInterpolator strikeInterpolator;
private CurveExtrapolator strikeExtrapolatorLeft;
private CurveExtrapolator strikeExtrapolatorRight;
/**
* Restricted constructor.
*/
private Builder() {
}
//-----------------------------------------------------------------------
@Override
public Object get(String propertyName) {
switch (propertyName.hashCode()) {
case 70074929: // volatilityTerm
return volatilityTerm;
case 1905311443: // dayCount
return dayCount;
case -587914188: // timeInterpolator
return timeInterpolator;
case -286652761: // timeExtrapolatorLeft
return timeExtrapolatorLeft;
case -290640004: // timeExtrapolatorRight
return timeExtrapolatorRight;
case 815202713: // strikeInterpolator
return strikeInterpolator;
case -1176196724: // strikeExtrapolatorLeft
return strikeExtrapolatorLeft;
case -2096699081: // strikeExtrapolatorRight
return strikeExtrapolatorRight;
default:
throw new NoSuchElementException("Unknown property: " + propertyName);
}
}
@SuppressWarnings("unchecked")
@Override
public Builder set(String propertyName, Object newValue) {
switch (propertyName.hashCode()) {
case 70074929: // volatilityTerm
this.volatilityTerm = (List) newValue;
break;
case 1905311443: // dayCount
this.dayCount = (DayCount) newValue;
break;
case -587914188: // timeInterpolator
this.timeInterpolator = (CurveInterpolator) newValue;
break;
case -286652761: // timeExtrapolatorLeft
this.timeExtrapolatorLeft = (CurveExtrapolator) newValue;
break;
case -290640004: // timeExtrapolatorRight
this.timeExtrapolatorRight = (CurveExtrapolator) newValue;
break;
case 815202713: // strikeInterpolator
this.strikeInterpolator = (CurveInterpolator) newValue;
break;
case -1176196724: // strikeExtrapolatorLeft
this.strikeExtrapolatorLeft = (CurveExtrapolator) newValue;
break;
case -2096699081: // strikeExtrapolatorRight
this.strikeExtrapolatorRight = (CurveExtrapolator) newValue;
break;
default:
throw new NoSuchElementException("Unknown property: " + propertyName);
}
return this;
}
@Override
public InterpolatedStrikeSmileDeltaTermStructure build() {
return new InterpolatedStrikeSmileDeltaTermStructure(
volatilityTerm,
dayCount,
timeInterpolator,
timeExtrapolatorLeft,
timeExtrapolatorRight,
strikeInterpolator,
strikeExtrapolatorLeft,
strikeExtrapolatorRight);
}
//-----------------------------------------------------------------------
@Override
public String toString() {
StringBuilder buf = new StringBuilder(288);
buf.append("InterpolatedStrikeSmileDeltaTermStructure.Builder{");
buf.append("volatilityTerm").append('=').append(JodaBeanUtils.toString(volatilityTerm)).append(',').append(' ');
buf.append("dayCount").append('=').append(JodaBeanUtils.toString(dayCount)).append(',').append(' ');
buf.append("timeInterpolator").append('=').append(JodaBeanUtils.toString(timeInterpolator)).append(',').append(' ');
buf.append("timeExtrapolatorLeft").append('=').append(JodaBeanUtils.toString(timeExtrapolatorLeft)).append(',').append(' ');
buf.append("timeExtrapolatorRight").append('=').append(JodaBeanUtils.toString(timeExtrapolatorRight)).append(',').append(' ');
buf.append("strikeInterpolator").append('=').append(JodaBeanUtils.toString(strikeInterpolator)).append(',').append(' ');
buf.append("strikeExtrapolatorLeft").append('=').append(JodaBeanUtils.toString(strikeExtrapolatorLeft)).append(',').append(' ');
buf.append("strikeExtrapolatorRight").append('=').append(JodaBeanUtils.toString(strikeExtrapolatorRight));
buf.append('}');
return buf.toString();
}
}
//-------------------------- AUTOGENERATED END --------------------------
}