Package com.opengamma.analytics.financial.model.volatility.smile.function

Source Code of com.opengamma.analytics.financial.model.volatility.smile.function.SABRHaganVolatilityFunctionTest

/**
* Copyright (C) 2011 - 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 com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption;
import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository;
import com.opengamma.analytics.math.MathException;
import com.opengamma.analytics.math.differentiation.FiniteDifferenceType;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.rootfinding.BisectionSingleRootFinder;
import com.opengamma.analytics.math.rootfinding.BracketRoot;
import com.opengamma.analytics.math.statistics.distribution.NormalDistribution;
import com.opengamma.analytics.math.statistics.distribution.ProbabilityDistribution;
import com.opengamma.util.test.TestGroup;

/**
* Tests related to the Hagan et al. approximation of the SABR implied volatility.
*/
public class SABRHaganVolatilityFunctionTest extends SABRVolatilityFunctionTestCase {

  private static final ProbabilityDistribution<Double> NORMAL = new NormalDistribution(0, 1, new MersenneTwister64(MersenneTwister.DEFAULT_SEED));

  private static final SABRHaganVolatilityFunction FUNCTION = new SABRHaganVolatilityFunction();

  private static final double ALPHA = 0.05;
  private static final double BETA = 0.50;
  private static final double RHO = -0.25;
  private static final double NU = 0.4;
  private static final double F = 0.05;
  private static final SABRFormulaData DATA = new SABRFormulaData(ALPHA, BETA, RHO, NU);
  private static final double T = 4.5;
  private static final double STRIKE_ITM = 0.0450;
  private static final double STRIKE_OTM = 0.0550;

  private static final EuropeanVanillaOption CALL_ATM = new EuropeanVanillaOption(F, T, true);
  private static final EuropeanVanillaOption CALL_ITM = new EuropeanVanillaOption(STRIKE_ITM, T, true);
  private static final EuropeanVanillaOption CALL_OTM = new EuropeanVanillaOption(STRIKE_OTM, T, true);

  @Override
  protected VolatilityFunctionProvider<SABRFormulaData> getFunction() {
    return FUNCTION;
  }

  @Test
  /**
   * Test if the Hagan volatility function implementation around ATM is numerically stable enough (the finite difference slope should be small enough).
   */
  public void testATMSmoothness() {
    final double timeToExpiry = 1;
    final boolean isCall = true;
    EuropeanVanillaOption option;
    final double alpha = 0.05;
    final double beta = 0.5;
    final double nu = 0.50;
    final double rho = -0.25;
    final int nbPoints = 100;
    final double forward = 0.05;
    final double[] sabrVolatilty = new double[2 * nbPoints + 1];
    final double range = 5E-9;
    final double strike[] = new double[2 * nbPoints + 1];
    for (int looppts = -nbPoints; looppts <= nbPoints; looppts++) {
      strike[looppts + nbPoints] = forward + ((double) looppts) / nbPoints * range;
      option = new EuropeanVanillaOption(strike[looppts + nbPoints], timeToExpiry, isCall);
      final SABRFormulaData SabrData = new SABRFormulaData(alpha, beta, rho, nu);
      sabrVolatilty[looppts + nbPoints] = FUNCTION.getVolatilityFunction(option, forward).evaluate(SabrData);
    }
    for (int looppts = -nbPoints; looppts < nbPoints; looppts++) {
      assertTrue(Math.abs(sabrVolatilty[looppts + nbPoints + 1] - sabrVolatilty[looppts + nbPoints]) / (strike[looppts + nbPoints + 1] - strike[looppts + nbPoints]) < 20.0);
    }
  }

  @Test(enabled = false)
  /**
   * Produce the smile for a given set of strikes.
   */
  public void smile() {
    final double alpha = 0.04079820992199477;
    final double beta = 0.5;
    final double rho = 0.12483799350466732;
    final double nu = 1.1156276403408933;
    final double timeToExpiry = 5.0;
    final double forward = 0.03189998273775524;
    final int nbpoints = 20;
    final double startStrike = 0.0001;
    final double endStrike = 0.2500;
    final SABRFormulaData SabrData = new SABRFormulaData(alpha, beta, rho, nu);
    final double[] strikes = new double[nbpoints + 1];
    final double[] sabrVolatilty = new double[nbpoints + 1];
    EuropeanVanillaOption option;
    for (int loopstrike = 0; loopstrike <= nbpoints; loopstrike++) {
      strikes[loopstrike] = startStrike + loopstrike * (endStrike - startStrike) / nbpoints;
      option = new EuropeanVanillaOption(strikes[loopstrike], timeToExpiry, true);
      sabrVolatilty[loopstrike] = FUNCTION.getVolatilityFunction(option, forward).evaluate(SabrData);
    }
  }

  @Test
  /**
   * Tests the first order adjoint derivatives for the SABR Hagan volatility function.
   * The derivatives with respect to the forward, strike, alpha, beta, rho and nu are provided.
   */
  public void testVolatilityAdjointDebug() {
    final double eps = 1e-6;
    final double tol = 1e-5;
    testVolatilityAdjoint(F, CALL_ATM, DATA, eps, tol);
    testVolatilityAdjoint(F, CALL_ITM, DATA, eps, tol);
    testVolatilityAdjoint(F, CALL_OTM, DATA, eps, tol);
  }

  /**
   * Test small strike edge case. Vol -> infinity as strike -> 0, so the strike is floored - tested against finite difference below this
   * floor will give spurious results
   */
  @Test
  public void testVolatilityAdjointSmallStrike() {
    final double eps = 1e-10;
    final double tol = 1e-6;
    final double strike = 2e-6 * F;
    testVolatilityAdjoint(F, CALL_ATM.withStrike(strike), DATA, eps, tol);
  }

  /**
   *Test the alpha = 0 edge case. Implied vol is zero for alpha = 0, and except in the ATM case, the alpha sensitivity is infinite. We
   *choose to (arbitrarily) return 1e7 in this case.
   */
  @Test
  public void testVolatilityAdjointAlpha0() {
    final double eps = 1e-5;
    final double tol = 1e-6;
    final SABRFormulaData data = DATA.withAlpha(0.0);
    testVolatilityAdjoint(F, CALL_ATM, data, eps, tol);

    final double volatility = FUNCTION.getVolatilityFunction(CALL_ITM, F).evaluate(data);
    final double[] volatilityAdjoint = FUNCTION.getVolatilityAdjoint(CALL_ITM, F, data);

    assertEquals("Vol", volatility, volatilityAdjoint[0], tol);

    assertEquals("Forward Sensitivity", 0.0, volatilityAdjoint[1], tol);
    assertEquals("Strike Sensitivity", 0.0, volatilityAdjoint[2], tol);
    assertEquals("Alpha Sensitivity", 1e7, volatilityAdjoint[3], tol);
    assertEquals("Beta Sensitivity", 0.0, volatilityAdjoint[4], tol);
    assertEquals("Rho Sensitivity", 0.0, volatilityAdjoint[5], tol);
    assertEquals("Nu Sensitivity", 0.0, volatilityAdjoint[6], tol);
  }

  @Test
  public void testVolatilityAdjointSmallAlpha() {
    final double eps = 1e-7;
    final double tol = 1e-3;
    final SABRFormulaData data = DATA.withAlpha(1e-5);
    testVolatilityAdjoint(F, CALL_ATM, data, eps, tol);
    testVolatilityAdjoint(F, CALL_ITM, data, eps, tol);
    testVolatilityAdjoint(F, CALL_OTM, data, eps, tol);
  }

  /**
   *Test the beta = 0 edge case
   */
  @Test
  public void testVolatilityAdjointBeta0() {
    final double eps = 1e-5;
    final double tol = 1e-6;
    final SABRFormulaData data = DATA.withBeta(0.0);
    testVolatilityAdjoint(F, CALL_ATM, data, eps, tol);
    testVolatilityAdjoint(F, CALL_ITM, data, eps, tol);
    testVolatilityAdjoint(F, CALL_OTM, data, eps, tol);
  }

  /**
   *Test the beta = 1 edge case
   */
  @Test
  public void testVolatilityAdjointBeta1() {
    final double eps = 1e-6;
    final double tol = 1e-6;
    final SABRFormulaData data = DATA.withBeta(1.0);
    testVolatilityAdjoint(F, CALL_ATM, data, eps, tol);
    testVolatilityAdjoint(F, CALL_ITM, data, eps, tol);
    testVolatilityAdjoint(F, CALL_OTM, data, eps, tol);
  }

  /**
   *Test the nu = 0 edge case
   */
  @Test
  public void testVolatilityAdjointNu0() {
    final double eps = 1e-5;
    final double tol = 1e-6;
    final SABRFormulaData data = DATA.withNu(0.0);
    testVolatilityAdjoint(F, CALL_ATM, data, eps, tol);
    testVolatilityAdjoint(F, CALL_ITM, data, eps, 2e-4);
    testVolatilityAdjoint(F, CALL_OTM, data, eps, 5e-5);
  }

  /**
   *Test the rho = -1 edge case
   */
  @Test
  public void testVolatilityAdjointRhoM1() {
    final double eps = 1e-5;
    final double tol = 1e-6;
    final SABRFormulaData data = DATA.withRho(-1.0);
    testVolatilityAdjoint(F, CALL_ATM, data, eps, tol);
    testVolatilityAdjoint(F, CALL_ITM, data, eps, tol);
    testVolatilityAdjoint(F, CALL_OTM, data, eps, tol);
  }

  /**
   *Test the rho = 1 edge case
   */
  @Test
  public void testVolatilityAdjointRho1() {
    final double eps = 1e-4;
    final double tol = 1e-5;
    final SABRFormulaData data = DATA.withRho(1.0);
    testVolatilityAdjoint(F, CALL_ATM, data, eps, tol);
    testVolatilityAdjoint(F, CALL_ITM, data, eps, tol);
    testVolatilityAdjoint(F, CALL_OTM, data, eps, tol);
  }

  @Test
  public void testVolatilityAdjointLargeRhoZLessThan1() {
    final double eps = 1e-4;
    final double tol = 1e-5;
    final SABRFormulaData data = DATA.withRho(1.0 - 1e-9);
    testVolatilityAdjoint(F, CALL_ITM, data, eps, tol);
  }

  @Test
  public void testVolatilityAdjointLargeRhoZGreaterThan1() {
    final double eps = 1e-11;
    final double tol = 1e-4;
    final SABRFormulaData data = DATA.withRho(1.0 - 1e-9).withAlpha(0.15 * ALPHA);
    testVolatilityAdjoint(F, CALL_ITM, data, eps, tol);
  }

  @Test
  public void testVolatilityModelAdjoint() {
    final double eps = 1e-5;
    final double tol = 1e-6;
    final SABRFormulaData data = DATA;
    testVolatilityModelAdjoint(F, CALL_ATM, data, eps, tol);
    testVolatilityModelAdjoint(F, CALL_ITM, data, eps, tol);
    testVolatilityModelAdjoint(F, CALL_OTM, data, eps, tol);
  }

  @Test
  public void testVolatilityModelAdjointRhoM1() {
    final double eps = 1e-5;
    final double tol = 1e-6;
    final SABRFormulaData data = DATA.withRho(-1.0);
    testVolatilityModelAdjoint(F, CALL_ATM, data, eps, tol); //z=0 case
    double z = -0.975;
    double strike = strikeForZ(z, F, ALPHA, BETA, NU);
    testVolatilityModelAdjoint(F, CALL_ATM.withStrike(strike), data, eps, 5e-4);
    z = 2.0;
    strike = strikeForZ(z, F, ALPHA, BETA, NU);
    testVolatilityModelAdjoint(F, CALL_ATM.withStrike(strike), data, eps, tol);
    z = -2.0;
    strike = strikeForZ(z, F, ALPHA, BETA, NU);
    //The true rho sensitivity at rho=-1 is infinity
    testVolatilityModelAdjoint(F, CALL_ATM.withStrike(strike), DATA.withRho(-1 + 1e-3), eps, 1e-4);
    testVolatilityModelAdjoint(F, CALL_ATM.withStrike(strike), data, 1e-6, 1.5);
  }

  @Test
  public void testVolatilityModelAdjointRhoP1() {
    final double eps = 1e-5;
    final double tol = 1e-6;
    final SABRFormulaData data = DATA.withRho(1.0);
    testVolatilityModelAdjoint(F, CALL_ATM, data, eps, tol); //z=0 case
    double z = 0.975;
    double strike = strikeForZ(z, F, ALPHA, BETA, NU);
    testVolatilityModelAdjoint(F, CALL_ATM.withStrike(strike), data, eps, 1e-2);
    z = -2.0;
    strike = strikeForZ(z, F, ALPHA, BETA, NU);
    testVolatilityModelAdjoint(F, CALL_ATM.withStrike(strike), data, eps, 50 * tol);
    z = 2.0;
    strike = strikeForZ(z, F, ALPHA, BETA, NU);
    //The true rho sensitivity at rho= 1 is -infinity
    testVolatilityModelAdjoint(F, CALL_ATM.withStrike(strike), DATA.withRho(1 - 1e-3), eps, 5e-5);
    testVolatilityModelAdjoint(F, CALL_ATM.withStrike(strike), data, 1e-6, 1.0);
  }

  @Test
  public void testVolatilityModelAdjointBeta0() {
    final double eps = 1e-5;
    final double tol = 1e-6;
    final SABRFormulaData data = DATA.withBeta(0);
    testVolatilityModelAdjoint(F, CALL_ATM, data, eps, tol);
    testVolatilityModelAdjoint(F, CALL_ITM, data, eps, tol);
    testVolatilityModelAdjoint(F, CALL_OTM, data, eps, tol);
  }

  @Test
  public void testVolatilityModelAdjointBeta1() {
    final double eps = 1e-5;
    final double tol = 1e-6;
    final SABRFormulaData data = DATA.withBeta(1);
    testVolatilityModelAdjoint(F, CALL_ATM, data, eps, tol);
    testVolatilityModelAdjoint(F, CALL_ITM, data, eps, tol);
    testVolatilityModelAdjoint(F, CALL_OTM, data, eps, tol);
  }

  @Test
  public void testVolatilityModelAdjointNu0() {
    final double eps = 1e-6;
    final double tol = 1e-6;
    final SABRFormulaData data = DATA.withNu(0);
    testVolatilityModelAdjoint(F, CALL_ATM, data, eps, tol);
    testVolatilityModelAdjoint(F, CALL_ITM, data, eps, tol);
    testVolatilityModelAdjoint(F, CALL_OTM, data, eps, tol);
  }

  @Test
  public void testVolatilityModelAdjoinAlpha0() {

    final double eps = 1e-10;
    final double tol = 1e-2;

    double z = getZ(F, CALL_ITM.getStrike(), ALPHA, BETA, NU);
    double alpha = z / 2e8;
    SABRFormulaData data = DATA.withAlpha(alpha);
    testVolatilityModelAdjoint(F, CALL_ITM, data, eps, tol);

    z = getZ(F, CALL_OTM.getStrike(), ALPHA, BETA, NU);
    alpha = -z / 2e6;
    data = DATA.withAlpha(alpha);
    testVolatilityModelAdjoint(F, CALL_ITM, data, eps, tol);

  }

  /**
   *Test the beta = 0.0, rho = 1 edge case
   */
  @Test
  public void testVolatilityModelAdjointBeta0Rho1() {
    final double eps = 1e-4;
    final double tol = 1e-5;
    final SABRFormulaData data = DATA.withRho(1.0).withBeta(0.0).withNu(20.0);
    testVolatilityModelAdjoint(F, CALL_ATM, data, eps, tol);
    // testVolatilityModelAdjoint(FORWARD, CALL_ITM, data, eps, tol);
    testVolatilityModelAdjoint(F, CALL_OTM, data, eps, tol);
  }

  private double getZ(final double forward, final double strike, final double alpha, final double beta, final double nu) {
    return nu / alpha * Math.pow(forward * strike, (1 - beta) / 2) * Math.log(forward / strike);
  }

  private double strikeForZ(final double z, final double forward, final double alpha, final double beta, final double nu) {
    if (z == 0) {
      return forward;
    }
    if (beta == 1) {
      return forward * Math.exp(-alpha * z / nu);
    }

    final BracketRoot bracketer = new BracketRoot();
    final BisectionSingleRootFinder rootFinder = new BisectionSingleRootFinder(1e-5);

    final Function1D<Double, Double> func = new Function1D<Double, Double>() {
      @SuppressWarnings("synthetic-access")
      @Override
      public Double evaluate(final Double strike) {
        return getZ(forward, strike, alpha, beta, nu) - z;
      }
    };

    final double k = forward * Math.exp(-alpha * z / nu * Math.pow(forward, beta - 1));
    double l, h;
    if (z > 0) {
      h = k;
      l = h / 2;
    } else {
      l = k;
      h = 2 * l;
    }

    final double[] brackets = bracketer.getBracketedPoints(func, l, h, forward / 20, 20 * forward);
    return rootFinder.getRoot(func, brackets[0], brackets[1]);
  }

  private void testVolatilityAdjoint(final double forward, final EuropeanVanillaOption optionData, final SABRFormulaData sabrData, final double eps, final double tol) {
    final double volatility = FUNCTION.getVolatilityFunction(optionData, forward).evaluate(sabrData);
    final double[] volatilityAdjoint = FUNCTION.getVolatilityAdjoint(optionData, forward, sabrData);

    assertEquals("Vol", volatility, volatilityAdjoint[0], tol);

    assertEqualsRelTol("Forward Sensitivity" + sabrData.toString(), fdSensitivity(optionData, forward, sabrData, SABRParameter.Forward, eps), volatilityAdjoint[1], tol);
    assertEqualsRelTol("Strike Sensitivity" + sabrData.toString(), fdSensitivity(optionData, forward, sabrData, SABRParameter.Strike, eps), volatilityAdjoint[2], tol);
    assertEqualsRelTol("Alpha Sensitivity" + sabrData.toString(), fdSensitivity(optionData, forward, sabrData, SABRParameter.Alpha, eps), volatilityAdjoint[3], tol);
    assertEqualsRelTol("Beta Sensitivity" + sabrData.toString(), fdSensitivity(optionData, forward, sabrData, SABRParameter.Beta, eps), volatilityAdjoint[4], tol);
    assertEqualsRelTol("Rho Sensitivity" + sabrData.toString(), fdSensitivity(optionData, forward, sabrData, SABRParameter.Rho, eps), volatilityAdjoint[5], tol);
    assertEqualsRelTol("Nu Sensitivity" + sabrData.toString(), fdSensitivity(optionData, forward, sabrData, SABRParameter.Nu, eps), volatilityAdjoint[6], tol);
  }

  private void testVolatilityModelAdjoint(final double forward, final EuropeanVanillaOption optionData, final SABRFormulaData sabrData, final double eps, final double tol) {
    //    double volatility = FUNCTION.getVolatilityFunction(optionData, forward).evaluate(sabrData);
    final double[] volatilityAdjoint = FUNCTION.getVolatilityModelAdjoint(optionData, forward, sabrData);

    assertEqualsRelTol("Alpha Sensitivity" + sabrData.toString(), fdSensitivity(optionData, forward, sabrData, SABRParameter.Alpha, eps), volatilityAdjoint[0], tol);
    assertEqualsRelTol("Beta Sensitivity" + sabrData.toString(), fdSensitivity(optionData, forward, sabrData, SABRParameter.Beta, eps), volatilityAdjoint[1], tol);
    assertEqualsRelTol("Rho Sensitivity" + sabrData.toString(), fdSensitivity(optionData, forward, sabrData, SABRParameter.Rho, eps), volatilityAdjoint[2], tol);
    assertEqualsRelTol("Nu Sensitivity" + sabrData.toString(), fdSensitivity(optionData, forward, sabrData, SABRParameter.Nu, eps), volatilityAdjoint[3], tol);
  }

  private void assertEqualsRelTol(final String msg, final double exp, final double act, final double tol) {
    final double delta = (Math.abs(exp) + Math.abs(act)) * tol / 2.0;
    assertEquals(msg, exp, act, delta);
  }

  @Test
  /**
   * Tests the second order adjoint derivatives for the SABR Hagan volatility function. Only the derivatives with respect to the forward and the strike are provided.
   */
  public void volatilityAdjoint2() {
    volatilityAdjoint2ForInstrument(CALL_ITM, 1.0E-6, 1.0E-2);
    volatilityAdjoint2ForInstrument(CALL_ATM, 1.0E-6, 1.0E+2); // ATM the second order derivative is poor.
    volatilityAdjoint2ForInstrument(CALL_OTM, 1.0E-6, 1.0E-2);
  }

  private void volatilityAdjoint2ForInstrument(final EuropeanVanillaOption option, final double tolerance1, final double tolerance2) {
    // Price
    final double volatility = FUNCTION.getVolatilityFunction(option, F).evaluate(DATA);
    final double[] volatilityAdjoint = FUNCTION.getVolatilityAdjoint(option, F, DATA);
    final double[] volD = new double[6];
    final double[][] volD2 = new double[2][2];
    final double vol = FUNCTION.getVolatilityAdjoint2(option, F, DATA, volD, volD2);
    assertEquals("SABR Hagan: adjoint 2", volatility, vol, tolerance1);
    // Derivative
    for (int loopder = 0; loopder < 6; loopder++) {
      assertEquals("Derivative " + loopder, volatilityAdjoint[loopder + 1], volD[loopder], tolerance1);
    }
    // Derivative forward-forward
    final double deltaF = 0.000001;
    final double volatilityFP = FUNCTION.getVolatilityFunction(option, F + deltaF).evaluate(DATA);
    final double volatilityFM = FUNCTION.getVolatilityFunction(option, F - deltaF).evaluate(DATA);
    final double derivativeFF_FD = (volatilityFP + volatilityFM - 2 * volatility) / (deltaF * deltaF);
    assertEquals("SABR adjoint order 2: forward-forward", derivativeFF_FD, volD2[0][0], tolerance2);
    // Derivative strike-strike
    final double deltaK = 0.000001;
    final EuropeanVanillaOption optionKP = new EuropeanVanillaOption(option.getStrike() + deltaK, T, true);
    final EuropeanVanillaOption optionKM = new EuropeanVanillaOption(option.getStrike() - deltaK, T, true);
    final double volatilityKP = FUNCTION.getVolatilityFunction(optionKP, F).evaluate(DATA);
    final double volatilityKM = FUNCTION.getVolatilityFunction(optionKM, F).evaluate(DATA);
    final double derivativeKK_FD = (volatilityKP + volatilityKM - 2 * volatility) / (deltaK * deltaK);
    assertEquals("SABR adjoint order 2: strike-strike", derivativeKK_FD, volD2[1][1], tolerance2);
    // Derivative strike-forward
    final double volatilityFPKP = FUNCTION.getVolatilityFunction(optionKP, F + deltaF).evaluate(DATA);
    final double derivativeFK_FD = (volatilityFPKP + volatility - volatilityFP - volatilityKP) / (deltaF * deltaK);
    assertEquals("SABR adjoint order 2: forward-strike", derivativeFK_FD, volD2[0][1], tolerance2);
    assertEquals("SABR adjoint order 2: strike-forward", volD2[0][1], volD2[1][0], 1E-6);
  }

  //TODO write a fuzzer that hits SABR with random parameters
  @Test(invocationCount = 5, successPercentage = 19, singleThreaded = true, groups = TestGroup.INTEGRATION)
  public void testRandomParameters() {
    final double eps = 1e-5;
    final double tol = 1e-3;

    for (int count = 0; count < 100; count++) {
      final double alpha = Math.exp(NORMAL.nextRandom() * 0.2 - 2);
      final double beta = Math.random(); //TODO Uniform numbers in distribution
      final double nu = Math.exp(NORMAL.nextRandom() * 0.3 - 1);
      final double rho = 2 * Math.random() - 1;
      final SABRFormulaData data = new SABRFormulaData(alpha, beta, rho, nu);
      testVolatilityAdjoint(F, CALL_ATM, data, eps, tol);
      testVolatilityAdjoint(F, CALL_ITM, data, eps, tol);
      testVolatilityAdjoint(F, CALL_OTM, data, eps, tol);
    }
  }

  @Test(enabled = false)
  public void testExtremeParameters2() {
    @SuppressWarnings("unused")
    final double alpha = 0.2 * ALPHA;
    //    double beta = 0.5;
    //    double nu = 0.2;
    @SuppressWarnings("unused")
    final double rho = 1 - 1e-9;

    //    double strike = 1e-8;
    //    EuropeanVanillaOption option = CALL_ITM.withStrike(strike);

    //  EuropeanVanillaOption option = CALL_STRIKE.withStrike(forward * 1.01);

    for (int i = 0; i < 200; i++) {
      //      double e = -5 - 15.*i/199;
      //      rho = 1.0 - Math.pow(10,e);
      final double forward = 0.045 + 0.01 * i / 199;

      final double volatility = FUNCTION.getVolatilityFunction(CALL_ITM, forward).evaluate(DATA);
      final double[] volatilityAdjoint = FUNCTION.getVolatilityAdjoint(CALL_ITM, F, DATA);
      System.out.println(forward + "\t" + volatility + "\t" + volatilityAdjoint[1]);

    }
    //
    //    SABRFormulaData data = new SABRFormulaData(forward, alpha, beta, nu, rho);
    //    double volatility = FUNCTION.getVolatilityFunction(option).evaluate(data);
    //
    //    double[] volatilityAdjoint = FUNCTION.getVolatilityAdjointDebug(option, data);
    //    System.out.println(volatility + "\t" + volatilityAdjoint[2]);

    //    testVolatilityAdjoint(option, data, 1e-6);
    //    testVolatilityAdjoint(CALL_ATM, data, 1e-5);
  }

  /**
   *Calculate the true SABR delta and gamma and compare with that found by finite difference
   */
  public void testGreeks() {
    final double eps = 1e-3;
    final double f = 1.2;
    final double k = 1.4;
    final double t = 5.0;
    final double alpha = 0.3;
    final double beta = 0.6;
    final double rho = -0.4;
    final double nu = 0.4;
    final SABRFormulaData sabrData = new SABRFormulaData(alpha, beta, rho, nu);

    final SABRHaganVolatilityFunction sabr = new SABRHaganVolatilityFunction();
    final double[] vol = sabr.getVolatilityAdjoint(new EuropeanVanillaOption(k, t, true), f, sabrData);
    final double bsDelta = BlackFormulaRepository.delta(f, k, t, vol[0], true);
    final double bsVega = BlackFormulaRepository.vega(f, k, t, vol[0]);
    final double volForwardSense = vol[1];
    final double delta = bsDelta + bsVega * volForwardSense;

    final double volUp = sabr.getVolatility(f + eps, k, t, alpha, beta, rho, nu);
    final double volDown = sabr.getVolatility(f - eps, k, t, alpha, beta, rho, nu);
    final double priceUp = BlackFormulaRepository.price(f + eps, k, t, volUp, true);
    final double price = BlackFormulaRepository.price(f, k, t, vol[0], true);
    final double priceDown = BlackFormulaRepository.price(f - eps, k, t, volDown, true);
    final double fdDelta = (priceUp - priceDown) / 2 / eps;
    assertEquals(fdDelta, delta, 1e-6);

    final double bsVanna = BlackFormulaRepository.vanna(f, k, t, vol[0]);
    final double bsGamma = BlackFormulaRepository.gamma(f, k, t, vol[0]);

    final double[] volD1 = new double[5];
    final double[][] volD2 = new double[2][2];
    sabr.getVolatilityAdjoint2(new EuropeanVanillaOption(k, t, true), f, sabrData, volD1, volD2);
    final double d2Sigmad2Fwd = volD2[0][0];
    final double gamma = bsGamma + 2 * bsVanna * vol[1] + bsVega * d2Sigmad2Fwd;
    final double fdGamma = (priceUp + priceDown - 2 * price) / eps / eps;

    final double d2Sigmad2FwdFD = (volUp + volDown - 2 * vol[0]) / eps / eps;
    assertEquals(d2Sigmad2FwdFD, d2Sigmad2Fwd, 1e-4);

    assertEquals(fdGamma, gamma, 1e-2);
  }

  private enum SABRParameter {
    Forward, Strike, Alpha, Beta, Nu, Rho
  }

  private double fdSensitivity(final EuropeanVanillaOption optionData, final double forward, final SABRFormulaData sabrData, final SABRParameter param, final double delta) {

    Function1D<SABRFormulaData, Double> funcC = null;
    Function1D<SABRFormulaData, Double> funcB = null;
    Function1D<SABRFormulaData, Double> funcA = null;
    SABRFormulaData dataC = null;
    SABRFormulaData dataB = sabrData;
    SABRFormulaData dataA = null;
    final Function1D<SABRFormulaData, Double> func = FUNCTION.getVolatilityFunction(optionData, forward);

    FiniteDifferenceType fdType = null;

    switch (param) {
      case Strike:
        final double strike = optionData.getStrike();
        if (strike >= delta) {
          fdType = FiniteDifferenceType.CENTRAL;
          funcA = FUNCTION.getVolatilityFunction(optionData.withStrike(strike - delta), forward);
          funcC = FUNCTION.getVolatilityFunction(optionData.withStrike(strike + delta), forward);
        } else {
          fdType = FiniteDifferenceType.FORWARD;
          funcA = func;
          funcB = FUNCTION.getVolatilityFunction(optionData.withStrike(strike + delta), forward);
          funcC = FUNCTION.getVolatilityFunction(optionData.withStrike(strike + 2 * delta), forward);
        }
        dataC = sabrData;
        dataB = sabrData;
        dataA = sabrData;
        break;
      case Forward:
        if (forward > delta) {
          fdType = FiniteDifferenceType.CENTRAL;
          funcA = FUNCTION.getVolatilityFunction(optionData, forward - delta);
          funcC = FUNCTION.getVolatilityFunction(optionData, forward + delta);
        } else {
          fdType = FiniteDifferenceType.FORWARD;
          funcA = func;
          funcB = FUNCTION.getVolatilityFunction(optionData, forward + delta);
          funcC = FUNCTION.getVolatilityFunction(optionData, forward + 2 * delta);
        }
        dataC = sabrData;
        dataB = sabrData;
        dataA = sabrData;
        break;
      case Alpha:
        final double a = sabrData.getAlpha();
        if (a >= delta) {
          fdType = FiniteDifferenceType.CENTRAL;
          dataA = sabrData.withAlpha(a - delta);
          dataC = sabrData.withAlpha(a + delta);
        } else {
          fdType = FiniteDifferenceType.FORWARD;
          dataA = sabrData;
          dataB = sabrData.withAlpha(a + delta);
          dataC = sabrData.withAlpha(a + 2 * delta);
        }
        funcC = func;
        funcB = func;
        funcA = func;
        break;
      case Beta:
        final double b = sabrData.getBeta();
        if (b >= delta) {
          fdType = FiniteDifferenceType.CENTRAL;
          dataA = sabrData.withBeta(b - delta);
          dataC = sabrData.withBeta(b + delta);
        } else {
          fdType = FiniteDifferenceType.FORWARD;
          dataA = sabrData;
          dataB = sabrData.withBeta(b + delta);
          dataC = sabrData.withBeta(b + 2 * delta);
        }
        funcC = func;
        funcB = func;
        funcA = func;
        break;
      case Nu:
        final double n = sabrData.getNu();
        if (n >= delta) {
          fdType = FiniteDifferenceType.CENTRAL;
          dataA = sabrData.withNu(n - delta);
          dataC = sabrData.withNu(n + delta);
        } else {
          fdType = FiniteDifferenceType.FORWARD;
          dataA = sabrData;
          dataB = sabrData.withNu(n + delta);
          dataC = sabrData.withNu(n + 2 * delta);
        }
        funcC = func;
        funcB = func;
        funcA = func;
        break;
      case Rho:
        final double r = sabrData.getRho();
        if ((r + 1) < delta) {
          fdType = FiniteDifferenceType.FORWARD;
          dataA = sabrData;
          dataB = sabrData.withRho(r + delta);
          dataC = sabrData.withRho(r + 2 * delta);
        } else if ((1 - r) < delta) {
          fdType = FiniteDifferenceType.BACKWARD;
          dataA = sabrData.withRho(r - 2 * delta);
          dataB = sabrData.withRho(r - delta);
          dataC = sabrData;
        } else {
          fdType = FiniteDifferenceType.CENTRAL;
          dataC = sabrData.withRho(r + delta);
          dataA = sabrData.withRho(r - delta);
        }
        funcC = func;
        funcB = func;
        funcA = func;
        break;
      default:
        throw new MathException("enum not found");
    }

    if (fdType != null) {
      switch (fdType) {
        case FORWARD:
          return (-1.5 * funcA.evaluate(dataA) + 2.0 * funcB.evaluate(dataB) - 0.5 * funcC.evaluate(dataC)) / delta;
        case BACKWARD:
          return (0.5 * funcA.evaluate(dataA) - 2.0 * funcB.evaluate(dataB) + 1.5 * funcC.evaluate(dataC)) / delta;
        case CENTRAL:
          return (funcC.evaluate(dataC) - funcA.evaluate(dataA)) / 2.0 / delta;
        default:
          throw new MathException("enum not found");
      }
    }
    throw new MathException("enum not found");
  }
}
TOP

Related Classes of com.opengamma.analytics.financial.model.volatility.smile.function.SABRHaganVolatilityFunctionTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.