com.opengamma.strata.pricer.impl.volatility.smile.VolatilityFunctionProvider Maven / Gradle / Ivy
/*
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.impl.volatility.smile;
import java.util.function.Function;
import com.opengamma.strata.basics.value.ValueDerivatives;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.array.DoubleArray;
/**
* Provides functions that return volatility and its sensitivity to volatility model parameters.
*
* @param Type of the data needed for the volatility function
*/
public abstract class VolatilityFunctionProvider {
private static final double EPS = 1.0e-6;
/**
* Calculates the volatility.
*
* @param forward the forward value of the underlying
* @param strike the strike value of the option
* @param timeToExpiry the time to expiry of the option
* @param data the model data
* @return the volatility
*/
public abstract double volatility(double forward, double strike, double timeToExpiry, T data);
/**
* Calculates volatility and the adjoint (volatility sensitivity to forward, strike and model parameters).
*
* By default the derivatives are computed by central finite difference approximation.
* This should be overridden in each subclass.
*
* @param forward the forward value of the underlying
* @param strike the strike value of the option
* @param timeToExpiry the time to expiry of the option
* @param data the model data
* @return the volatility and associated derivatives
*/
public ValueDerivatives volatilityAdjoint(double forward, double strike, double timeToExpiry, T data) {
ArgChecker.isTrue(forward >= 0.0, "forward must be greater than zero");
double[] res = new double[2 + data.getNumberOfParameters()]; // fwd, strike, the model parameters
double volatility = volatility(forward, strike, timeToExpiry, data);
res[0] = forwardBar(forward, strike, timeToExpiry, data);
res[1] = strikeBar(forward, strike, timeToExpiry, data);
Function func = getVolatilityFunction(forward, strike, timeToExpiry);
double[] modelAdjoint = paramBar(func, data);
System.arraycopy(modelAdjoint, 0, res, 2, data.getNumberOfParameters());
return ValueDerivatives.of(volatility, DoubleArray.ofUnsafe(res));
}
/**
* Computes the first and second order derivatives of the volatility.
*
* The first derivative values will be stored in the input array {@code volatilityD}
* The array contains, [0] Derivative w.r.t the forward, [1] the derivative w.r.t the strike, then followed by model
* parameters.
* Thus the length of the array should be 2 + (number of model parameters).
*
* The second derivative values will be stored in the input array {@code volatilityD2}.
* Only the second order derivative with respect to the forward and strike must be implemented.
* The array contains [0][0] forward-forward; [0][1] forward-strike; [1][1] strike-strike.
* Thus the size should be 2 x 2.
*
* @param forward the forward value of the underlying
* @param strike the strike value of the option
* @param timeToExpiry the time to expiry of the option
* @param data the model data
* @param volatilityD the array used to return the first order derivative
* @param volatilityD2 the array of array used to return the second order derivative
* @return the volatility
*/
public abstract double volatilityAdjoint2(
double forward,
double strike,
double timeToExpiry,
T data,
double[] volatilityD,
double[][] volatilityD2);
//-------------------------------------------------------------------------
private double forwardBar(double forward, double strike, double timeToExpiry, T data) {
double volUp = volatility(forward + EPS, strike, timeToExpiry, data);
double volDown = volatility(forward - EPS, strike, timeToExpiry, data);
return 0.5 * (volUp - volDown) / EPS;
}
private double strikeBar(double forward, double strike, double timeToExpiry, T data) {
double volUp = volatility(forward, strike + EPS, timeToExpiry, data);
double volDown = volatility(forward, strike - EPS, timeToExpiry, data);
return 0.5 * (volUp - volDown) / EPS;
}
private Function getVolatilityFunction(double forward, double strike, double timeToExpiry) {
return new Function() {
@Override
public Double apply(T data) {
ArgChecker.notNull(data, "data");
return volatility(forward, strike, timeToExpiry, data);
}
};
}
private double[] paramBar(Function func, T data) {
int n = data.getNumberOfParameters();
double[] res = new double[n];
for (int i = 0; i < n; i++) {
res[i] = paramBar(func, data, i);
}
return res;
}
@SuppressWarnings("unchecked")
private double paramBar(Function func, T data, int index) {
double mid = data.getParameter(index);
double up = mid + EPS;
double down = mid - EPS;
if (data.isAllowed(index, down)) {
if (data.isAllowed(index, up)) {
T dUp = (T) data.with(index, up);
T dDown = (T) data.with(index, down);
return 0.5 * (func.apply(dUp) - func.apply(dDown)) / EPS;
}
T dDown = (T) data.with(index, down);
return (func.apply(data) - func.apply(dDown)) / EPS;
}
ArgChecker.isTrue(data.isAllowed(index, up), "No values and index {} = {} are allowed", index, mid);
T dUp = (T) data.with(index, up);
return (func.apply(dUp) - func.apply(data)) / EPS;
}
}