SmileInterpolatorSABRWithExtrapolation extends SmileInterpolatorSABR { @Override public Function1D<Double, Double> getVolatilityFunction(final double forward, final double[] strikes, final double expiry, final double[] impliedVols) { ArgumentChecker.notNull(strikes, "strikes"); ArgumentChecker.notNull(impliedVols, "impliedVols"); ArgumentChecker.isTrue(strikes.length == impliedVols.length, "strikes and impliedVols should have the same length"); final int nStrikes = strikes.length; final double cutOffStrikeLow = strikes[0]; final double cutOffStrikeHigh = strikes[nStrikes - 1]; SABRFormulaData sabrDataLow; SABRFormulaData sabrDataHigh; final List<SABRFormulaData> modelParams; final int n; List<SABRFormulaData> modelParamsTmp; int nTmp; try { modelParamsTmp = getFittedModelParameters(forward, strikes, expiry, impliedVols); nTmp = strikes.length; int ref = Math.max(0, nTmp - 3); sabrDataLow = modelParamsTmp.get(0); sabrDataHigh = modelParamsTmp.get(ref); } catch (final Exception e) { nTmp = 1; modelParamsTmp = getFittedModelParametersGlobal(forward, strikes, expiry, impliedVols); sabrDataLow = modelParamsTmp.get(0); sabrDataHigh = modelParamsTmp.get(0); } modelParams = modelParamsTmp; n = nTmp; final Function1D<Double, Double> interpFunc; if (n == 1) { interpFunc = new Function1D<Double, Double>() { @SuppressWarnings("synthetic-access") @Override public Double evaluate(final Double strike) { final EuropeanVanillaOption option = new EuropeanVanillaOption(strike, expiry, true); final Function1D<SABRFormulaData, Double> volFunc = getModel().getVolatilityFunction(option, forward); return volFunc.evaluate(modelParams.get(0)); } }; } else { interpFunc = getVolatilityFunctionFromModelParameters(forward, strikes, expiry, modelParams); } final Function1D<Double, Double> extrapFunc = _extrapolationFunctionProvider.getExtrapolationFunction(sabrDataLow, sabrDataHigh, getModel(), forward, expiry, cutOffStrikeLow, cutOffStrikeHigh); return new Function1D<Double, Double>() { @Override public Double evaluate(final Double strike) { ArgumentChecker.notNegative(strike, "strike"); if (strike < cutOffStrikeLow) { return extrapFunc.evaluate(strike); } if (strike > cutOffStrikeHigh) { return extrapFunc.evaluate(strike); } return interpFunc.evaluate(strike); } }; } SmileInterpolatorSABRWithExtrapolation(
      final SmileExtrapolationFunctionSABRProvider extrapolationFunctionProvider);  SmileInterpolatorSABRWithExtrapolation(final VolatilityFunctionProvider<SABRFormulaData> formula,
      final SmileExtrapolationFunctionSABRProvider extrapolationFunctionProvider);  SmileInterpolatorSABRWithExtrapolation(final int seed,
      final VolatilityFunctionProvider<SABRFormulaData> formula, final double beta,
      final WeightingFunction weightFunction, final SmileExtrapolationFunctionSABRProvider extrapolationFunctionProvider); @Override Function1D<Double, Double> getVolatilityFunction(final double forward, final double[] strikes,
      final double expiry, final double[] impliedVols); @Override int hashCode(); @Override boolean equals(Object obj);  }
@Test public void interpolationTest() { double eps = 1.0e-14; double expiry = 1.2; double forward = 1.7; int nStrikes = 11; double[] strikes = new double[nStrikes]; double[] impliedVols = new double[] {2.17, 1.92, 1.702, 1.545, 1.281, 0.912, 0.9934, 1.0878, 1.1499, 1.2032, 1.242 }; for (int i = 0; i < nStrikes; ++i) { strikes[i] = forward * (0.85 + i * 0.05); } WeightingFunction weight = LinearWeightingFunction.getInstance(); int seed = 4729; double beta = 0.95; ShiftedLogNormalExtrapolationFunctionProvider extapQuiet = new ShiftedLogNormalExtrapolationFunctionProvider( "Quiet"); SmileInterpolatorSABRWithExtrapolation interpQuiet = new SmileInterpolatorSABRWithExtrapolation(seed, new SABRHaganVolatilityFunction(), beta, weight, extapQuiet); InterpolatedSmileFunction funcQuiet = new InterpolatedSmileFunction(interpQuiet, forward, strikes, expiry, impliedVols); SmileInterpolatorSABR sabr = new SmileInterpolatorSABR(seed, new SABRHaganVolatilityFunction(), beta, weight); Function1D<Double, Double> volFunc = sabr.getVolatilityFunction(forward, strikes, expiry, impliedVols); int nKeys = 20; for (int i = 0; i < nKeys + 1; ++i) { Double key = strikes[0] + (strikes[nStrikes - 1] - strikes[0]) * i / nKeys; assertEquals(volFunc.evaluate(key), funcQuiet.getVolatility(key), eps); } SmileInterpolatorSABRWithExtrapolation interpGlobal1 = new SmileInterpolatorSABRWithExtrapolation( new SABRPaulotVolatilityFunction(), extapQuiet); SmileInterpolatorSABRWithExtrapolation interpGlobal2 = new SmileInterpolatorSABRWithExtrapolation( new SABRBerestyckiVolatilityFunction(), extapQuiet); SmileInterpolatorSABRWithExtrapolation interpGlobal3 = new SmileInterpolatorSABRWithExtrapolation( new SABRHaganAlternativeVolatilityFunction(), extapQuiet); InterpolatedSmileFunction funcGlobal1 = new InterpolatedSmileFunction(interpGlobal1, forward, strikes, expiry, impliedVols); InterpolatedSmileFunction funcGlobal2 = new InterpolatedSmileFunction(interpGlobal2, forward, strikes, expiry, impliedVols); InterpolatedSmileFunction funcGlobal3 = new InterpolatedSmileFunction(interpGlobal3, forward, strikes, expiry, impliedVols); for (int i = 0; i < nKeys + 1; ++i) { Double key = strikes[0] + (strikes[nStrikes - 1] - strikes[0]) * i / nKeys; double ref = funcQuiet.getVolatility(key); assertEquals(ref, funcGlobal1.getVolatility(key), 1.5 * ref * 1.0e-1); assertEquals(ref, funcGlobal2.getVolatility(key), ref * 1.0e-1); assertEquals(ref, funcGlobal3.getVolatility(key), ref * 1.0e-1); } }