/** * 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.smile.function; import org.apache.commons.lang.Validate; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.lang.annotation.ExternalFunction; /** * Gatheral's Stochastic Volatility Inspired (SVI) model */ public class SVIVolatilityFunction extends VolatilityFunctionProvider<SVIFormulaData> { @Override public Function1D<SVIFormulaData, Double> getVolatilityFunction(final EuropeanVanillaOption option, final double forward) { Validate.notNull(option, "option"); Validate.isTrue(forward > 0, "Need forward >= 0"); final double strike = option.getStrike(); Validate.isTrue(strike > 0, "Need strike >= 0"); final double kappa = Math.log(strike / forward); return new Function1D<SVIFormulaData, Double>() { @SuppressWarnings("synthetic-access") @Override public Double evaluate(final SVIFormulaData data) { return getVolatility(kappa, data); } }; } @ExternalFunction public double getVolatility(final double forward, final double strike, final double a, final double b, final double rho, final double nu, final double m) { final SVIFormulaData data = new SVIFormulaData(a, b, rho, nu, m); return getVolatility(forward, strike, data); } public double getVolatility(final double forward, final double strike, final SVIFormulaData data) { Validate.isTrue(forward > 0, "Need forward >= 0"); Validate.isTrue(strike > 0, "Need strike >= 0"); Validate.notNull(data, "null SVI parameters"); final double kappa = Math.log(strike / forward); return getVolatility(kappa, data); } private double getVolatility(final double kappa, final SVIFormulaData data) { Validate.notNull(data, "null SVI parameters"); final double d = kappa - data.getM(); final double nu = data.getNu(); return Math.sqrt(data.getA() + data.getB() * (data.getRho() * d + Math.sqrt(d * d + nu * nu))); } @Override public Function1D<SVIFormulaData, double[]> getVolatilityAdjointFunction(final EuropeanVanillaOption option, final double forward) { Validate.notNull(option, "option"); Validate.isTrue(forward > 0, "Need forward >= 0"); final double strike = option.getStrike(); Validate.isTrue(strike > 0, "Need strike >= 0"); final double kappa = Math.log(strike / forward); return new Function1D<SVIFormulaData, double[]>() { @SuppressWarnings("synthetic-access") @Override public double[] evaluate(final SVIFormulaData data) { return getVolatilityAdjoint(forward, strike, kappa, data); } }; } @Override public Function1D<SVIFormulaData, double[][]> getVolatilityAdjointFunction(final double forward, final double[] strikes, final double timeToExpiry) { return getVolatilityAdjointFunctionByCallingSingleStrikes(forward, strikes, timeToExpiry); } @Override public Function1D<SVIFormulaData, double[]> getModelAdjointFunction(final EuropeanVanillaOption option, final double forward) { Validate.notNull(option, "option"); Validate.isTrue(forward > 0, "Need forward >= 0"); final double strike = option.getStrike(); Validate.isTrue(strike > 0, "Need strike >= 0"); final double kappa = Math.log(strike / forward); return new Function1D<SVIFormulaData, double[]>() { @SuppressWarnings("synthetic-access") @Override public double[] evaluate(final SVIFormulaData data) { return getModelAdjoint(kappa, data); } }; } @Override public Function1D<SVIFormulaData, double[][]> getModelAdjointFunction(final double forward, final double[] strikes, final double timeToExpiry) { return getModelAdjointFunctionByCallingSingleStrikes(forward, strikes, timeToExpiry); } @ExternalFunction public double[] getVolatilityAjoint(final double forward, final double strike, final double a, final double b, final double rho, final double nu, final double m) { final SVIFormulaData data = new SVIFormulaData(a, b, rho, nu, m); return getVolatilityAjoint(forward, strike, data); } public double[] getVolatilityAjoint(final double forward, final double strike, final SVIFormulaData data) { Validate.isTrue(forward > 0, "Need forward >= 0"); Validate.isTrue(strike > 0, "Need strike >= 0"); final double kappa = Math.log(strike / forward); return getVolatilityAdjoint(forward, strike, kappa, data); } private double[] getVolatilityAdjoint(final double forward, final double strike, final double kappa, final SVIFormulaData data) { Validate.notNull(data, "null data"); final double b = data.getB(); final double rho = data.getRho(); final double nu = data.getNu(); final double d = kappa - data.getM(); final double r = Math.sqrt(d * d + nu * nu); final double s = rho * d + r; final double sigma = Math.sqrt(data.getA() + b * s); final double kappaBar = b * (rho + d / r) / 2 / sigma; final double[] res = new double[8]; res[0] = sigma; res[1] = -kappaBar / forward; //fBar res[2] = kappaBar / strike; //strikebar res[3] = 1. / 2. / sigma; //aBar res[4] = s / 2 / sigma; //bBar res[5] = b * d / 2 / sigma; //rhoBar res[6] = nu * b / r / 2 / sigma; //nuBar res[7] = -kappaBar; //mBar return res; } private double[] getModelAdjoint(final double kappa, final SVIFormulaData data) { Validate.notNull(data, "null data"); final double b = data.getB(); final double rho = data.getRho(); final double nu = data.getNu(); final double d = kappa - data.getM(); final double r = Math.sqrt(d * d + nu * nu); final double s = rho * d + r; final double sigma = Math.sqrt(data.getA() + b * s); final double kappaBar = b * (rho + d / r) / 2 / sigma; final double[] res = new double[5]; res[0] = 1. / 2. / sigma; //aBar res[1] = s / 2 / sigma; //bBar res[2] = b * d / 2 / sigma; //rhoBar res[3] = nu * b / r / 2 / sigma; //nuBar res[4] = -kappaBar; //mBar return res; } @Override public int hashCode() { return toString().hashCode(); } @Override public boolean equals(final Object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (getClass() != obj.getClass()) { return false; } return true; } @Override public String toString() { return "SVI"; } }