/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.volatility.smile.fitting.interpolation;
import org.apache.commons.lang.Validate;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.SABRExtrapolationRightFunction;
import com.opengamma.analytics.financial.model.volatility.smile.function.SABRFormulaData;
import com.opengamma.analytics.financial.model.volatility.smile.function.SABRHaganVolatilityFunction;
import com.opengamma.analytics.financial.model.volatility.smile.function.VolatilityFunctionProvider;
import com.opengamma.analytics.math.MathException;
import com.opengamma.analytics.math.differentiation.FiniteDifferenceType;
import com.opengamma.analytics.math.differentiation.ScalarFirstOrderDifferentiator;
import com.opengamma.analytics.math.function.Function1D;
/**
* Abstract class for {@link SABRExtrapolationRightFunction} and {@link SABRExtrapolationLeftFunction}
*/
public abstract class SABRExtrapolationLeftRightFunction {
private final VolatilityFunctionProvider<SABRFormulaData> _sabrFunction;
/**
* Constructor using default volatility function
*/
public SABRExtrapolationLeftRightFunction() {
_sabrFunction = new SABRHaganVolatilityFunction();
}
/**
* Constructor specifying volatility function
* @param volatilityFunction The volatility function
*/
public SABRExtrapolationLeftRightFunction(final VolatilityFunctionProvider<SABRFormulaData> volatilityFunction) {
Validate.notNull(volatilityFunction, "volatilityFunction");
_sabrFunction = volatilityFunction;
}
/**
* Get volatility function
* @return The volatility function
*/
public VolatilityFunctionProvider<SABRFormulaData> getVolatilityFunction() {
return _sabrFunction;
}
/**
* Computes the first and second order derivatives of the Black implied volatility in the SABR model.
* @param option The option
* @param forward The forward value of the underlying
* @param data The SABR data
* @param volatilityD The array used to return the first order derivatives. [0] Derivative w.r.t the forward, [1] the derivative w.r.t the strike.
* @param volatilityD2 The array of array used to return the second order derivative. Only the second order derivative with respect to the forward and strike are implemented.
* [0][0] forward-forward; [0][1] forward-strike; [1][1] strike-strike.
* @return The volatility
*/
protected double getVolatilityAdjoint2(final EuropeanVanillaOption option, final double forward,
final SABRFormulaData data, final double[] volatilityD, final double[][] volatilityD2) {
if (_sabrFunction instanceof SABRHaganVolatilityFunction) {
return ((SABRHaganVolatilityFunction) _sabrFunction).getVolatilityAdjoint2(option, forward, data, volatilityD, volatilityD2);
}
double eps = 1.0e-6;
volatilityD[0] = fdSensitivity(option, forward, data, 1, eps);
volatilityD[1] = fdSensitivity(option, forward, data, 2, eps);
if (forward > eps) {
double fwdUp = fdSensitivity(option, forward + eps, data, 1, eps);
double fwdDw = fdSensitivity(option, forward - eps, data, 1, eps);
double crUp = fdSensitivity(option, forward + eps, data, 2, eps);
double crDw = fdSensitivity(option, forward - eps, data, 2, eps);
volatilityD2[0][0] = 0.5 * (fwdUp - fwdDw) / eps;
volatilityD2[1][0] = 0.5 * (crUp - crDw) / eps;
volatilityD2[0][1] = volatilityD2[1][0];
} else {
double fwdBase = fdSensitivity(option, forward, data, 1, eps);
double fwdUp = fdSensitivity(option, forward + eps, data, 1, eps);
double fwdUpUp = fdSensitivity(option, forward + 2.0 * eps, data, 1, eps);
double crBase = fdSensitivity(option, forward, data, 2, eps);
double crUp = fdSensitivity(option, forward + eps, data, 2, eps);
double crUpUp = fdSensitivity(option, forward + 2.0 * eps, data, 2, eps);
volatilityD2[0][0] = (2.0 * fwdUp - 0.5 * fwdUpUp - 1.5 * fwdBase) / eps;
volatilityD2[1][0] = (2.0 * crUp - 0.5 * crUpUp - 1.5 * crBase) / eps;
volatilityD2[0][1] = volatilityD2[1][0];
}
double strike = option.getStrike();
if (strike > eps) {
double strUp = fdSensitivity(option.withStrike(strike + eps), forward, data, 2, eps);
double strDw = fdSensitivity(option.withStrike(strike - eps), forward, data, 2, eps);
volatilityD2[1][1] = 0.5 * (strUp - strDw) / eps;
} else {
double strBase = fdSensitivity(option.withStrike(strike), forward, data, 2, eps);
double strUp = fdSensitivity(option.withStrike(strike + eps), forward, data, 2, eps);
double strUpUp = fdSensitivity(option.withStrike(strike + 2.0 * eps), forward, data, 2, eps);
volatilityD2[1][1] = 0.5 * (2.0 * strUp - 0.5 * strUpUp - 1.5 * strBase) / eps;
}
return _sabrFunction.getVolatilityFunction(option, forward).evaluate(data);
}
private double fdSensitivity(final EuropeanVanillaOption optionData, final double forward, final SABRFormulaData sabrData, final int sense, final double delta) {
FiniteDifferenceType fdType = null;
ScalarFirstOrderDifferentiator differentiator;
Function1D<Double, Double> function;
Double ref;
switch (sense) {
case 1:
if (forward > delta) {
fdType = FiniteDifferenceType.CENTRAL;
} else {
fdType = FiniteDifferenceType.FORWARD;
}
differentiator = new ScalarFirstOrderDifferentiator(fdType, delta);
function = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double x) {
return _sabrFunction.getVolatilityFunction(optionData, x).evaluate(sabrData);
}
};
ref = forward;
break;
case 2:
double strike = optionData.getStrike();
if (strike >= delta) {
fdType = FiniteDifferenceType.CENTRAL;
} else {
fdType = FiniteDifferenceType.FORWARD;
}
function = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double x) {
return _sabrFunction.getVolatilityFunction(optionData.withStrike(x), forward).evaluate(sabrData);
}
};
differentiator = new ScalarFirstOrderDifferentiator(fdType, delta);
ref = strike;
break;
default:
throw new MathException();
}
return differentiator.differentiate(function).evaluate(ref);
}
}