/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.volatility.surface;
import java.util.Arrays;
import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang.ObjectUtils;
import com.opengamma.analytics.financial.model.option.definition.SmileDeltaParameters;
import com.opengamma.analytics.financial.model.volatility.SmileAndBucketedSensitivities;
import com.opengamma.analytics.financial.model.volatility.VolatilityAndBucketedSensitivities;
import com.opengamma.analytics.financial.model.volatility.VolatilityAndBucketedSensitivitiesModel;
import com.opengamma.analytics.financial.model.volatility.curve.BlackForexTermStructureParameters;
import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve;
import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory;
import com.opengamma.analytics.math.interpolation.Interpolator1D;
import com.opengamma.analytics.math.interpolation.Interpolator1DFactory;
import com.opengamma.analytics.math.interpolation.data.ArrayInterpolator1DDataBundle;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.tuple.Triple;
/**
* Class describing the a term structure of smiles from ATM, risk reversal and strangle as used in Forex market.
* The delta used is the delta with respect to forward.
*/
public class SmileDeltaTermStructureParameters implements VolatilityAndBucketedSensitivitiesModel<Triple<Double, Double, Double>> {
/**
* The time to expiration in the term structure.
*/
private final double[] _timeToExpiration;
/**
* The smile description at the different time to expiration. All item should have the same deltas.
*/
private final SmileDeltaParameters[] _volatilityTerm;
/**
* The interpolator/extrapolator used in the expiry dimension.
*/
private final Interpolator1D _timeInterpolator;
/**
* The default interpolator: time square (total variance) with flat extrapolation.
*/
private static final Interpolator1D DEFAULT_INTERPOLATOR_EXPIRY = CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.TIME_SQUARE, Interpolator1DFactory.FLAT_EXTRAPOLATOR,
Interpolator1DFactory.FLAT_EXTRAPOLATOR);
/**
* Constructor from volatility term structure.
* @param volatilityTerm The volatility description at the different expiration.
*/
public SmileDeltaTermStructureParameters(final SmileDeltaParameters[] volatilityTerm) {
this(volatilityTerm, DEFAULT_INTERPOLATOR_EXPIRY);
}
/**
* Constructor from volatility term structure.
* @param volatilityTerm The volatility description at the different expiration.
* @param interpolator The time interpolator
*/
public SmileDeltaTermStructureParameters(final SmileDeltaParameters[] volatilityTerm, final Interpolator1D interpolator) {
ArgumentChecker.notNull(volatilityTerm, "Volatility term structure");
ArgumentChecker.notNull(interpolator, "interpolator");
_volatilityTerm = volatilityTerm;
final int nbExp = volatilityTerm.length;
_timeToExpiration = new double[nbExp];
for (int loopexp = 0; loopexp < nbExp; loopexp++) {
_timeToExpiration[loopexp] = _volatilityTerm[loopexp].getTimeToExpiry();
}
_timeInterpolator = interpolator;
}
/**
* Constructor from market data.
* @param timeToExpiration The time to expiration of each volatility smile, not null
* @param delta The delta at which the volatilities are given. Must be positive and sorted in ascending order. The put will have as delta the opposite of the numbers.
* Common to all time to expiration. Not null
* @param volatility The volatilities at each delta, not null
*/
public SmileDeltaTermStructureParameters(final double[] timeToExpiration, final double[] delta, final double[][] volatility) {
ArgumentChecker.notNull(timeToExpiration, "time to expiry");
ArgumentChecker.notNull(delta, "delta");
ArgumentChecker.notNull(volatility, "volatility");
final int nbExp = timeToExpiration.length;
ArgumentChecker.isTrue(volatility.length == nbExp, "Volatility array length {} should be equal to the number of expiries {}", volatility.length, nbExp);
ArgumentChecker.isTrue(volatility[0].length == 2 * delta.length + 1, "Volatility array {} should be equal to (2 * number of deltas) + 1, have {}", volatility[0].length, 2 * delta.length + 1);
_timeToExpiration = timeToExpiration;
_volatilityTerm = new SmileDeltaParameters[nbExp];
for (int loopexp = 0; loopexp < nbExp; loopexp++) {
_volatilityTerm[loopexp] = new SmileDeltaParameters(timeToExpiration[loopexp], delta, volatility[loopexp]);
}
_timeInterpolator = DEFAULT_INTERPOLATOR_EXPIRY;
ArgumentChecker.isTrue(_volatilityTerm[0].getVolatility().length > 1, "Need more than one volatility value to perform interpolation");
}
/**
* Constructor from market data. The default interpolator is used for the time dimension.
* @param timeToExpiration The time to expiration of each volatility smile.
* @param delta The delta at which the volatilities are given. Common to all time to expiration.
* @param atm The ATM volatilities for each time to expiration. The length should be equal to the length of timeToExpiration.
* @param riskReversal The risk reversal figures.
* @param strangle The strangle figures.
*/
public SmileDeltaTermStructureParameters(final double[] timeToExpiration, final double[] delta, final double[] atm, final double[][] riskReversal, final double[][] strangle) {
this(timeToExpiration, delta, atm, riskReversal, strangle, DEFAULT_INTERPOLATOR_EXPIRY);
}
/**
* Constructor from market data.
* @param timeToExpiration The time to expiration of each volatility smile, not null
* @param delta The delta at which the volatilities are given. Common to all time to expiration. Not null
* @param atm The ATM volatilities for each time to expiration. The length should be equal to the length of timeToExpiration. Not null
* @param riskReversal The risk reversal figures, not null.
* @param strangle The strangle figures, not null.
* @param timeInterpolator The interpolator to be used in the time dimension, not null.
*/
public SmileDeltaTermStructureParameters(final double[] timeToExpiration, final double[] delta, final double[] atm, final double[][] riskReversal, final double[][] strangle,
final Interpolator1D timeInterpolator) {
ArgumentChecker.notNull(timeToExpiration, "time to expiry");
ArgumentChecker.notNull(delta, "delta");
ArgumentChecker.notNull(atm, "ATM");
ArgumentChecker.notNull(riskReversal, "risk reversal");
ArgumentChecker.notNull(strangle, "strangle");
ArgumentChecker.notNull(timeInterpolator, "time interpolator");
final int nbExp = timeToExpiration.length;
ArgumentChecker.isTrue(atm.length == nbExp, "ATM length should be coherent with time to expiration length");
ArgumentChecker.isTrue(riskReversal.length == nbExp, "Risk reversal length should be coherent with time to expiration length");
ArgumentChecker.isTrue(strangle.length == nbExp, "Risk reversal length should be coherent with time to expiration length");
ArgumentChecker.isTrue(riskReversal[0].length == delta.length, "Risk reversal size should be coherent with time to delta length");
ArgumentChecker.isTrue(strangle[0].length == delta.length, "Risk reversal size should be coherent with time to delta length");
_timeToExpiration = timeToExpiration;
_volatilityTerm = new SmileDeltaParameters[nbExp];
for (int loopexp = 0; loopexp < nbExp; loopexp++) {
_volatilityTerm[loopexp] = new SmileDeltaParameters(timeToExpiration[loopexp], atm[loopexp], delta, riskReversal[loopexp], strangle[loopexp]);
}
_timeInterpolator = timeInterpolator;
ArgumentChecker.isTrue(_volatilityTerm[0].getVolatility().length > 1, "Need more than one volatility value to perform interpolation");
}
public SmileDeltaTermStructureParameters copy() {
return new SmileDeltaTermStructureParameters(getVolatilityTerm(), getTimeInterpolator());
}
/**
* Get smile at a given time. The smile is described by the volatilities at a given delta. The smile is obtained from the data by the given interpolator.
* @param time The time to expiration.
* @return The smile.
*/
public SmileDeltaParameters getSmileForTime(final double time) {
final int nbVol = _volatilityTerm[0].getVolatility().length;
final int nbTime = _timeToExpiration.length;
ArgumentChecker.isTrue(nbTime > 1, "Need more than one time value to perform interpolation");
final double[] volatilityT = new double[nbVol];
for (int loopvol = 0; loopvol < nbVol; loopvol++) {
final double[] volDelta = new double[nbTime];
for (int looptime = 0; looptime < nbTime; looptime++) {
volDelta[looptime] = _volatilityTerm[looptime].getVolatility()[loopvol];
}
final ArrayInterpolator1DDataBundle interpData = new ArrayInterpolator1DDataBundle(_timeToExpiration, volDelta, true);
volatilityT[loopvol] = _timeInterpolator.interpolate(interpData, time);
}
final SmileDeltaParameters smile = new SmileDeltaParameters(time, _volatilityTerm[0].getDelta(), volatilityT);
return smile;
}
/**
* Get the smile at a given time and the sensitivities with respect to the volatilities.
* @param time The time to expiration.
* @param volatilityAtTimeSensitivity The sensitivity to the volatilities of the smile at the given time.
* After the methods, it contains the volatility sensitivity to the data points.
* @return The smile
*/
public SmileAndBucketedSensitivities getSmileAndSensitivitiesForTime(final double time, final double[] volatilityAtTimeSensitivity) {
final int nbVol = _volatilityTerm[0].getVolatility().length;
ArgumentChecker.isTrue(volatilityAtTimeSensitivity.length == nbVol, "Sensitivity with incorrect size");
ArgumentChecker.isTrue(nbVol > 1, "Need more than one volatility value to perform interpolation");
final int nbTime = _timeToExpiration.length;
ArgumentChecker.isTrue(nbTime > 1, "Need more than one time value to perform interpolation");
final double[] volatilityT = new double[nbVol];
final double[][] volatilitySensitivity = new double[nbTime][nbVol];
for (int loopvol = 0; loopvol < nbVol; loopvol++) {
final double[] volDelta = new double[nbTime];
for (int looptime = 0; looptime < nbTime; looptime++) {
volDelta[looptime] = _volatilityTerm[looptime].getVolatility()[loopvol];
}
final ArrayInterpolator1DDataBundle interpData = new ArrayInterpolator1DDataBundle(_timeToExpiration, volDelta, true);
final double[] volatilitySensitivityVol = _timeInterpolator.getNodeSensitivitiesForValue(interpData, time);
for (int looptime = 0; looptime < nbTime; looptime++) {
volatilitySensitivity[looptime][loopvol] = volatilitySensitivityVol[looptime] * volatilityAtTimeSensitivity[loopvol];
}
volatilityT[loopvol] = _timeInterpolator.interpolate(interpData, time);
}
final SmileDeltaParameters smile = new SmileDeltaParameters(time, _volatilityTerm[0].getDelta(), volatilityT);
return new SmileAndBucketedSensitivities(smile, volatilitySensitivity);
}
/**
* Gets the times to expiration.
* @return The times.
*/
public double[] getTimeToExpiration() {
return _timeToExpiration;
}
/**
* Gets the number of expirations.
* @return The number of expirations.
*/
public int getNumberExpiration() {
return _timeToExpiration.length;
}
/**
* Gets the time interpolator
* @return The time interpolator
*/
public Interpolator1D getTimeInterpolator() {
return _timeInterpolator;
}
/**
* Gets the volatility smiles from delta.
* @return The volatility smiles.
*/
public SmileDeltaParameters[] getVolatilityTerm() {
return _volatilityTerm;
}
/**
* Gets the number of strikes (common to all dates).
* @return The number of strikes.
*/
public int getNumberStrike() {
return _volatilityTerm[0].getVolatility().length;
}
/**
* Gets delta (common to all time to expiration).
* @return The delta.
*/
public double[] getDelta() {
return _volatilityTerm[0].getDelta();
}
/**
* Gets put delta absolute value for all strikes. The ATM is 0.50 delta and the x call are transformed in 1-x put.
* @return The delta.
*/
public double[] getDeltaFull() {
final int nbDelta = _volatilityTerm[0].getDelta().length;
final double[] result = new double[2 * nbDelta + 1];
for (int loopdelta = 0; loopdelta < nbDelta; loopdelta++) {
result[loopdelta] = _volatilityTerm[0].getDelta()[loopdelta];
result[nbDelta + 1 + loopdelta] = 1.0 - _volatilityTerm[0].getDelta()[nbDelta - 1 - loopdelta];
}
result[nbDelta] = 0.50;
return result;
}
/**
* Get the volatility from a triple.
* @param tsf The Time, Strike, Forward triple, not null
* @return The volatility.
*/
@Override
public Double getVolatility(final Triple<Double, Double, Double> tsf) {
throw new NotImplementedException();
}
@Override
public VolatilityAndBucketedSensitivities getVolatilityAndSensitivities(final Triple<Double, Double, Double> tsf) {
throw new NotImplementedException();
}
public BlackForexTermStructureParameters toTermStructureOnlyData(final Interpolator1D interpolator) {
ArgumentChecker.notNull(interpolator, "interpolator");
final int n = _timeToExpiration.length;
final double[] timesToExpiry = new double[n];
System.arraycopy(_timeToExpiration, 0, timesToExpiry, 0, n);
final double[] vols = new double[n];
final int atmIndex = (_volatilityTerm[0].getVolatility().length - 1) / 2;
for (int i = 0; i < n; i++) {
vols[i] = _volatilityTerm[i].getVolatility()[atmIndex];
}
return new BlackForexTermStructureParameters(InterpolatedDoublesCurve.fromSorted(timesToExpiry, vols, interpolator));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(_timeToExpiration);
result = prime * result + Arrays.hashCode(_volatilityTerm);
result = prime * result + _timeInterpolator.hashCode();
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final SmileDeltaTermStructureParameters other = (SmileDeltaTermStructureParameters) obj;
if (!Arrays.equals(_timeToExpiration, other._timeToExpiration)) {
return false;
}
if (!Arrays.equals(_volatilityTerm, other._volatilityTerm)) {
return false;
}
if (!ObjectUtils.equals(_timeInterpolator, other._timeInterpolator)) {
return false;
}
return true;
}
}