/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.equity.variance; import static com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory.getInterpolator; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; import com.opengamma.analytics.financial.equity.variance.pricing.DisplacedDiffusionModel; import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve; import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository; import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurface; import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceDelta; import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceStrike; import com.opengamma.analytics.math.function.Function; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolator; import com.opengamma.analytics.math.interpolation.GridInterpolator2D; import com.opengamma.analytics.math.interpolation.Interpolator1DFactory; import com.opengamma.analytics.math.interpolation.Interpolator2D; import com.opengamma.analytics.math.interpolation.LinearInterpolator1D; import com.opengamma.analytics.math.rootfinding.VectorRootFinder; import com.opengamma.analytics.math.rootfinding.newton.BroydenVectorRootFinder; import com.opengamma.analytics.math.surface.ConstantDoublesSurface; import com.opengamma.analytics.math.surface.FunctionalDoublesSurface; import com.opengamma.analytics.math.surface.InterpolatedDoublesSurface; import com.opengamma.util.test.TestGroup; /** * Test. */ @Test(groups = TestGroup.UNIT) public class DisplacedDiffusionModelTest { // -------------------------------- SETUP ------------------------------------------ // The derivative // private static final double varStrike = 0.05; // private static final double varNotional = 3150; private static final double expiry1 = 1; private static final double expiry5 = 5; // private static final int nObsExpected = 750; // private static final int nObsDisrupted = 0; // private static final double[] observations = {}; // private static final double[] obsWeights = {}; /* final VarianceSwap swap1 = new VarianceSwap(0, expiry1, expiry1, varStrike, varNotional, Currency.EUR, 250, nObsExpected, nObsDisrupted, observations, obsWeights); final VarianceSwap swap5 = new VarianceSwap(0, expiry5, expiry5, varStrike, varNotional, Currency.EUR, 250, nObsExpected, nObsDisrupted, observations, obsWeights); // The pricing method final VarSwapStaticReplication pricer_default_w_cutoff = new VarSwapStaticReplication(null); final VarSwapStaticReplication pricer_null_cutoff = new VarSwapStaticReplication(1.0E-4, 5.0, new RungeKuttaIntegrator1D(), null, null, null); */ // Market data private static final double SPOT = 80; private static final double DRIFT = -0.01; private static final ForwardCurve FORWARD_CURVE = new ForwardCurve(SPOT, DRIFT); //double forward = 100; //private static final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1(); //private static final YieldAndDiscountCurve curveDiscount = curves.getCurve("Funding"); private static final double[] EXPIRIES = new double[] {0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 5.0, 5.0, 5.0, 5.0 }; private static final double[] DELTAS = new double[] {0.75, 0.5, 0.25, 0.1, 0.75, 0.5, 0.25, 0.1, 0.75, 0.5, 0.25, 0.1 }; private static final double[] STRIKES = new double[] {10, 50, 105, 150, 10, 50, 105, 150, 10, 50, 105, 150 }; private static final double[] VOLS = new double[] {0.28, 0.28, 0.28, 0.28, 0.25, 0.25, 0.25, 0.25, 0.26, 0.24, 0.23, 0.25 }; private static final CombinedInterpolatorExtrapolator INTERPOLATOR_1D = getInterpolator(Interpolator1DFactory.DOUBLE_QUADRATIC, Interpolator1DFactory.LINEAR_EXTRAPOLATOR, Interpolator1DFactory.FLAT_EXTRAPOLATOR); private static final Interpolator2D INTERPOLATOR_2D = new GridInterpolator2D(new LinearInterpolator1D(), INTERPOLATOR_1D); private static final InterpolatedDoublesSurface DELTA_SURFACE = new InterpolatedDoublesSurface(EXPIRIES, DELTAS, VOLS, INTERPOLATOR_2D); private static final BlackVolatilitySurface<?> DELTA_VOLSURFACE = new BlackVolatilitySurfaceDelta(DELTA_SURFACE, FORWARD_CURVE); private static final InterpolatedDoublesSurface STRIKE_SURFACE = new InterpolatedDoublesSurface(EXPIRIES, STRIKES, VOLS, INTERPOLATOR_2D); private static final BlackVolatilitySurface<?> STRIKE_VOLSURFACE = new BlackVolatilitySurfaceStrike(STRIKE_SURFACE); // -------------------------------- TESTS ------------------------------------------ @Test public void testFlatSurfaceReproducesVolAndZeroShift() { final double delta1 = 0.75; final double forward = FORWARD_CURVE.getForward(expiry1); final double vol1 = DELTA_VOLSURFACE.getVolatility(expiry1, delta1); final double strike1 = BlackFormulaRepository.impliedStrike(delta1, true, forward, expiry1, vol1); final double delta2 = 0.5; final double vol2 = DELTA_VOLSURFACE.getVolatility(expiry1, delta2); final double strike2 = BlackFormulaRepository.impliedStrike(delta2, true, forward, expiry1, vol2); final DisplacedDiffusionModel logBlack = new DisplacedDiffusionModel(forward, expiry1, strike1, vol1, strike2, vol2); assertEquals(logBlack.getVol(), 0.25, 1e-9); assertEquals(logBlack.getShift(), 0.0, 1e-9); } @Test public void testConstantDoublesSurface() { final BlackVolatilitySurface<?> constVolSurf = new BlackVolatilitySurfaceDelta(ConstantDoublesSurface.from(0.25), FORWARD_CURVE); final double forward = FORWARD_CURVE.getForward(expiry1); final double delta1 = 0.75; final double vol1 = constVolSurf.getVolatility(expiry1, delta1); final double strike1 = BlackFormulaRepository.impliedStrike(delta1, true, forward, expiry1, vol1); final double delta2 = 0.5; final double vol2 = constVolSurf.getVolatility(expiry1, delta2); final double strike2 = BlackFormulaRepository.impliedStrike(delta2, true, forward, expiry1, vol2); final DisplacedDiffusionModel logBlack = new DisplacedDiffusionModel(forward, expiry1, strike1, vol1, strike2, vol2); assertEquals(logBlack.getVol(), 0.25, 1e-9); assertEquals(logBlack.getShift(), 0.0, 1e-9); } @Test public void testOnSmileySurface() { final double forward = FORWARD_CURVE.getForward(expiry5); final VectorRootFinder testSolver = new BroydenVectorRootFinder(1.0e-6, 1.0e-6, 50000); final double vol1 = STRIKE_VOLSURFACE.getVolatility(expiry5, 40); final double vol2 = STRIKE_VOLSURFACE.getVolatility(expiry5, 41); final DisplacedDiffusionModel logBlack = new DisplacedDiffusionModel(forward, expiry5, 40, vol1, 41, vol2, 0.26, 0.01, testSolver); //TODO really - more magic numbers assertEquals(0.21148605807417542, logBlack.getVol(), 1e-8); assertEquals(8.183093915461424, logBlack.getShift(), 1e-8); final DisplacedDiffusionModel logBlackDef = new DisplacedDiffusionModel(forward, expiry5, 40, vol1, 41, vol2); assertEquals(logBlackDef.getVol(), logBlack.getVol(), 1e-8); assertEquals(logBlackDef.getShift(), logBlack.getShift(), 1e-8); } @Test public void testDisplacedDiffusionSmile() { final double displacement = -10.0; final double sigma = 0.2; final Function<Double, Double> surf = new Function<Double, Double>() { @Override public Double evaluate(final Double... x) { final double t = x[0]; final double k = x[1]; @SuppressWarnings("synthetic-access") final double fwd = FORWARD_CURVE.getForward(t); final double price = BlackFormulaRepository.price(fwd + displacement, k + displacement, t, sigma, true); return BlackFormulaRepository.impliedVolatility(price, fwd, k, t, true); } }; final BlackVolatilitySurface<?> volSurface = new BlackVolatilitySurfaceStrike(FunctionalDoublesSurface.from(surf)); final double forward = FORWARD_CURVE.getForward(expiry5); final double vol1 = volSurface.getVolatility(expiry5, 40); final double vol2 = volSurface.getVolatility(expiry5, 41); final DisplacedDiffusionModel logBlackDef = new DisplacedDiffusionModel(forward, expiry5, 40, vol1, 41, vol2); assertEquals(sigma, logBlackDef.getVol(), 1e-8); assertEquals(displacement, logBlackDef.getShift(), 1e-6); } // @Test // public void testPricingAndImpVol() { // double vol1 = STRIKE_VOLSURFACE.getVolatility(expiry5, 40); // double vol2 = STRIKE_VOLSURFACE.getVolatility(expiry5, 41); // final ShiftedLognormalVolModel shiftedBlack = new ShiftedLognormalVolModel(forward, expiry5, 40, vol1, 41, vol2); // // assertEquals(0.2123433522914358, shiftedBlack.getVol(), 1e-8); // assertEquals(8.977510042076403, shiftedBlack.getShift(), 1e-8); // // double[] targets = {0.8579743918497895, 0.2403423824470046, 0.23181972642559168, 0.22833151007347832, 0.22634625564003685, 0.2250331292507572, 0.2240855895344658, // 0.22336179752396107, 0.22278625617056824, 0.22231472625418022, 0.2219193953132662 }; // // final double highStrike = 5.0 * forward; // final double lowStrike = 1e-4 * forward; // final int nSamples = 10; // final double[] strikes = new double[nSamples + 1]; // final double[] prices = new double[nSamples + 1]; // final double[] vols = new double[nSamples + 1]; // for (int i = 0; i <= nSamples; i++) { // strikes[i] = lowStrike + (double) i / nSamples * (highStrike - lowStrike); // prices[i] = shiftedBlack.priceFromFixedStrike(strikes[i]); // vols[i] = new BlackFormula(forward, strikes[i], expiry5, null, prices[i], strikes[i] > forward).computeImpliedVolatility(); // assertEquals(targets[i], vols[i], 1e-8); // } // } // // @Test // public void testImpactOfShiftOnImpliedVols() { // // final double highStrike = 5.0 * forward; // final double lowStrike = 1e-4 * forward; // final int nSamples = 10; // final double[] strikes = new double[nSamples + 1]; // final double[] prices = new double[nSamples + 1]; // final double[] vols = new double[nSamples + 1]; // // double lowVol = 0.1; // double highVol = 0.1; // double lowShift = 0; // double highShift = 0.5 * forward; // // final double[] lnVols = new double[nSamples + 1]; // final double[] shifts = new double[nSamples + 1]; // // for (int j = 0; j <= nSamples; j++) { // lnVols[j] = lowVol + (double) j / nSamples * (highVol - lowVol); // shifts[j] = lowShift + (double) j / nSamples * (highShift - lowShift); // ShiftedLognormalVolModel shiftedBlack = new ShiftedLognormalVolModel(forward, expiry5, lnVols[j], shifts[j]); // // for (int i = 0; i <= nSamples; i++) { // strikes[i] = lowStrike + (double) i / nSamples * (highStrike - lowStrike); // prices[i] = shiftedBlack.priceFromFixedStrike(strikes[i]); // vols[i] = new BlackFormula(forward, strikes[i], expiry5, null, prices[i], strikes[i] > forward).computeImpliedVolatility(); // // // System.err.println(lnVols[j] + "," + shifts[j] + "," + strikes[i] + "," + vols[i]); // } // } // } }