ArgumentChecker.notNull(swap, "swap");
ArgumentChecker.notNull(discountCurve, "discount curve");
ArgumentChecker.notNull(dividends, "dividends");
ArgumentChecker.notNull(marketVols, "market volatilities");
final PureLocalVolatilitySurface plv = getPureLocalVolFromMarket(spot, discountCurve, dividends, marketVols);
final EquityDividendsCurvesBundle divCurves = new EquityDividendsCurvesBundle(spot, discountCurve, dividends);
final LocalVolatilitySurfaceStrike lv = VolatilitySurfaceConverter.convertLocalVolSurface(plv, divCurves);
final double eps = 1e-5;
final int index = swap.correctForDividends() ? 0 : 1;
final double t = swap.getTimeToSettlement();
final double mid = VAR_SWAP_FWD_PDE_CALCULATOR.expectedVariance(spot, discountCurve, dividends, t, plv)[index];
//up - here we assume that the local vol surface is invariant to a change of spot and form a new pure local vol surface corresponding to the bumped spot
final double sUp = spot * (1 + eps);
final EquityDividendsCurvesBundle divCurvesUp = new EquityDividendsCurvesBundle(sUp, discountCurve, dividends);
final PureLocalVolatilitySurface plvUp = VolatilitySurfaceConverter.convertLocalVolSurface(lv, divCurvesUp);
final double up = VAR_SWAP_FWD_PDE_CALCULATOR.expectedVariance(sUp, discountCurve, dividends, t, plvUp)[index];
//down
final double sDown = spot * (1 - eps);
final EquityDividendsCurvesBundle divCurvesDown = new EquityDividendsCurvesBundle(sDown, discountCurve, dividends);
final PureLocalVolatilitySurface plvDown = VolatilitySurfaceConverter.convertLocalVolSurface(lv, divCurvesDown);
final double down = VAR_SWAP_FWD_PDE_CALCULATOR.expectedVariance(sDown, discountCurve, dividends, t, plvDown)[index];
final double gamma = (up + down - 2 * mid) / eps / eps / t;
return gamma;
}