SABRExtrapolationRightFunction extends SABRExtrapolationLeftRightFunction { public double price(final EuropeanVanillaOption option) { double p = 0.0; final double k = option.getStrike(); if (k <= _cutOffStrike) { final Function1D<SABRFormulaData, Double> funcSabr = getVolatilityFunction().getVolatilityFunction(option, _forward); final double volatility = funcSabr.evaluate(_sabrData); final BlackFunctionData dataBlack = new BlackFunctionData(_forward, 1.0, volatility); final Function1D<BlackFunctionData, Double> funcBlack = BLACK_FUNCTION.getPriceFunction(option); p = funcBlack.evaluate(dataBlack); } else { p = extrapolation(k); if (!option.isCall()) { p = p - (_forward - option.getStrike()); } } return p; } SABRExtrapolationRightFunction(final double forward, final SABRFormulaData sabrData, final double cutOffStrike, final double timeToExpiry, final double mu);  SABRExtrapolationRightFunction(final double forward, final SABRFormulaData sabrData, final double cutOffStrike, final double timeToExpiry, final double mu,
      final VolatilityFunctionProvider<SABRFormulaData> volatilityFunction); double price(final EuropeanVanillaOption option); double priceDerivativeStrike(final EuropeanVanillaOption option); double priceDerivativeForward(final EuropeanVanillaOption option); double priceAdjointSABR(final EuropeanVanillaOption option, final double[] priceDerivativeSABR); SABRFormulaData getSabrData(); double getCutOffStrike(); double getMu(); double getTimeToExpiry(); double[] getParameter(); double[] getParameterDerivativeForward(); double[][] getParameterDerivativeSABR();  }
@Test public void price() { double strikeIn = 0.08; double strikeAt = CUT_OFF_STRIKE; double strikeOut = 0.12; EuropeanVanillaOption optionIn = new EuropeanVanillaOption(strikeIn, TIME_TO_EXPIRY, true); EuropeanVanillaOption optionAt = new EuropeanVanillaOption(strikeAt, TIME_TO_EXPIRY, true); EuropeanVanillaOption optionOut = new EuropeanVanillaOption(strikeOut, TIME_TO_EXPIRY, true); Function1D<SABRFormulaData, Double> funcSabrIn = SABR_FUNCTION.getVolatilityFunction(optionIn, FORWARD); double volatilityIn = funcSabrIn.evaluate(SABR_DATA); BlackFunctionData dataBlackIn = new BlackFunctionData(FORWARD, 1.0, volatilityIn); Function1D<BlackFunctionData, Double> funcBlackIn = BLACK_FUNCTION.getPriceFunction(optionIn); double priceExpectedIn = funcBlackIn.evaluate(dataBlackIn); double priceIn = SABR_EXTRAPOLATION.price(optionIn); assertEquals("SABR extrapolation, below cut-off", priceExpectedIn, priceIn, 1E-10); Function1D<SABRFormulaData, Double> funcSabrAt = SABR_FUNCTION.getVolatilityFunction(optionAt, FORWARD); double volatilityAt = funcSabrAt.evaluate(SABR_DATA); BlackFunctionData dataBlackAt = new BlackFunctionData(FORWARD, 1.0, volatilityAt); Function1D<BlackFunctionData, Double> funcBlackAt = BLACK_FUNCTION.getPriceFunction(optionAt); double priceExpectedAt = funcBlackAt.evaluate(dataBlackAt); double priceAt = SABR_EXTRAPOLATION.price(optionAt); assertEquals("SABR extrapolation, at cut-off", priceExpectedAt, priceAt, 1E-10); double priceOut = SABR_EXTRAPOLATION.price(optionOut); double priceExpectedOut = 5.427104E-5; assertEquals("SABR extrapolation, above cut-off", priceExpectedOut, priceOut, 1E-10); }
@Test public void priceCloseToExpiry() { double[] timeToExpiry = {1.0 / 365, 0.0 }; double strikeIn = 0.08; double strikeAt = CUT_OFF_STRIKE; double strikeOut = 0.12; for (int loopexp = 0; loopexp < timeToExpiry.length; loopexp++) { SABRExtrapolationRightFunction sabrExtra = new SABRExtrapolationRightFunction(FORWARD, SABR_DATA, CUT_OFF_STRIKE, timeToExpiry[loopexp], MU); EuropeanVanillaOption optionIn = new EuropeanVanillaOption(strikeIn, timeToExpiry[loopexp], true); EuropeanVanillaOption optionAt = new EuropeanVanillaOption(strikeAt, timeToExpiry[loopexp], true); EuropeanVanillaOption optionOut = new EuropeanVanillaOption(strikeOut, timeToExpiry[loopexp], true); Function1D<SABRFormulaData, Double> funcSabrIn = SABR_FUNCTION.getVolatilityFunction(optionIn, FORWARD); double volatilityIn = funcSabrIn.evaluate(SABR_DATA); BlackFunctionData dataBlackIn = new BlackFunctionData(FORWARD, 1.0, volatilityIn); Function1D<BlackFunctionData, Double> funcBlackIn = BLACK_FUNCTION.getPriceFunction(optionIn); double priceExpectedIn = funcBlackIn.evaluate(dataBlackIn); double priceIn = sabrExtra.price(optionIn); assertEquals("SABR extrapolation, below cut-off", priceExpectedIn, priceIn, TOLERANCE_PRICE); Function1D<SABRFormulaData, Double> funcSabrAt = SABR_FUNCTION.getVolatilityFunction(optionAt, FORWARD); double volatilityAt = funcSabrAt.evaluate(SABR_DATA); BlackFunctionData dataBlackAt = new BlackFunctionData(FORWARD, 1.0, volatilityAt); Function1D<BlackFunctionData, Double> funcBlackAt = BLACK_FUNCTION.getPriceFunction(optionAt); double priceExpectedAt = funcBlackAt.evaluate(dataBlackAt); double priceAt = sabrExtra.price(optionAt); assertEquals("SABR extrapolation, at cut-off", priceExpectedAt, priceAt, TOLERANCE_PRICE); double priceOut = sabrExtra.price(optionOut); double priceExpectedOut = 0.0; assertEquals("SABR extrapolation, above cut-off", priceExpectedOut, priceOut, TOLERANCE_PRICE); } }
@Test public void pricePutCallParity() { double strikeIn = 0.08; double strikeAt = CUT_OFF_STRIKE; double strikeOut = 0.12; EuropeanVanillaOption callIn = new EuropeanVanillaOption(strikeIn, TIME_TO_EXPIRY, true); EuropeanVanillaOption putIn = new EuropeanVanillaOption(strikeIn, TIME_TO_EXPIRY, false); EuropeanVanillaOption callAt = new EuropeanVanillaOption(strikeAt, TIME_TO_EXPIRY, true); EuropeanVanillaOption putAt = new EuropeanVanillaOption(strikeAt, TIME_TO_EXPIRY, false); EuropeanVanillaOption callOut = new EuropeanVanillaOption(strikeOut, TIME_TO_EXPIRY, true); EuropeanVanillaOption putOut = new EuropeanVanillaOption(strikeOut, TIME_TO_EXPIRY, false); double priceCallIn = SABR_EXTRAPOLATION.price(callIn); double pricePutIn = SABR_EXTRAPOLATION.price(putIn); assertEquals("SABR extrapolation, below cut-off: put/call parity", FORWARD - strikeIn, priceCallIn - pricePutIn, 1E-10); double priceCallAt = SABR_EXTRAPOLATION.price(callAt); double pricePutAt = SABR_EXTRAPOLATION.price(putAt); assertEquals("SABR extrapolation, at cut-off: put/call parity", FORWARD - strikeAt, priceCallAt - pricePutAt, 1E-10); double priceCallOut = SABR_EXTRAPOLATION.price(callOut); double pricePutOut = SABR_EXTRAPOLATION.price(putOut); assertEquals("SABR extrapolation, above cut-off: put/call parity", FORWARD - strikeOut, priceCallOut - pricePutOut, 1E-10); }
@Test public void smileSmooth() { int nbPts = 100; double rangeStrike = 0.02; double[] price = new double[nbPts + 1]; double[] strike = new double[nbPts + 1]; for (int looppts = 0; looppts <= nbPts; looppts++) { strike[looppts] = CUT_OFF_STRIKE - rangeStrike + looppts * 2.0 * rangeStrike / nbPts; EuropeanVanillaOption option = new EuropeanVanillaOption(strike[looppts], TIME_TO_EXPIRY, true); price[looppts] = SABR_EXTRAPOLATION.price(option); } double[] priceD = new double[nbPts]; double[] priceD2 = new double[nbPts]; for (int looppts = 1; looppts < nbPts; looppts++) { priceD[looppts] = (price[looppts + 1] - price[looppts - 1]) / (strike[looppts + 1] - strike[looppts - 1]); priceD2[looppts] = (price[looppts + 1] + price[looppts - 1] - 2 * price[looppts]) / ((strike[looppts + 1] - strike[looppts]) * (strike[looppts + 1] - strike[looppts])); } for (int looppts = 2; looppts < nbPts; looppts++) { assertEquals("SABR extrapolation, smooth first derivative", priceD[looppts - 1], priceD[looppts], 1.5E-3); assertEquals("SABR extrapolation, smooth second derivative", priceD2[looppts - 1], priceD2[looppts], 1.5E-1); } }
@Test public void smileSmoothMaturity() { int nbPts = 100; double[] timeToExpiry = new double[] {2.0, 1.0, 0.50, 0.25, 1.0d / 12.0d, 1.0d / 52.0d, 1.0d / 365d }; int nbTTM = timeToExpiry.length; double rangeStrike = 0.02; double[] strike = new double[nbPts + 1]; for (int looppts = 0; looppts <= nbPts; looppts++) { strike[looppts] = CUT_OFF_STRIKE - rangeStrike + looppts * 2.0 * rangeStrike / nbPts; } SABRExtrapolationRightFunction[] sabrExtrapolation = new SABRExtrapolationRightFunction[nbTTM]; for (int loopmat = 0; loopmat < nbTTM; loopmat++) { sabrExtrapolation[loopmat] = new SABRExtrapolationRightFunction(FORWARD, SABR_DATA, CUT_OFF_STRIKE, timeToExpiry[loopmat], MU); } double[][] price = new double[nbTTM][nbPts + 1]; for (int loopmat = 0; loopmat < nbTTM; loopmat++) { for (int looppts = 0; looppts <= nbPts; looppts++) { EuropeanVanillaOption option = new EuropeanVanillaOption(strike[looppts], timeToExpiry[loopmat], true); price[loopmat][looppts] = sabrExtrapolation[loopmat].price(option); } } double[][] priceD = new double[nbTTM][nbPts - 1]; double[][] priceD2 = new double[nbTTM][nbPts - 1]; for (int loopmat = 0; loopmat < nbTTM; loopmat++) { for (int looppts = 1; looppts < nbPts; looppts++) { priceD[loopmat][looppts - 1] = (price[loopmat][looppts + 1] - price[loopmat][looppts - 1]) / (strike[looppts + 1] - strike[looppts - 1]); priceD2[loopmat][looppts - 1] = (price[loopmat][looppts + 1] + price[loopmat][looppts - 1] - 2 * price[loopmat][looppts]) / ((strike[looppts + 1] - strike[looppts]) * (strike[looppts + 1] - strike[looppts])); } } double epsDensity = 1.0E-20; for (int loopmat = 0; loopmat < nbTTM; loopmat++) { for (int looppts = 1; looppts < nbPts - 1; looppts++) { assertTrue("SABR extrapolation, smooth first derivative - mat " + loopmat + " / pt " + looppts + " [" + priceD[loopmat][looppts] + "/" + priceD[loopmat][looppts - 1] + "]", ((priceD[loopmat][looppts] / priceD[loopmat][looppts - 1] < 1) && (priceD[loopmat][looppts] / priceD[loopmat][looppts - 1] > 0.50)) || Math.abs(priceD2[loopmat][looppts]) < epsDensity); assertTrue("SABR extrapolation, positive second derivative - mat " + loopmat + " / pt " + looppts + " [" + priceD2[loopmat][looppts] + "]", priceD2[loopmat][looppts] > 0 || Math.abs(priceD2[loopmat][looppts]) < epsDensity); assertTrue("SABR extrapolation, smooth second derivative - mat " + loopmat + " / pt " + looppts + " [" + priceD2[loopmat][looppts] + "/" + priceD2[loopmat][looppts - 1] + "]", (priceD2[loopmat][looppts] / priceD2[loopmat][looppts - 1] < 1 && priceD2[loopmat][looppts] / priceD2[loopmat][looppts - 1] > 0.50) || Math.abs(priceD2[loopmat][looppts]) < epsDensity); } } }
@Test(enabled = false) public void smileMultiMu() { double[] mu = new double[] {5.0, 40.0, 90.0, 150.0 }; int nbMu = mu.length; int nbPts = 100; double rangeStrike = 0.02; double[] strike = new double[nbPts + 1]; double[][] price = new double[nbMu][nbPts + 1]; double[][] impliedVolatility = new double[nbMu][nbPts + 1]; BlackImpliedVolatilityFormula implied = new BlackImpliedVolatilityFormula(); BlackFunctionData blackData = new BlackFunctionData(FORWARD, 1.0, 0.0); for (int loopmu = 0; loopmu < nbMu; loopmu++) { SABRExtrapolationRightFunction sabrExtra = new SABRExtrapolationRightFunction(FORWARD, SABR_DATA, CUT_OFF_STRIKE, TIME_TO_EXPIRY, mu[loopmu]); for (int looppts = 0; looppts <= nbPts; looppts++) { strike[looppts] = CUT_OFF_STRIKE - rangeStrike + looppts * 4.0 * rangeStrike / nbPts; EuropeanVanillaOption option = new EuropeanVanillaOption(strike[looppts], TIME_TO_EXPIRY, true); price[loopmu][looppts] = sabrExtra.price(option); impliedVolatility[loopmu][looppts] = implied.getImpliedVolatility(blackData, option, price[loopmu][looppts]); } } }
@Test public void smoothnessTest() { for (VolatilityFunctionProvider<SABRFormulaData> func : FUNCTIONS) { SABRExtrapolationRightFunction extrapolation = new SABRExtrapolationRightFunction(FORWARD, SABR_DATA, CUT_OFF_STRIKE, TIME_TO_EXPIRY, MU, func); for (boolean isCall : new boolean[] {true, false }) { EuropeanVanillaOption optionBase = new EuropeanVanillaOption(CUT_OFF_STRIKE, TIME_TO_EXPIRY, isCall); EuropeanVanillaOption optionUp = new EuropeanVanillaOption(CUT_OFF_STRIKE + EPS, TIME_TO_EXPIRY, isCall); EuropeanVanillaOption optionDw = new EuropeanVanillaOption(CUT_OFF_STRIKE - EPS, TIME_TO_EXPIRY, isCall); double priceBase = extrapolation.price(optionBase); double priceUp = extrapolation.price(optionUp); double priceDw = extrapolation.price(optionDw); assertEquals(priceBase, priceUp, EPS); assertEquals(priceBase, priceDw, EPS); EuropeanVanillaOption optionUpUp = new EuropeanVanillaOption(CUT_OFF_STRIKE + 2.0 * EPS, TIME_TO_EXPIRY, isCall); EuropeanVanillaOption optionDwDw = new EuropeanVanillaOption(CUT_OFF_STRIKE - 2.0 * EPS, TIME_TO_EXPIRY, isCall); double priceUpUp = extrapolation.price(optionUpUp); double priceDwDw = extrapolation.price(optionDwDw); double firstUp = (-0.5 * priceUpUp + 2.0 * priceUp - 1.5 * priceBase) / EPS; double firstDw = (-2.0 * priceDw + 0.5 * priceDwDw + 1.5 * priceBase) / EPS; assertEquals(firstDw, firstUp, EPS); double firstUpUp = 0.5 * (priceUpUp - priceBase) / EPS; double firstDwDw = 0.5 * (priceBase - priceDwDw) / EPS; double secondUp = (firstUpUp - firstUp) / EPS; double secondDw = (firstDw - firstDwDw) / EPS; double secondRef = 0.5 * (firstUpUp - firstDwDw) / EPS; assertEquals(secondRef, secondUp, secondRef * 0.15); assertEquals(secondRef, secondDw, secondRef * 0.15); } } }
@Test public void smallForwardTest() { double smallForward = 0.1e-6; double smallCutoff = 0.9e-6; for (VolatilityFunctionProvider<SABRFormulaData> func : FUNCTIONS) { SABRExtrapolationRightFunction right = new SABRExtrapolationRightFunction(smallForward, SABR_DATA, smallCutoff, TIME_TO_EXPIRY, MU, func); EuropeanVanillaOption optionBase = new EuropeanVanillaOption(smallCutoff, TIME_TO_EXPIRY, false); EuropeanVanillaOption optionUp = new EuropeanVanillaOption(smallCutoff + EPS * 0.1, TIME_TO_EXPIRY, false); EuropeanVanillaOption optionDw = new EuropeanVanillaOption(smallCutoff - EPS * 0.1, TIME_TO_EXPIRY, false); double priceBase = right.price(optionBase); double priceUp = right.price(optionUp); double priceDw = right.price(optionDw); assertEquals(priceBase, priceUp, EPS * 10.0); assertEquals(priceBase, priceDw, EPS * 10.0); } }