/**
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.volatility.smile.function;
import com.opengamma.analytics.math.differentiation.ValueDerivatives;
import com.opengamma.util.ArgumentChecker;
/**
* Surface Stochastic Volatility Inspired (SSVI) formula.
* <p>
* Reference: Gatheral, Jim and Jacquier, Antoine. Arbitrage-free SVI volatility surfaces. arXiv:1204.0646v4, 2013. Section 4.
*/
public class SSVIVolatilityFunction {
/**
* Computes the volatility in the SSVI formula.
* <p>
* The forward and the strike must be strictly positive. The time to expiration must be strictly positive.
*
* @param forward the forward (rate or price)
* @param strike the option strike
* @param timeToExpiry the option time to expiration
* @param volatilityAtm the at-the-money volatility
* @param rho the correlation parameter
* @param eta the eta parameter
* @return the volatility
*/
public static double volatility(double forward, double strike, double timeToExpiry,
double volatilityAtm, double rho, double eta) {
ArgumentChecker.isTrue(strike > 0, "strike must be strctly positive");
ArgumentChecker.isTrue(forward > 0, "strike must be strctly positive");
ArgumentChecker.isTrue(timeToExpiry > 0, "time to expiration must be strctly positive");
double theta = volatilityAtm * volatilityAtm * timeToExpiry;
double phi = eta / Math.sqrt(theta);
double k = Math.log(strike / forward);
double w = 0.5 * theta * (1.0d + rho * phi * k + Math.sqrt(1.0d + 2 * rho * phi * k + phi * k * phi * k));
return Math.sqrt(w / timeToExpiry);
}
/**
* Computes the adjoint of the volatility function in the SSVI formula.
* <p>
* The derivatives in the output are in the order of the inputs.
*
* @param forward the forward (rate or price)
* @param strike the option strike
* @param timeToExpiry the option time to expiration
* @param volatilityAtm the at-the-money volatility
* @param rho the correlation parameter
* @param eta the eta parameter
* @return the volatility and its derivatives
*/
public static ValueDerivatives volatilityAdjoint(double forward, double strike, double timeToExpiry,
double volatilityAtm, double rho, double eta) {
ArgumentChecker.isTrue(strike > 0, "strike must be strctly positive");
ArgumentChecker.isTrue(forward > 0, "strike must be strctly positive");
ArgumentChecker.isTrue(timeToExpiry > 0, "time to expiration must be strctly positive");
double theta = volatilityAtm * volatilityAtm * timeToExpiry;
double stheta = Math.sqrt(theta);
double phi = eta / stheta;
double k = Math.log(strike / forward);
double s = Math.sqrt(1.0d + 2 * rho * phi * k + phi * k * phi * k);
double w = 0.5 * theta * (1.0d + rho * phi * k + s);
double volatility = Math.sqrt(w / timeToExpiry);
// Backward sweep.
double[] derivatives = new double[6]; // 6 inputs
double volatilityBar = 1.0; // OK
double wBar = 0.5 / (volatility * timeToExpiry) * volatilityBar; // OK
derivatives[2] += -0.5 * volatility / timeToExpiry * volatilityBar;
double thetaBar = w / theta * wBar;
derivatives[4] += 0.5 * theta * phi * k * wBar; // OK
double phiBar = 0.5 * theta * rho * k * wBar;
double kBar = 0.5 * theta * rho * phi * wBar; // OK
double sBar = 0.5 * theta * wBar; // OK
derivatives[4] += phi * k / s * sBar; // OK
phiBar += (rho * k + phi * k * k) / s * sBar;
kBar += (rho * phi + phi * phi * k) / s * sBar;
derivatives[1] += 1.0d / strike * kBar; // OK
derivatives[0] += -1.0d / forward * kBar; // OK
derivatives[5] += phiBar / stheta;
double sthetaBar = -eta / (stheta * stheta) * phiBar;
thetaBar += 0.5 / stheta * sthetaBar;
derivatives[3] += 2 * volatilityAtm * timeToExpiry * thetaBar;
derivatives[2] += volatilityAtm * volatilityAtm * thetaBar;
return new ValueDerivatives(volatility, derivatives);
}
}