FFTPricer { public double[][] price(final double forward, final double discountFactor, final double t, final boolean isCall, final MartingaleCharacteristicExponent ce, final double lowestStrike, final double highestStrike, final int minStrikesDisplayed, final double limitSigma, final double alpha, final double tol) { Validate.notNull(ce, "characteristic exponent"); Validate.isTrue(tol > 0.0, "need tol > 0"); Validate.isTrue(alpha != 0.0 && alpha != -1.0, "alpha cannot be -1 or 0"); Validate.isTrue(highestStrike >= lowestStrike, "need highestStrike >= lowestStrike"); Validate.isTrue(limitSigma > 0.0, "need limitSigma > 0"); double kMax; final double limitSigmaRootT = limitSigma * Math.sqrt(t); final double atm = NORMAL.getCDF(limitSigmaRootT / 2.0); if (alpha > 0) { kMax = -Math.log((2 * atm - 1) * tol) / alpha; } else if (alpha < -1.0) { kMax = Math.log((2 * atm - 1) * tol) / (1 + alpha); } else { kMax = -Math.log(2 * (1 - atm) * tol) * Math.max(-1.0 / alpha, 1 / (1 + alpha)); } final EuropeanCallFourierTransform psi = new EuropeanCallFourierTransform(ce); final Function1D<ComplexNumber, ComplexNumber> psiFunction = psi.getFunction(t); final double xMax = LIMIT_CALCULATOR.solve(psiFunction, alpha, tol); double deltaK; if (highestStrike == lowestStrike) { deltaK = Math.PI / xMax; } else { deltaK = Math.min(Math.log(highestStrike / lowestStrike) / (minStrikesDisplayed - 1), Math.PI / xMax); } final double log2 = Math.log(2); final int twoPow = (int) Math.ceil(Math.log(kMax / deltaK) / log2); final int n = (int) Math.pow(2, twoPow); final double delta = 2 * Math.PI / n / deltaK; final int m = (int) (xMax * deltaK * n / 2 / Math.PI); final int nLowStrikes = (int) Math.max(0, Math.ceil(Math.log(forward / lowestStrike) / deltaK)); final int nHighStrikes = (int) Math.max(0, Math.ceil(Math.log(highestStrike / forward) / deltaK)); return price(forward, discountFactor, t, isCall, ce, nLowStrikes, nHighStrikes, alpha, delta, n, m); }  double[][] price(final double forward, final double discountFactor, final double t, final boolean isCall, final MartingaleCharacteristicExponent ce, final double lowestStrike,
      final double highestStrike, final int minStrikesDisplayed, final double limitSigma, final double alpha, final double tol); double[][] price(final double forward, final double discountFactor, final double t, final boolean isCall, final MartingaleCharacteristicExponent ce, final int nStrikes,
      final double maxDeltaMoneyness,
      final double limitSigma, final double alpha,
      final double tol); double[][] price(final double forward, final double discountFactor, final double t, final boolean isCall, final MartingaleCharacteristicExponent ce, final int nStrikesBelowATM,
      final int nStrikesAboveATM, final double alpha, final double delta, final int n, final int m);  }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullCharacteristicExponent1() { PRICER.price(FORWARD, DF, T, true, null, 10, 10, SIGMA, ALPHA, TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeTolerance1() { PRICER.price(FORWARD, DF, T, true, CEF, 10, 10, SIGMA, ALPHA, -TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeNStrikes() { PRICER.price(FORWARD, DF, T, true, CEF, -10, 10, SIGMA, ALPHA, TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeMaxDeltaMoneyness() { PRICER.price(FORWARD, DF, T, true, CEF, 10, -10, SIGMA, ALPHA, TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeVol1() { PRICER.price(FORWARD, DF, T, true, CEF, 10, 10, -0.3, ALPHA, TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testZeroAlpha1() { PRICER.price(FORWARD, DF, T, true, CEF, 10, 10, SIGMA, 0, TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullCharacteristicExponent2() { PRICER.price(FORWARD, DF, T, true, null, 10, 110, 10, SIGMA, ALPHA, TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeTolerance2() { PRICER.price(FORWARD, DF, T, true, CEF, 10, 110, 10, SIGMA, ALPHA, -TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeVol2() { PRICER.price(FORWARD, DF, T, true, CEF, 10, 110, 10, -0.5, ALPHA, TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testZeroAlpha2() { PRICER.price(FORWARD, DF, T, true, CEF, 10, 110, 10, SIGMA, 0, TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testWrongLowStrike() { PRICER.price(FORWARD, DF, T, true, CEF, FORWARD + 11, 110, 10, SIGMA, ALPHA, TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testWrongHighStrike() { PRICER.price(FORWARD, DF, T, true, CEF, FORWARD, FORWARD-1, 10, SIGMA, ALPHA, TOL); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullCharacteristicExponent3() { PRICER.price(FORWARD, DF, T, true, null, 10, 10, ALPHA, 0.5, 64, 20); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testZeroAlpha3() { PRICER.price(FORWARD, DF, T, true, CEF, 10, 10, 0.0, 0.5, 64, 20); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeStrikesAboveATM() { PRICER.price(FORWARD, DF, T, true, CEF, 10, -10, ALPHA, 0.5, 64, 20); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeStrikesBelowATM() { PRICER.price(FORWARD, DF, T, true, CEF, 10, -10, ALPHA, 0.5, 64, 20); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeDelta() { PRICER.price(FORWARD, DF, T, true, CEF, 10, 10, ALPHA, -0.5, 64, 20); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeM() { PRICER.price(FORWARD, DF, T, true, CEF, 10, 10, ALPHA, 0.5, 64, -10); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testWrongN() { PRICER.price(FORWARD, DF, T, true, CEF, 10, 10, ALPHA, 0.5, 64, 128); }
@Test public void testLargeNumberOfStrikes() { final boolean isCall = true; final int n = 1024; final int m = 100; final double delta = 0.1; final int nL = 550; final int nH = 600; final double alpha = -0.5; final double[][] temp = PRICER.price(FORWARD, DF, T, isCall, CEF, nL, nH, alpha, delta, n, m); assertEquals(n + 1, temp.length); }
@Test public void test() { final boolean isCall = true; final int nStrikes = 21; final double deltaMoneyness = 0.01; final double alpha = -0.5; final double tol = 1e-10; final double[][] strikeNprice = PRICER.price(FORWARD, DF, T, isCall, CEF, nStrikes, deltaMoneyness, SIGMA, alpha, tol); assertEquals(nStrikes, strikeNprice.length); double k; double price; for (int i = 0; i < nStrikes; i++) { price = strikeNprice[i][1]; k = strikeNprice[i][0]; double impVol = 0; try { final EuropeanVanillaOption o = new EuropeanVanillaOption(k, T, true); impVol = BLACK_IMPLIED_VOL.getImpliedVolatility(DATA, o, price); } catch (final Exception e) { } assertEquals(SIGMA, impVol, 1e-5); } }
@Test public void testPutsAndCalls() { boolean isCall = true; final double tol = 1e-10; for (int i = 0; i < 5; i++) { final double alpha = -1.6 + i * 0.5; for (int j = 0; j < 1; j++) { isCall = (j == 0); final EuropeanVanillaOption option = new EuropeanVanillaOption(FORWARD, T, isCall); final double[][] strikeNprice = PRICER.price(FORWARD, DF, T, isCall, CEF, FORWARD, FORWARD, 1, 0.3, alpha, tol); assertEquals(FORWARD, strikeNprice[0][0], 1e-9); assertEquals(SIGMA, BLACK_IMPLIED_VOL.getImpliedVolatility(DATA, option, strikeNprice[0][1]), 1e-7); } } }
@Test public void testStability() { final boolean isCall = false; final int nStrikes = 21; final double kappa = 1.2; final double theta = 0.1; final double vol0 = 1.8 * theta; final double omega = 0.4; final double rho = -0.7; final double t = 2.0; final MartingaleCharacteristicExponent heston = new HestonCharacteristicExponent(kappa, theta, vol0, omega, rho); final double[][] strikeNPrice = PRICER.price(FORWARD, DF, t, isCall, heston, 0.7 * FORWARD, 1.5 * FORWARD, nStrikes, 0.3, -0.5, 1e-10); final int n = strikeNPrice.length; final double[] k = new double[n]; final double[] vol = new double[n]; for (int i = 0; i < n; i++) { k[i] = strikeNPrice[i][0]; vol[i] = BLACK_IMPLIED_VOL.getImpliedVolatility(new BlackFunctionData(FORWARD, DF, 0.0), new EuropeanVanillaOption(k[i], t, isCall), strikeNPrice[i][1]); } final Interpolator1DDataBundle dataBundle = INTERPOLATOR.getDataBundleFromSortedArrays(k, vol); final double[][] strikeNPrice2 = PRICER.price(FORWARD, DF, t, isCall, heston, 0.7 * FORWARD, 1.5 * FORWARD, nStrikes, 0.2, 0.75, 1e-8); final int m = strikeNPrice2.length; for (int i = 0; i < m; i++) { final double strike = strikeNPrice2[i][0]; final double sigma = BLACK_IMPLIED_VOL.getImpliedVolatility(new BlackFunctionData(FORWARD, DF, 0.0), new EuropeanVanillaOption(strike, t, isCall), strikeNPrice2[i][1]); assertEquals(sigma, INTERPOLATOR.interpolate(dataBundle, strike), 1e-5); } }