/** * General method when you wish to compute the expected variance from a log-moneyness * parametrised surface to within a certain tolerance * * @param surface log-moneyness parametrised volatility surface * @return expected variance */ @SuppressWarnings("synthetic-access") @Override public Double visitLogMoneyness(final BlackVolatilitySurfaceLogMoneyness surface) { final double atmVol = surface.getVolatilityForLogMoneyness(_t, 0.0); if (_t < 1e-4) { return atmVol * atmVol; } final double rootT = Math.sqrt(_t); final double invNorTol = NORMAL.getInverseCDF(_tol); final Function1D<Double, Double> integrand = getLogMoneynessIntegrand(surface); double putPart; if (_addResidual) { putPart = _integrator.integrate(integrand, Math.log(_lowStrikeCutoff / _f), 0.0); putPart += _residual; } else { final double l = invNorTol * atmVol * rootT; // initial estimate of lower limit putPart = _integrator.integrate(integrand, l, 0.0); double rem = integrand.evaluate(l); double error = rem / putPart; int step = 1; while (error > _tol) { putPart += _integrator.integrate(integrand, (step + 1) * l, step * l); step++; rem = integrand.evaluate((step + 1) * l); error = rem / putPart; } putPart += rem; // add on the (very small) remainder estimate otherwise we'll always underestimate // variance } final double u = _f * Math.exp(-invNorTol * atmVol * rootT); // initial estimate of upper limit double callPart = _integrator.integrate(integrand, 0.0, u); double rem = integrand.evaluate(u); double error = rem / callPart; int step = 1; while (error > _tol) { callPart += _integrator.integrate(integrand, step * u, (1 + step) * u); step++; rem = integrand.evaluate((1 + step) * u); error = rem / putPart; } // callPart += rem; // don't add on the remainder estimate as it is very conservative, and likely too large return 2 * (putPart + callPart) / _t; }
@SuppressWarnings("synthetic-access") @Override public Double visitStrike(final BlackVolatilitySurfaceStrike surface) { final double atmVol = surface.getVolatility(_t, _f); if (_t < 1e-4) { return atmVol * atmVol; } final double rootT = Math.sqrt(_t); final double invNorTol = NORMAL.getInverseCDF(_tol); final Function1D<Double, Double> integrand = getStrikeIntegrand(surface); final Function1D<Double, Double> remainder = new Function1D<Double, Double>() { @Override public Double evaluate(final Double strike) { if (strike == 0) { return 0.0; } final boolean isCall = strike >= _f; final double vol = surface.getVolatility(_t, strike); final double otmPrice = BlackFormulaRepository.price(_f, strike, _t, vol, isCall); final double res = (isCall ? otmPrice / strike : otmPrice / 2 / strike); return res; } }; double putPart; if (_addResidual) { putPart = _integrator.integrate(integrand, _lowStrikeCutoff, _f); putPart += _residual; } else { double l = _f * Math.exp(invNorTol * atmVol * rootT); // initial estimate of lower limit putPart = _integrator.integrate(integrand, l, _f); double rem = remainder.evaluate(l); double error = rem / putPart; while (error > _tol) { l /= 2.0; putPart += _integrator.integrate(integrand, l, 2 * l); rem = remainder.evaluate(l); error = rem / putPart; } putPart += rem; // add on the (very small) remainder estimate otherwise we'll always underestimate // variance } double u = _f * Math.exp(-invNorTol * atmVol * rootT); // initial estimate of upper limit double callPart = _integrator.integrate(integrand, _f, u); double rem = remainder.evaluate(u); double error = rem / callPart; while (error > _tol) { callPart += _integrator.integrate(integrand, u, 2 * u); u *= 2.0; rem = remainder.evaluate(u); error = rem / putPart; } // callPart += rem/2.0; // don't add on the remainder estimate as it is very conservative, and likely too large return 2 * (putPart + callPart) / _t; }