@Override
 public <T> Optional<T> findData(MarketDataName<T> name) {
   if (surface.getName().equals(name)) {
     return Optional.of(name.getMarketDataType().cast(surface));
   }
   return Optional.empty();
 }
 // -------------------------------------------------------------------------
 @Override
 public double volatility(
     double expiry, LocalDate fixingDate, double strikePrice, double futurePrice) {
   double simpleMoneyness =
       moneynessOnPrice ? strikePrice - futurePrice : futurePrice - strikePrice;
   return surface.zValue(expiry, simpleMoneyness);
 }
 private CurrencyParameterSensitivity parameterSensitivity(IborFutureOptionSensitivity point) {
   double simpleMoneyness =
       moneynessOnPrice
           ? point.getStrikePrice() - point.getFuturePrice()
           : point.getFuturePrice() - point.getStrikePrice();
   UnitParameterSensitivity unitSens =
       surface.zValueParameterSensitivity(point.getExpiry(), simpleMoneyness);
   return unitSens.multipliedBy(point.getCurrency(), point.getSensitivity());
 }
  @ImmutableConstructor
  private NormalIborFutureOptionExpirySimpleMoneynessVolatilities(
      IborIndex index, ZonedDateTime valuationDateTime, Surface surface) {

    ArgChecker.notNull(index, "index");
    ArgChecker.notNull(valuationDateTime, "valuationDateTime");
    ArgChecker.notNull(surface, "surface");
    surface
        .getMetadata()
        .getXValueType()
        .checkEquals(ValueType.YEAR_FRACTION, "Incorrect x-value type for Normal volatilities");
    surface
        .getMetadata()
        .getYValueType()
        .checkEquals(ValueType.SIMPLE_MONEYNESS, "Incorrect y-value type for Normal volatilities");
    surface
        .getMetadata()
        .getZValueType()
        .checkEquals(ValueType.NORMAL_VOLATILITY, "Incorrect z-value type for Normal volatilities");
    DayCount dayCount =
        surface
            .getMetadata()
            .findInfo(SurfaceInfoType.DAY_COUNT)
            .orElseThrow(
                () -> new IllegalArgumentException("Incorrect surface metadata, missing DayCount"));
    MoneynessType moneynessType =
        surface
            .getMetadata()
            .findInfo(SurfaceInfoType.MONEYNESS_TYPE)
            .orElseThrow(
                () ->
                    new IllegalArgumentException(
                        "Incorrect surface metadata, missing MoneynessType"));

    this.index = index;
    this.valuationDateTime = valuationDateTime;
    this.surface = surface;
    this.moneynessOnPrice = moneynessType == MoneynessType.PRICE;
    this.dayCount = dayCount;
  }
 @Override
 public NormalIborFutureOptionExpirySimpleMoneynessVolatilities withPerturbation(
     ParameterPerturbation perturbation) {
   return new NormalIborFutureOptionExpirySimpleMoneynessVolatilities(
       index, valuationDateTime, surface.withPerturbation(perturbation));
 }
 @Override
 public NormalIborFutureOptionExpirySimpleMoneynessVolatilities withParameter(
     int parameterIndex, double newValue) {
   return new NormalIborFutureOptionExpirySimpleMoneynessVolatilities(
       index, valuationDateTime, surface.withParameter(parameterIndex, newValue));
 }
 @Override
 public ParameterMetadata getParameterMetadata(int parameterIndex) {
   return surface.getParameterMetadata(parameterIndex);
 }
 @Override
 public double getParameter(int parameterIndex) {
   return surface.getParameter(parameterIndex);
 }
 @Override
 public int getParameterCount() {
   return surface.getParameterCount();
 }
 // -------------------------------------------------------------------------
 @Override
 public IborFutureOptionVolatilitiesName getName() {
   return IborFutureOptionVolatilitiesName.of(surface.getName().getName());
 }