/** * 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 static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; import com.opengamma.analytics.math.differentiation.FiniteDifferenceType; import com.opengamma.analytics.math.differentiation.ScalarFieldFirstOrderDifferentiator; import com.opengamma.analytics.math.differentiation.ValueDerivatives; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.analytics.math.matrix.DoubleMatrix1D; import com.opengamma.util.test.TestGroup; /** * Tests {@link SSVIVolatilityFunction} */ @Test(groups = TestGroup.UNIT) public class SSVIVolatilityFunctionTest { private static final double VOL_ATM = 0.20; private static final double RHO = -0.25; private static final double ETA = 0.50; private static final double TIME_EXP = 2.5; private static final double FORWARD = 0.05; private static final int N = 10; private static final double[] STRIKES = new double[N]; static { for (int i = 0; i < N; i++) { STRIKES[i] = FORWARD - 0.03 + (i * 0.05 / N); } } private static final double TOLERANCE_VOL = 1.0E-10; private static final double TOLERANCE_AD = 1.0E-6; @Test public void volatility() { // Function versus local implementation of formula double theta = VOL_ATM * VOL_ATM * TIME_EXP; double phi = ETA / Math.sqrt(theta); for (int i = 0; i < N; i++) { double k = Math.log(STRIKES[i] / FORWARD); double w = 0.5 * theta * (1.0d + RHO * phi * k + Math.sqrt(Math.pow(phi * k + RHO, 2) + (1.0d - RHO * RHO))); double sigmaExpected = Math.sqrt(w / TIME_EXP); double sigmaComputed = SSVIVolatilityFunction.volatility(FORWARD, STRIKES[i], TIME_EXP, VOL_ATM, RHO, ETA); assertEquals("Strike: " + STRIKES[i], sigmaExpected, sigmaComputed, TOLERANCE_VOL); } } @Test public void derivatives() { // AD v Finite Difference ScalarFieldFirstOrderDifferentiator differentiator = new ScalarFieldFirstOrderDifferentiator(FiniteDifferenceType.CENTRAL, 1.0E-5); for (int i = 0; i < N; i++) { Function1D<DoubleMatrix1D, Double> function = new Function1D<DoubleMatrix1D, Double>() { private static final long serialVersionUID = 1L; @Override public Double evaluate(DoubleMatrix1D x) { return SSVIVolatilityFunction.volatility(x.getEntry(0), x.getEntry(1), x.getEntry(2), x.getEntry(3), x.getEntry(4), x.getEntry(5)); } }; Function1D<DoubleMatrix1D, DoubleMatrix1D> d = differentiator.differentiate(function); DoubleMatrix1D fd = d.evaluate(new DoubleMatrix1D(FORWARD, STRIKES[i], TIME_EXP, VOL_ATM, RHO, ETA)); ValueDerivatives ad = SSVIVolatilityFunction.volatilityAdjoint(FORWARD, STRIKES[i], TIME_EXP, VOL_ATM, RHO, ETA); for (int j = 0; j < 6; j++) { assertEquals("Strike: " + STRIKES[i], fd.getEntry(j), ad.getDerivatives()[j], TOLERANCE_AD); } } } }