@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() {
   double timeToExpiry = 1;
   double alpha = 0.05;
   double beta = 0.5;
   double nu = 0.50;
   double rho = -0.25;
   int nbPoints = 100;
   double forward = 0.05;
   double[] sabrVolatilty = new double[2 * nbPoints + 1];
   double range = 5E-9;
   double strike[] = new double[2 * nbPoints + 1];
   for (int looppts = -nbPoints; looppts <= nbPoints; looppts++) {
     strike[looppts + nbPoints] = forward + ((double) looppts) / nbPoints * range;
     SabrFormulaData SabrData = SabrFormulaData.of(alpha, beta, rho, nu);
     sabrVolatilty[looppts + nbPoints] =
         FUNCTION.getVolatility(forward, strike[looppts + nbPoints], timeToExpiry, 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 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() {
   double eps = 1e-5;
   double tol = 1e-6;
   SabrFormulaData data = DATA.withAlpha(0.0);
   testVolatilityAdjoint(F, CALL_ATM, data, eps, tol);
   double volatility = FUNCTION.getVolatility(F, STRIKE_ITM, T, data);
   ValueDerivatives volatilityAdjoint = FUNCTION.getVolatilityAdjoint(F, STRIKE_ITM, T, data);
   assertEquals(volatility, volatilityAdjoint.getValue(), tol);
   assertEquals(0.0, volatilityAdjoint.getDerivative(0), tol);
   assertEquals(0.0, volatilityAdjoint.getDerivative(1), tol);
   assertEquals(1e7, volatilityAdjoint.getDerivative(2), tol);
   assertEquals(0.0, volatilityAdjoint.getDerivative(3), tol);
   assertEquals(0.0, volatilityAdjoint.getDerivative(4), tol);
   assertEquals(0.0, volatilityAdjoint.getDerivative(5), tol);
 }
 private void testVolatilityAdjoint(
     double forward,
     EuropeanVanillaOption optionData,
     SabrFormulaData sabrData,
     double eps,
     double tol) {
   double volatility =
       FUNCTION.getVolatility(
           forward, optionData.getStrike(), optionData.getTimeToExpiry(), sabrData);
   double[] volatilityAdjoint =
       toArray(
           FUNCTION.getVolatilityAdjoint(
               forward, optionData.getStrike(), optionData.getTimeToExpiry(), sabrData));
   assertEquals(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);
 }
  /** Calculate the true SABR delta and gamma and compare with that found by finite difference */
  @Test(enabled = false)
  public void testGreeks() {
    double eps = 1e-3;
    double f = 1.2;
    double k = 1.4;
    double t = 5.0;
    double alpha = 0.3;
    double beta = 0.6;
    double rho = -0.4;
    double nu = 0.4;
    SabrFormulaData sabrData = SabrFormulaData.of(alpha, beta, rho, nu);
    ValueDerivatives adj = FUNCTION.getVolatilityAdjoint(f, k, t, sabrData);
    double bsDelta = BlackFormulaRepository.delta(f, k, t, adj.getValue(), true);
    double bsVega = BlackFormulaRepository.vega(f, k, t, adj.getValue());
    double volForwardSense = adj.getDerivative(1);
    double delta = bsDelta + bsVega * volForwardSense;

    SabrFormulaData data = SabrFormulaData.of(alpha, beta, rho, nu);
    double volUp = FUNCTION.getVolatility(f + eps, k, t, data);
    double volDown = FUNCTION.getVolatility(f - eps, k, t, data);
    double priceUp = BlackFormulaRepository.price(f + eps, k, t, volUp, true);
    double price = BlackFormulaRepository.price(f, k, t, adj.getValue(), true);
    double priceDown = BlackFormulaRepository.price(f - eps, k, t, volDown, true);
    double fdDelta = (priceUp - priceDown) / 2 / eps;
    assertEquals(fdDelta, delta, 1e-6);

    double bsVanna = BlackFormulaRepository.vanna(f, k, t, adj.getValue());
    double bsGamma = BlackFormulaRepository.gamma(f, k, t, adj.getValue());

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

    double d2Sigmad2FwdFD = (volUp + volDown - 2 * adj.getValue()) / eps / eps;
    assertEquals(d2Sigmad2FwdFD, d2Sigmad2Fwd, 1e-4);

    assertEquals(fdGamma, gamma, 1e-2);
  }
 private void volatilityAdjoint2ForInstrument(
     EuropeanVanillaOption option, double tolerance1, double tolerance2) {
   // vol
   double volatility =
       FUNCTION.getVolatility(F, option.getStrike(), option.getTimeToExpiry(), DATA);
   double[] volatilityAdjoint =
       toArray(
           FUNCTION.getVolatilityAdjoint(F, option.getStrike(), option.getTimeToExpiry(), DATA));
   double[] volD = new double[6];
   double[][] volD2 = new double[2][2];
   double vol =
       FUNCTION.getVolatilityAdjoint2(
           F, option.getStrike(), option.getTimeToExpiry(), DATA, volD, volD2);
   assertEquals(volatility, vol, tolerance1);
   // Derivative
   for (int loopder = 0; loopder < 6; loopder++) {
     assertEquals(volatilityAdjoint[loopder + 1], volD[loopder], tolerance1);
   }
   // Derivative forward-forward
   double deltaF = 0.000001;
   double volatilityFP =
       FUNCTION.getVolatility(F + deltaF, option.getStrike(), option.getTimeToExpiry(), DATA);
   double volatilityFM =
       FUNCTION.getVolatility(F - deltaF, option.getStrike(), option.getTimeToExpiry(), DATA);
   double derivativeFF_FD = (volatilityFP + volatilityFM - 2 * volatility) / (deltaF * deltaF);
   assertEquals(derivativeFF_FD, volD2[0][0], tolerance2);
   // Derivative strike-strike
   double deltaK = 0.000001;
   double volatilityKP =
       FUNCTION.getVolatility(F, option.getStrike() + deltaK, option.getTimeToExpiry(), DATA);
   double volatilityKM =
       FUNCTION.getVolatility(F, option.getStrike() - deltaK, option.getTimeToExpiry(), DATA);
   double derivativeKK_FD = (volatilityKP + volatilityKM - 2 * volatility) / (deltaK * deltaK);
   assertEquals(derivativeKK_FD, volD2[1][1], tolerance2);
   // Derivative strike-forward
   double volatilityFPKP =
       FUNCTION.getVolatility(
           F + deltaF, option.getStrike() + deltaK, option.getTimeToExpiry(), DATA);
   double derivativeFK_FD =
       (volatilityFPKP + volatility - volatilityFP - volatilityKP) / (deltaF * deltaK);
   assertEquals(derivativeFK_FD, volD2[0][1], tolerance2);
   assertEquals(volD2[0][1], volD2[1][0], 1E-6);
 }
  /**
   * Check that $\rho \simeq 1$ case is smoothly connected with a general case, i.e., comparing the
   * approximated computation and full computation around the cutoff, which is currently $\rho = 1.0
   * - 1.0e-5$ Note that the resulting numbers contain a large error if $\rho \simeq 1$ and $z
   * \simeq 1$ are true at the same time
   */
  @Test
  public void largeRhoSmoothnessTest() {
    double rhoEps = 1.e-5;
    // rhoIn is larger than the cutoff,
    // thus vol and sensitivities are computed by approximation formulas which are regular in the
    // limit rho -> 1.
    double rhoIn = 1.0 - 0.5 * rhoEps;
    // rhoOut is smaller than the cutoff, thus vol and sensitivities are computed by full formula.
    double rhoOut = 1.0 - 1.5 * rhoEps;
    SabrFormulaData dataIn = SabrFormulaData.of(ALPHA, BETA, rhoIn, NU);
    SabrFormulaData dataOut = SabrFormulaData.of(ALPHA, BETA, rhoOut, NU);

    /*
     * z<1 case, i.e., finite values in the rho->1 limit
     */
    double volatilityOut = FUNCTION.getVolatility(F, STRIKE_OTM, T, dataOut);
    double[] adjointOut = toArray(FUNCTION.getVolatilityAdjoint(F, STRIKE_OTM, T, dataOut));
    double[] volatilityDOut = new double[6];
    double[][] volatilityD2Out = new double[2][2];
    double volatility2Out =
        FUNCTION.getVolatilityAdjoint2(F, STRIKE_OTM, T, dataOut, volatilityDOut, volatilityD2Out);

    double volatilityIn = FUNCTION.getVolatility(F, STRIKE_OTM, T, dataIn);
    double[] adjointIn = toArray(FUNCTION.getVolatilityAdjoint(F, STRIKE_OTM, T, dataIn));
    double[] volatilityDIn = new double[6];
    double[][] volatilityD2In = new double[2][2];
    double volatility2In =
        FUNCTION.getVolatilityAdjoint2(F, STRIKE_OTM, T, dataIn, volatilityDIn, volatilityD2In);

    assertEquals(volatilityOut, volatilityIn, rhoEps);
    assertEquals(volatility2Out, volatility2In, rhoEps);
    for (int i = 0; i < adjointOut.length; ++i) {
      double ref = adjointOut[i];
      assertEquals(adjointOut[i], adjointIn[i], Math.max(Math.abs(ref), 1.0) * 1.e-3);
    }
    for (int i = 0; i < volatilityDOut.length; ++i) {
      double ref = volatilityDOut[i];
      assertEquals(volatilityDOut[i], volatilityDIn[i], Math.max(Math.abs(ref), 1.0) * 1.e-3);
    }

    /*
     * z>1 case, runs into infinity or 0.
     * Convergence speed is much faster (and typically smoother).
     */
    rhoIn = 1.0 - 0.999 * rhoEps;
    rhoOut = 1.0 - 1.001 * rhoEps;
    dataIn = SabrFormulaData.of(ALPHA, BETA, rhoIn, NU);
    dataOut = SabrFormulaData.of(ALPHA, BETA, rhoOut, NU);

    volatilityOut = FUNCTION.getVolatility(3.0 * F, STRIKE_ITM, T, dataOut);
    adjointOut = toArray(FUNCTION.getVolatilityAdjoint(3.0 * F, STRIKE_ITM, T, dataOut));
    volatilityDOut = new double[6];
    volatilityD2Out = new double[2][2];
    volatility2Out =
        FUNCTION.getVolatilityAdjoint2(
            3.0 * F, STRIKE_ITM, T, dataOut, volatilityDOut, volatilityD2Out);

    volatilityIn = FUNCTION.getVolatility(3.0 * F, STRIKE_ITM, T, dataIn);
    adjointIn = toArray(FUNCTION.getVolatilityAdjoint(3.0 * F, STRIKE_ITM, T, dataIn));
    volatilityDIn = new double[6];
    volatilityD2In = new double[2][2];
    volatility2In =
        FUNCTION.getVolatilityAdjoint2(
            3.0 * F, STRIKE_ITM, T, dataIn, volatilityDIn, volatilityD2In);

    assertEquals(volatilityOut, volatilityIn, rhoEps);
    assertEquals(volatility2Out, volatility2In, rhoEps);
    for (int i = 0; i < adjointOut.length; ++i) {
      double ref = adjointOut[i];
      assertEquals(ref, adjointIn[i], Math.max(Math.abs(ref), 1.0) * 1.e-2);
    }
    for (int i = 0; i < volatilityDOut.length; ++i) {
      double ref = volatilityDOut[i];
      assertEquals(ref, volatilityDIn[i], Math.max(Math.abs(ref), 1.0) * 1.e-2);
    }
  }