SmileInterpolatorSpline implements GeneralSmileInterpolator { @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, "implied vols"); final int n = strikes.length; ArgumentChecker.isTrue(impliedVols.length == n, "#strikes {} does not match #vols {}", n, impliedVols.length); final double kL = strikes[0]; final double kH = strikes[n - 1]; final double[] x = new double[n]; for (int i = 0; i < n; i++) { x[i] = Math.log(strikes[i] / forward); } final Interpolator1DDataBundle data = _interpolator.getDataBundle(x, impliedVols); final Function1D<Double, Double> interpFunc = new Function1D<Double, Double>() { @SuppressWarnings("synthetic-access") @Override public Double evaluate(final Double k) { final double m = Math.log(k / forward); return _interpolator.interpolate(data, m); } }; final Function1D<Double, Boolean> domain = new Function1D<Double, Boolean>() { @Override public Boolean evaluate(final Double k) { return k >= kL && k <= kH; } }; final double[] shiftLnVolHighTail; final double[] shiftLnVolLowTail; final Function1D<Double, Double> dSigmaDx = DIFFERENTIATOR.differentiate(interpFunc, domain); if (_extrapolatorFailureBehaviour.equalsIgnoreCase(s_quiet)) { ArgumentChecker.isTrue(kL <= forward, "Cannot do left tail extrapolation when the lowest strike ({}) is greater than the forward ({})", kL, forward); ArgumentChecker.isTrue(kH >= forward, "Cannot do right tail extrapolation when the highest strike ({}) is less than the forward ({})", kH, forward); shiftLnVolHighTail = TAIL_FITTER.fitVolatilityAndGradRecursivelyByReducingSmile(forward, strikes[n - 1], impliedVols[n - 1], dSigmaDx.evaluate(kH), expiry); shiftLnVolLowTail = TAIL_FITTER.fitVolatilityAndGradRecursivelyByReducingSmile(forward, kL, impliedVols[0], dSigmaDx.evaluate(kL), expiry); } else if (_extrapolatorFailureBehaviour.equalsIgnoreCase(s_exception)) { ArgumentChecker.isTrue(kL <= forward, "Cannot do left tail extrapolation when the lowest strike ({}) is greater than the forward ({})", kL, forward); ArgumentChecker.isTrue(kH >= forward, "Cannot do right tail extrapolation when the highest strike ({}) is less than the forward ({})", kH, forward); shiftLnVolHighTail = TAIL_FITTER.fitVolatilityAndGrad(forward, kH, impliedVols[n - 1], dSigmaDx.evaluate(kH), expiry); shiftLnVolLowTail = TAIL_FITTER.fitVolatilityAndGrad(forward, kL, impliedVols[0], dSigmaDx.evaluate(kL), expiry); } else if (_extrapolatorFailureBehaviour.equalsIgnoreCase(s_flat)) { shiftLnVolHighTail = TAIL_FITTER.fitVolatilityAndGrad(forward, kH, impliedVols[n - 1], 0.0, expiry); shiftLnVolLowTail = TAIL_FITTER.fitVolatilityAndGrad(forward, kL, impliedVols[0], 0.0, expiry); } else { throw new OpenGammaRuntimeException("Unrecognized _extrapolatorFailureBehaviour. Looking for one of Exception, Quiet, or Flat"); } Function1D<Double, Double> volSmileFunction = new Function1D<Double, Double>() { @Override public Double evaluate(final Double k) { if (k < kL) { return ShiftedLogNormalTailExtrapolation.impliedVolatility(forward, k, expiry, shiftLnVolLowTail[0], shiftLnVolLowTail[1]); } else if (k > kH) { return ShiftedLogNormalTailExtrapolation.impliedVolatility(forward, k, expiry, shiftLnVolHighTail[0], shiftLnVolHighTail[1]); } else { return interpFunc.evaluate(k); } } }; return volSmileFunction; } SmileInterpolatorSpline();  SmileInterpolatorSpline(final Interpolator1D interpolator);  SmileInterpolatorSpline(final Interpolator1D interpolator, String extrapolatorFailureBehaviour); final String getExtrapolatorFailureBehaviour(); @Override Function1D<Double, Double> getVolatilityFunction(final double forward, final double[] strikes, final double expiry, final double[] impliedVols); Interpolator1D getInterpolator(); @Override int hashCode(); @Override boolean equals(final Object obj);  }
@Test(expectedExceptions = IllegalArgumentException.class) public void testBadDataLeftExtrapolation() { INTERPOLATOR.getVolatilityFunction(getStrikes()[0] - 10, getStrikes(), getExpiry(), getVols()); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testBadDataRightExtrapolation() { INTERPOLATOR.getVolatilityFunction(getStrikes()[getStrikes().length - 1] + 10, getStrikes(), getExpiry(), getVols()); }