/** * Copyright (C) 2012 - 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 static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import cern.jet.random.engine.MersenneTwister; import cern.jet.random.engine.MersenneTwister64; import cern.jet.random.engine.RandomEngine; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.util.test.TestGroup; /** * Test. */ @Test(groups = TestGroup.UNIT) public class MixedLogNormalVolatilityModelTest { private static final RandomEngine RANDOM = new MersenneTwister64(MersenneTwister.DEFAULT_SEED); private static final MixedLogNormalModelData LEPTOKURTIC1; private static final MixedLogNormalModelData LEPTOKURTIC2; private static final MixedLogNormalModelData PLATYKURTIC; private static final MixedLogNormalModelData LARGE_SYSTEM; private static final double FORWARD = 0.05; private static final double T = 0.6; private static final MixedLogNormalVolatilityFunction VOL_FUNC = MixedLogNormalVolatilityFunction.getInstance(); private static final VolatilityFunctionProvider<MixedLogNormalModelData> FD_VOL_FUNC = new VolatilityFunctionProvider<MixedLogNormalModelData>() { @SuppressWarnings("synthetic-access") @Override public Function1D<MixedLogNormalModelData, Double> getVolatilityFunction(final EuropeanVanillaOption option, final double forward) { return VOL_FUNC.getVolatilityFunction(option, forward); } }; static { LEPTOKURTIC1 = new MixedLogNormalModelData(new double[] {0.8, 0.2 }, new double[] {0.2, 0.7 }); LEPTOKURTIC2 = new MixedLogNormalModelData(new double[] {0.8, 0.2 }, new double[] {0.2, 0.7 }, new double[] {1.1, 0.6 }); PLATYKURTIC = new MixedLogNormalModelData(new double[] {0.5, 0.5 }, new double[] {0.2, 0.2 }, new double[] {0.5, 1.5 }); final int n = 5; final double[] parms = new double[3 * n - 2]; parms[0] = 0.2; for (int i = 1; i < n; i++) { parms[i] = 0.5 * RANDOM.nextDouble(); } for (int i = 0; i < n - 1; i++) { parms[n + i] = Math.PI / 2 * RANDOM.nextDouble(); parms[2 * n - 1 + i] = parms[n + i] * (0.9 + 0.2 * RANDOM.nextDouble()); } LARGE_SYSTEM = new MixedLogNormalModelData(parms); } @Test(enabled = false) public void printTest() { for (int i = 0; i < 101; i++) { final double k = FORWARD * (0.5 + 2.5 * i / 100.); final EuropeanVanillaOption option = new EuropeanVanillaOption(k, T, true); final double vol1 = VOL_FUNC.getVolatility(option, FORWARD, LEPTOKURTIC1); final double vol2 = VOL_FUNC.getVolatility(option, FORWARD, LEPTOKURTIC2); final double vol3 = VOL_FUNC.getVolatility(option, FORWARD, PLATYKURTIC); System.out.println(k + "\t" + vol1 + "\t" + vol2 + "\t" + vol3); } } @Test public void smileTest() { final double shift = 1e-4; final EuropeanVanillaOption optionPlus = new EuropeanVanillaOption((1 + shift) * FORWARD, T, true); final EuropeanVanillaOption option = new EuropeanVanillaOption(FORWARD, T, true); final EuropeanVanillaOption optionMinus = new EuropeanVanillaOption((1 - shift) * FORWARD, T, true); final MixedLogNormalModelData[] data = new MixedLogNormalModelData[] {LEPTOKURTIC1, LEPTOKURTIC2, PLATYKURTIC }; final double[] skew = new double[3]; final double[] kurt = new double[3]; for (int i = 0; i < 3; i++) { skew[i] = (VOL_FUNC.getVolatility(optionPlus, FORWARD, data[i]) - VOL_FUNC.getVolatility(optionMinus, FORWARD, data[i])) / 2 / shift / FORWARD; kurt[i] = (VOL_FUNC.getVolatility(optionPlus, FORWARD, data[i]) + VOL_FUNC.getVolatility(optionMinus, FORWARD, data[i]) - 2 * VOL_FUNC.getVolatility(option, FORWARD, data[i])) / shift / shift / FORWARD / FORWARD; } assertEquals("leptokurtic1", 0, skew[0], 1e-6); assertTrue("leptokurtic2", skew[1] < 0); assertTrue("leptokurtic1", kurt[0] > 0); assertTrue("leptokurtic2", kurt[1] > 0); assertTrue("platykurtic", kurt[2] < 0); } @Test public void modelAdjointTest() { modelAdjointTest(LEPTOKURTIC1); modelAdjointTest(LEPTOKURTIC2); modelAdjointTest(LARGE_SYSTEM); } private void modelAdjointTest(final MixedLogNormalModelData data) { final double strike = 1.1 * FORWARD; final EuropeanVanillaOption option = new EuropeanVanillaOption(strike, T, true); final Function1D<MixedLogNormalModelData, double[]> modelAdjointFunc = VOL_FUNC.getModelAdjointFunction(option, FORWARD); final Function1D<MixedLogNormalModelData, double[]> fdModelAdjointFunc = FD_VOL_FUNC.getModelAdjointFunction(option, FORWARD); final double[] sense = modelAdjointFunc.evaluate(data); final double[] fdSense = fdModelAdjointFunc.evaluate(data); final int nParms = data.getNumberOfParameters(); for (int i = 0; i < nParms; i++) { assertEquals(" : parameter " + i, fdSense[i], sense[i], 1e-6); } } @Test public void volatilityAdjointTest() { volatilityAdjointTest(LEPTOKURTIC1); volatilityAdjointTest(LEPTOKURTIC2); volatilityAdjointTest(LARGE_SYSTEM); } public void volatilityAdjointTest(final MixedLogNormalModelData data) { final double strike = 0.8 * FORWARD; final EuropeanVanillaOption option = new EuropeanVanillaOption(strike, T, true); final Function1D<MixedLogNormalModelData, double[]> modelAdjointFunc = VOL_FUNC.getVolatilityAdjointFunction(option, FORWARD); final Function1D<MixedLogNormalModelData, double[]> fdModelAdjointFunc = FD_VOL_FUNC.getVolatilityAdjointFunction(option, FORWARD); final double[] sense = modelAdjointFunc.evaluate(data); final double[] fdSense = fdModelAdjointFunc.evaluate(data); final int nParms = 3 + data.getNumberOfParameters(); for (int i = 0; i < nParms; i++) { assertEquals("parameter " + i, fdSense[i], sense[i], 1e-6); } } }