ArgumentChecker.notNull(dividends, "dividends");
ArgumentChecker.notNull(marketVols, "market vols");
final double eps = 1e-5;
final double t = swap.getTimeToObsEnd();
final int index = swap.correctForDividends() ? 0 : 1;
final PureImpliedVolatilitySurface pureSurf = getPureImpliedVolFromMarket(spot, discountCurve, dividends, marketVols);
final double base = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dividends, t, pureSurf)[index];
final int n = dividends.getNumberOfDividends();
final double[][] res = new double[n][2];
for (int i = 0; i < n; i++) {
//bump alpha
if (dividends.getAlpha(i) > eps / (1 - eps)) {
//up
final AffineDividends daUp = dividends.withAlpha(dividends.getAlpha(i) * (1 + eps) + eps, i);
final PureImpliedVolatilitySurface pvUp = getPureImpliedVolFromMarket(spot, discountCurve, daUp, marketVols);
final double[] aUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, daUp, t, pvUp);
//down
final AffineDividends daDown = dividends.withAlpha(dividends.getAlpha(i) * (1 - eps) - eps, i);
final PureImpliedVolatilitySurface pvDown = getPureImpliedVolFromMarket(spot, discountCurve, daDown, marketVols);
final double[] aDown = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, daDown, t, pvDown);
res[i][0] = spot * (aUp[index] - aDown[index]) / 2 / eps / (1 + dividends.getAlpha(i));
} else {
//forward difference for zero (or very near zero) alpha
final AffineDividends daUp = dividends.withAlpha(dividends.getAlpha(i) + eps, i);
final PureImpliedVolatilitySurface pvUp = getPureImpliedVolFromMarket(spot, discountCurve, daUp, marketVols);
final double[] aUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, daUp, t, pvUp);
res[i][0] = spot * (aUp[index] - base) / eps;
}
//bump beta
if (dividends.getBeta(i) > eps) {
final AffineDividends dbUp = dividends.withBeta(dividends.getBeta(i) + eps, i);
final PureImpliedVolatilitySurface pvUp = getPureImpliedVolFromMarket(spot, discountCurve, dbUp, marketVols);
final double[] bUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dbUp, swap.getTimeToObsEnd(), pvUp);
final AffineDividends dbDown = dividends.withBeta(dividends.getBeta(i) - eps, i);
final PureImpliedVolatilitySurface pvDown = getPureImpliedVolFromMarket(spot, discountCurve, dbDown, marketVols);
final double[] bDown = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dbDown, swap.getTimeToObsEnd(), pvDown);
res[i][1] = (bUp[index] - bDown[index]) / 2 / eps;
} else {
//forward difference for zero (or near zero) beta
final AffineDividends dbUp = dividends.withBeta(dividends.getBeta(i) + eps, i);
final PureImpliedVolatilitySurface pvUp = getPureImpliedVolFromMarket(spot, discountCurve, dbUp, marketVols);
final double[] bUp = VAR_SWAP_CALCULATOR.expectedVariance(spot, discountCurve, dbUp, swap.getTimeToObsEnd(), pvUp);
res[i][1] = (bUp[index] - base) / eps;
}
}
return res;