/**
* Copyright (C) 2009 - 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.io.Serializable;
import java.util.List;
import org.apache.commons.lang.ObjectUtils;
import com.google.common.collect.ImmutableList;
import com.opengamma.analytics.financial.model.volatility.VolatilityModel;
import com.opengamma.analytics.financial.model.volatility.curve.VolatilityCurve;
import com.opengamma.analytics.math.Axis;
import com.opengamma.analytics.math.curve.Curve;
import com.opengamma.analytics.math.interpolation.Interpolator1D;
import com.opengamma.analytics.math.surface.Surface;
import com.opengamma.analytics.math.surface.SurfaceShiftFunctionFactory;
import com.opengamma.analytics.math.surface.SurfaceSliceFunction;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.time.Tenor;
import com.opengamma.util.tuple.DoublesPair;
/**
*
*/
public class VolatilitySurface implements VolatilityModel<DoublesPair>, Serializable {
private final Surface<Double, Double, Double> _surface;
/** x-axis */
public static final Axis EXPIRY_AXIS = Axis.X; // TODO Review
/** y-axis */
public static final Axis STRIKE_AXIS = Axis.Y;
/**
* The tenor of the expiry values of the surface. This isn't guaranteed to be
* populated for all surfaces. If it is populated it is guaranteed to have the same size as
* the surface, otherwise it will be empty.
*/
private final List<Tenor> _expiryTenors;
/**
* Creates a new volatility surface backed by the specified surface.
* <p>
* If a surface is created using this constructor it doesn't contain any tenor information for its expiries.
* This means it won't be possible to apply a shock to individual points on the surface using the scenario
* framework. The other constructor should be used to provide information about expiry tenors and ensure
* compatibility with the scenario framework.
*
* @param surface a surface containing the volatility data with expiries on the x-axis, strikes on the y-axis
*/
public VolatilitySurface(Surface<Double, Double, Double> surface) {
_surface = ArgumentChecker.notNull(surface, "surface");
_expiryTenors = ImmutableList.of();
}
/**
* Creates a new volatility surface backed by the specified surface and with the specified tenors on the x-axis.
*
* @param surface a surface containing the volatility data with expiries on the x-axis, strikes on the y-axis
* @param expiryTenors the expiry tenor of each point. This size of this list must be the same as the size
* of the surface
*/
public VolatilitySurface(Surface<Double, Double, Double> surface, List<Tenor> expiryTenors) {
_surface = ArgumentChecker.notNull(surface, "surface");
ArgumentChecker.notNull(expiryTenors, "expiryTenors");
if (expiryTenors.size() != surface.size()) {
throw new IllegalArgumentException(
"The number of tenors (" + expiryTenors.size() + ") doesn't match " +
"the size of the surface(" + surface.size() + ")");
}
_expiryTenors = ImmutableList.copyOf(expiryTenors);
}
/**
* Returns the expiry tenors of the points in the surface. This can be empty if the data wasn't provided when
* the surface was constructed. If it is not empty it is guaranteed to be the same size as the surface.
*
* @return the expiry tenors of the points in the surface
*/
public List<Tenor> getExpiryTenors() {
return _expiryTenors;
}
@Override
public Double getVolatility(final DoublesPair xy) {
ArgumentChecker.notNull(xy, "xy pair");
return _surface.getZValue(xy);
}
/**
* Return a volatility for the expiry, strike pair provided.
* Interpolation/extrapolation behaviour depends on underlying surface
* @param t time to maturity
* @param k strike
* @return The Black (implied) volatility
*/
public double getVolatility(final double t, final double k) {
final DoublesPair temp = DoublesPair.of(t, k);
return getVolatility(temp);
}
public VolatilityCurve getSlice(final Axis axis, final double here, final Interpolator1D interpolator) {
final Curve<Double, Double> curve = SurfaceSliceFunction.cut(_surface, axis, here, interpolator);
return new VolatilityCurve(curve);
}
public Surface<Double, Double, Double> getSurface() {
return _surface;
}
public VolatilitySurface withParallelShift(final double shift) {
return new VolatilitySurface(getParallelShiftedSurface(shift));
}
public VolatilitySurface withSingleAdditiveShift(final double x, final double y, final double shift) {
return new VolatilitySurface(getSingleAdditiveShiftSurface(x, y, shift));
}
public VolatilitySurface withMultipleAdditiveShifts(final double[] x, final double[] y, final double[] shifts) {
return new VolatilitySurface(getMultipleAdditiveShiftsSurface(x, y, shifts));
}
public VolatilitySurface withConstantMultiplicativeShift(final double shift) {
return new VolatilitySurface(getConstantMultiplicativeShiftSurface(shift));
}
public VolatilitySurface withSingleMultiplicativeShift(final double x, final double y, final double shift) {
return new VolatilitySurface(getSingleMultiplicativeShiftSurface(x, y, shift));
}
public VolatilitySurface withMultipleMultiplicativeShifts(final double[] x, final double[] y, final double[] shifts) {
return new VolatilitySurface(getMultipleMultiplicativeShiftsSurface(x, y, shifts));
}
protected Surface<Double, Double, Double> getParallelShiftedSurface(final double shift) {
return SurfaceShiftFunctionFactory.getShiftedSurface(_surface, shift, true);
}
protected Surface<Double, Double, Double> getSingleAdditiveShiftSurface(final double x, final double y, final double shift) {
return SurfaceShiftFunctionFactory.getShiftedSurface(_surface, x, y, shift, true);
}
protected Surface<Double, Double, Double> getMultipleAdditiveShiftsSurface(final double[] x, final double[] y, final double[] shifts) {
return SurfaceShiftFunctionFactory.getShiftedSurface(_surface, x, y, shifts, true);
}
protected Surface<Double, Double, Double> getConstantMultiplicativeShiftSurface(final double shift) {
return SurfaceShiftFunctionFactory.getShiftedSurface(_surface, shift, false);
}
protected Surface<Double, Double, Double> getSingleMultiplicativeShiftSurface(final double x, final double y, final double shift) {
return SurfaceShiftFunctionFactory.getShiftedSurface(_surface, x, y, shift, false);
}
protected Surface<Double, Double, Double> getMultipleMultiplicativeShiftsSurface(final double[] x, final double[] y, final double[] shifts) {
return SurfaceShiftFunctionFactory.getShiftedSurface(_surface, x, y, shifts, false);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + _surface.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 VolatilitySurface other = (VolatilitySurface) obj;
return ObjectUtils.equals(_surface, other._surface);
}
}