CEVPriceFunction implements OptionPriceFunction<CEVFunctionData> { @Override public Function1D<CEVFunctionData, Double> getPriceFunction(final EuropeanVanillaOption option) { final double k = option.getStrike(); final double t = option.getTimeToExpiry(); final boolean isCall = option.isCall(); return new Function1D<CEVFunctionData, Double>() { @SuppressWarnings("synthetic-access") @Override public Double evaluate(final CEVFunctionData data) { Validate.notNull(data, "data"); final double forward = data.getForward(); final double numeraire = data.getNumeraire(); final double sigma = data.getVolatility(); final double beta = data.getBeta(); if (beta == 1.0) { final Function1D<BlackFunctionData, Double> blackFormula = BLACK_PRICE_FUNCTION.getPriceFunction(option); return blackFormula.evaluate(new BlackFunctionData(forward, numeraire, sigma)); } if (beta == 0.0) { final Function1D<NormalFunctionData, Double> normalFormula = NORMAL_PRICE_FUNCTION.getPriceFunction(option); return normalFormula.evaluate(new NormalFunctionData(forward, numeraire, sigma)); } final double b = 1.0 / (1 - beta); final double x = b * b / sigma / sigma / t; final double a = Math.pow(k, 2 * (1 - beta)) * x; final double c = Math.pow(forward, 2 * (1 - beta)) * x; if (beta < 1) { final NonCentralChiSquaredDistribution chiSq1 = new NonCentralChiSquaredDistribution(b + 2, c); final NonCentralChiSquaredDistribution chiSq2 = new NonCentralChiSquaredDistribution(b, a); if (isCall) { return numeraire * (forward * (1 - chiSq1.getCDF(a)) - k * chiSq2.getCDF(c)); } return numeraire * (k * (1 - chiSq2.getCDF(c)) - forward * chiSq1.getCDF(a)); } final NonCentralChiSquaredDistribution chiSq1 = new NonCentralChiSquaredDistribution(-b, a); final NonCentralChiSquaredDistribution chiSq2 = new NonCentralChiSquaredDistribution(2 - b, c); if (isCall) { return numeraire * (forward * (1 - chiSq1.getCDF(c)) - k * chiSq2.getCDF(a)); } return numeraire * (k * (1 - chiSq2.getCDF(a)) - forward * chiSq1.getCDF(c)); } }; }  @Override Function1D<CEVFunctionData, Double> getPriceFunction(final EuropeanVanillaOption option);  }
@Test public void testBeta() { final double f = 4; final double k = 3.5; final double atmVol = 0.3; final double t = 0.1; double beta; for (int i = 0; i < 200; i++) { beta = i / 100.0; final double sigma = atmVol * Math.pow(f, 1 - beta); final EuropeanVanillaOption option = new EuropeanVanillaOption(k, t, true); final CEVFunctionData cevData = new CEVFunctionData(f, 1.0, sigma, beta); final double price = CEV.getPriceFunction(option).evaluate(cevData); final double vol = BLACK_IMPLIED_VOL.getImpliedVolatility(new BlackFunctionData(f, 1.0, sigma), option, price); final SABRFormulaData sabrData = new SABRFormulaData(sigma, beta, 0.0, 0.0); final double sabrVol = SABR.getVolatilityFunction(option, f).evaluate(sabrData); assertEquals(sabrVol, vol, 1e-4); } }
@Test public void testStrike() { final double f = 4; double k; final double atmVol = 0.3; final double t = 0.5; final double beta = 0.5; final double sigma = atmVol * Math.pow(f, 1 - beta); for (int i = 0; i < 20; i++) { k = 1.0 + i / 2.5; final EuropeanVanillaOption option = new EuropeanVanillaOption(k, t, true); final CEVFunctionData cevData = new CEVFunctionData(f, 1.0, sigma, beta); final double price = CEV.getPriceFunction(option).evaluate(cevData); final double vol = BLACK_IMPLIED_VOL.getImpliedVolatility(new BlackFunctionData(f, 1.0, sigma), option, price); final SABRFormulaData sabrData = new SABRFormulaData(sigma, beta, 0.0, 0.0); final double sabrVol = SABR.getVolatilityFunction(option, f).evaluate(sabrData); assertEquals(sabrVol, vol, 1e-4); } }
@Test public void testBetaAndStrike() { final double f = 4; double k; final double atmVol = 0.3; final double t = 0.1; double beta; double sigma; for (int i = 0; i < 20; i++) { beta = (i + 1) / 20.0; sigma = atmVol * Math.pow(f, 1 - beta); for (int j = 0; j < 20; j++) { k = 3.0 + j / 10.0; final EuropeanVanillaOption option = new EuropeanVanillaOption(k, t, true); final CEVFunctionData cevData = new CEVFunctionData(f, 1.0, sigma, beta); final double price = CEV.getPriceFunction(option).evaluate(cevData); final double vol = BLACK_IMPLIED_VOL.getImpliedVolatility(new BlackFunctionData(f, 1.0, sigma), option, price); final SABRFormulaData sabrData = new SABRFormulaData(sigma, beta, 0.0, 0.0); final double sabrVol = SABR.getVolatilityFunction(option, f).evaluate(sabrData); assertEquals(sabrVol, vol, 1e-4); } } }
@Test(enabled = false) public void funnySmileTest() { final double beta = 0.4; final double t = 5.0; final double r = 0.0; final double spot = 100; final double k = spot * Math.exp(-r * t); final double atmVol = 0.20; final double volBeta = atmVol * Math.pow(k, 1 - beta); final EuropeanVanillaOption option = new EuropeanVanillaOption(k, t, true); for (int i = 0; i < 101; i++) { final double f = 350.0 + 1.0 * i; final CEVFunctionData cevData = new CEVFunctionData(f, 1.0, volBeta, beta); final double cevPrice = CEV.getPriceFunction(option).evaluate(cevData); @SuppressWarnings("unused") final double cevVol = BLACK_IMPLIED_VOL.getImpliedVolatility(new BlackFunctionData(f, 1.0, volBeta), option, cevPrice); } }
@Test public void testDebug() { final double beta = 0.4; final double t = 5.0; final double r = 0.1; final double forward = 163.10; final double k = 164.87; final double volBeta = 4.298; final EuropeanVanillaOption option = new EuropeanVanillaOption(k, t, true); final CEVFunctionData cevData = new CEVFunctionData(forward, Math.exp(-r * t), volBeta, beta); final double cevPrice = CEV.getPriceFunction(option).evaluate(cevData); @SuppressWarnings("unused") final double cevVol = BLACK_IMPLIED_VOL.getImpliedVolatility(new BlackFunctionData(forward, Math.exp(-r * t), volBeta), option, cevPrice); }