class SabrIborCapletFloorletPeriodPricer extends VolatilityIborCapletFloorletPeriodPricer {

  public PointSensitivityBuilder presentValueSensitivityRatesStickyModel(
      IborCapletFloorletPeriod period,
      RatesProvider ratesProvider,
      SabrIborCapletFloorletVolatilities volatilities) {

    Currency currency = period.getCurrency();
    if (ratesProvider.getValuationDate().isAfter(period.getPaymentDate())) {
      return PointSensitivityBuilder.none();
    }
    double expiry = volatilities.relativeTime(period.getFixingDateTime());
    PutCall putCall = period.getPutCall();
    double strike = period.getStrike();
    double indexRate = ratesProvider.iborIndexRates(period.getIndex()).rate(period.getIborRate().getObservation());
    PointSensitivityBuilder dfSensi = ratesProvider.discountFactors(currency).zeroRatePointSensitivity(period.getPaymentDate());
    double factor = period.getNotional() * period.getYearFraction();
    if (expiry < 0d) { // option expired already, but not yet paid
      double sign = putCall.isCall() ? 1d : -1d;
      double payoff = Math.max(sign * (indexRate - strike), 0d);
      return dfSensi.multipliedBy(payoff * factor);
    }
    ValueDerivatives volatilityAdj = volatilities.volatilityAdjoint(expiry, strike, indexRate);
    PointSensitivityBuilder indexRateSensiSensi =
        ratesProvider.iborIndexRates(period.getIndex()).ratePointSensitivity(period.getIborRate().getObservation());
    double df = ratesProvider.discountFactor(currency, period.getPaymentDate());
    double fwdPv = factor * volatilities.price(expiry, putCall, strike, indexRate, volatilityAdj.getValue());
    double fwdDelta = factor * volatilities.priceDelta(expiry, putCall, strike, indexRate, volatilityAdj.getValue());
    double fwdVega = factor * volatilities.priceVega(expiry, putCall, strike, indexRate, volatilityAdj.getValue());

    return dfSensi.multipliedBy(fwdPv).combinedWith(
        indexRateSensiSensi.multipliedBy(fwdDelta * df + fwdVega * volatilityAdj.getDerivative(0) * df));
  }

}