/** * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.impl.volatility.smile; import static com.opengamma.strata.collect.TestHelper.assertSerialization; import static com.opengamma.strata.collect.TestHelper.assertThrowsIllegalArg; import static com.opengamma.strata.collect.TestHelper.coverImmutableBean; import static org.testng.Assert.assertEquals; import java.util.function.Function; import org.testng.annotations.Test; import com.opengamma.strata.basics.value.ValueDerivatives; import com.opengamma.strata.collect.array.DoubleArray; import com.opengamma.strata.math.impl.differentiation.FiniteDifferenceType; import com.opengamma.strata.math.impl.differentiation.ScalarFieldFirstOrderDifferentiator; /** * Test {@link SsviVolatilityFunction}. */ @Test 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 SsviFormulaData DATA = SsviFormulaData.of(VOL_ATM, RHO, ETA); 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 SsviVolatilityFunction SSVI_FUNCTION = SsviVolatilityFunction.DEFAULT; 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 = SSVI_FUNCTION.volatility(FORWARD, STRIKES[i], TIME_EXP, DATA); assertEquals(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++) { Function<DoubleArray, Double> function = new Function<DoubleArray, Double>() { @Override public Double apply(DoubleArray x) { SsviFormulaData data = SsviFormulaData.of(x.get(3), x.get(4), x.get(5)); return SSVI_FUNCTION.volatility(x.get(0), x.get(1), x.get(2), data); } }; Function<DoubleArray, DoubleArray> d = differentiator.differentiate(function); DoubleArray fd = d.apply(DoubleArray.of(FORWARD, STRIKES[i], TIME_EXP, VOL_ATM, RHO, ETA)); ValueDerivatives ad = SSVI_FUNCTION.volatilityAdjoint(FORWARD, STRIKES[i], TIME_EXP, DATA); for (int j = 0; j < 6; j++) { assertEquals(fd.get(j), ad.getDerivatives().get(j), TOLERANCE_AD); } } } @Test public void test_small_time() { assertThrowsIllegalArg(() -> SSVI_FUNCTION.volatility(FORWARD, STRIKES[0], 0.0, DATA)); } public void coverage() { coverImmutableBean(SSVI_FUNCTION); } public void test_serialization() { assertSerialization(SSVI_FUNCTION); } }