protected double getResidual( final double fwd, final double expiry, final double[] ks, final double[] vols) { // Check for trivial case where cutoff is so low that there's no effective value in the option final double cutoffPrice = BlackFormulaRepository.price(fwd, ks[0], expiry, vols[0], ks[0] > fwd); if (CompareUtils.closeEquals(cutoffPrice, 0)) { return 0.0; // i.e. the tail function is never used } // The typical case - fit a ShiftedLognormal to the two strike-vol pairs final ShiftedLognormalVolModel leftExtrapolator = new ShiftedLognormalVolModel(fwd, expiry, ks[0], vols[0], ks[1], vols[1]); // Now, handle behaviour near zero strike. ShiftedLognormalVolModel has non-zero put price for // zero strike. // What we do is to find the strike, k_min, at which f(k) = p(k)/k^2 begins to blow up, by // finding the minimum of this function, k_min // then setting f(k) = f(k_min) for k < k_min. This ensures the implied volatility and the // integrand are well behaved in the limit k -> 0. final Function1D<Double, Double> shiftedLnIntegrand = new Function1D<Double, Double>() { @Override public Double evaluate(final Double strike) { return leftExtrapolator.priceFromFixedStrike(strike) / (strike * strike); } }; final double kMin = new BrentMinimizer1D().minimize(shiftedLnIntegrand, EPS, EPS, ks[0]); final double fMin = shiftedLnIntegrand.evaluate(kMin); double res = fMin * kMin; // the (hopefully) very small rectangular bit between zero and kMin res += _integrator.integrate(shiftedLnIntegrand, kMin, ks[0]); return res; }