//-------------------------------------------------------------------------
        public virtual void coverage()
        {
            coverImmutableBean(VOLS);
            ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities vols = ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(USD_LIBOR_3M, VAL_DATE_TIME.plusMonths(1), InterpolatedNodalSurface.of(METADATA, TIME, STRIKE, VOL, GridSurfaceInterpolator.of(TIME_SQUARE, LINEAR)), ConstantCurve.of("shift", 0.05));

            coverBeanEquals(VOLS, vols);
        }
        public virtual void recovery_test_shiftedBlack()
        {
            double        lambdaT    = 0.07;
            double        lambdaK    = 0.07;
            double        error      = 1.0e-5;
            ConstantCurve shiftCurve = ConstantCurve.of("Black shift", 0.02);
            DirectIborCapletFloorletVolatilityDefinition definition = DirectIborCapletFloorletVolatilityDefinition.of(NAME, USD_LIBOR_3M, ACT_ACT_ISDA, lambdaT, lambdaK, INTERPOLATOR, shiftCurve);
            ImmutableList <Period> maturities  = createBlackMaturities();
            DoubleArray            strikes     = createBlackStrikes();
            DoubleMatrix           errorMatrix = DoubleMatrix.filled(maturities.size(), strikes.size(), error);
            RawOptionData          data        = RawOptionData.of(maturities, strikes, STRIKE, createFullBlackDataMatrix(), errorMatrix, BLACK_VOLATILITY);
            IborCapletFloorletVolatilityCalibrationResult          res     = CALIBRATOR.calibrate(definition, CALIBRATION_TIME, data, RATES_PROVIDER);
            ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities resVols = (ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities)res.Volatilities;

            for (int i = 0; i < NUM_BLACK_STRIKES; ++i)
            {
                Pair <IList <ResolvedIborCapFloorLeg>, IList <double> > capsAndVols = getCapsBlackVols(i);
                IList <ResolvedIborCapFloorLeg> caps = capsAndVols.First;
                IList <double> vols  = capsAndVols.Second;
                int            nCaps = caps.Count;
                for (int j = 0; j < nCaps; ++j)
                {
                    ConstantSurface volSurface = ConstantSurface.of(Surfaces.blackVolatilityByExpiryStrike("test", ACT_ACT_ISDA), vols[j]);
                    BlackIborCapletFloorletExpiryStrikeVolatilities constVol = BlackIborCapletFloorletExpiryStrikeVolatilities.of(USD_LIBOR_3M, CALIBRATION_TIME, volSurface);
                    double priceOrg   = LEG_PRICER_BLACK.presentValue(caps[j], RATES_PROVIDER, constVol).Amount;
                    double priceCalib = LEG_PRICER_BLACK.presentValue(caps[j], RATES_PROVIDER, resVols).Amount;
                    assertEquals(priceOrg, priceCalib, Math.Max(priceOrg, 1d) * TOL);
                }
            }
            assertTrue(res.ChiSquare > 0d);
            assertEquals(resVols.Index, USD_LIBOR_3M);
            assertEquals(resVols.Name, definition.Name);
            assertEquals(resVols.ValuationDateTime, CALIBRATION_TIME);
            assertEquals(resVols.ShiftCurve, definition.ShiftCurve.get());
        }
        public virtual void test_volatility_sensitivity()
        {
            double eps   = 1.0e-6;
            int    nData = TIME.size();

            for (int i = 0; i < NB_TEST; i++)
            {
                for (int k = 0; k < NB_TEST; k++)
                {
                    double expiryTime = VOLS.relativeTime(TEST_OPTION_EXPIRY[i]);
                    IborCapletFloorletSensitivity point = IborCapletFloorletSensitivity.of(VOLS.Name, expiryTime, TEST_STRIKE[k], TEST_FORWARD, GBP, TEST_SENSITIVITY[i]);
                    double[] sensFd = new double[nData];
                    for (int j = 0; j < nData; j++)
                    {
                        DoubleArray volDataUp            = VOL.subArray(0, nData).with(j, VOL.get(j) + eps);
                        DoubleArray volDataDw            = VOL.subArray(0, nData).with(j, VOL.get(j) - eps);
                        InterpolatedNodalSurface paramUp = InterpolatedNodalSurface.of(METADATA, TIME, STRIKE, volDataUp, INTERPOLATOR_2D);
                        InterpolatedNodalSurface paramDw = InterpolatedNodalSurface.of(METADATA, TIME, STRIKE, volDataDw, INTERPOLATOR_2D);
                        ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities provUp = ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(GBP_LIBOR_3M, VAL_DATE_TIME, paramUp, CURVE);
                        ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities provDw = ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(GBP_LIBOR_3M, VAL_DATE_TIME, paramDw, CURVE);
                        double volUp = provUp.volatility(TEST_OPTION_EXPIRY[i], TEST_STRIKE[k], TEST_FORWARD);
                        double volDw = provDw.volatility(TEST_OPTION_EXPIRY[i], TEST_STRIKE[k], TEST_FORWARD);
                        double fd    = 0.5 * (volUp - volDw) / eps;
                        sensFd[j] = fd * TEST_SENSITIVITY[i];
                    }
                    CurrencyParameterSensitivity sensActual = VOLS.parameterSensitivity(point).Sensitivities.get(0);
                    double[] computed = sensActual.Sensitivity.toArray();
                    assertTrue(DoubleArrayMath.fuzzyEquals(computed, sensFd, eps));
                }
            }
        }
        public virtual void recovery_test_normal2_shift()
        {
            SurfaceIborCapletFloorletVolatilityBootstrapDefinition definition = SurfaceIborCapletFloorletVolatilityBootstrapDefinition.of(IborCapletFloorletVolatilitiesName.of("test"), USD_LIBOR_3M, ACT_ACT_ISDA, LINEAR, DOUBLE_QUADRATIC, ConstantCurve.of("Black shift", 0.02));
            DoubleArray   strikes = createNormalEquivStrikes();
            RawOptionData data    = RawOptionData.of(createNormalEquivMaturities(), strikes, ValueType.STRIKE, createFullNormalEquivDataMatrix(), ValueType.NORMAL_VOLATILITY);
            IborCapletFloorletVolatilityCalibrationResult          res    = CALIBRATOR.calibrate(definition, CALIBRATION_TIME, data, RATES_PROVIDER);
            ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities resVol = (ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities)res.Volatilities;

            for (int i = 0; i < strikes.size(); ++i)
            {
                Pair <IList <ResolvedIborCapFloorLeg>, IList <double> > capsAndVols = getCapsNormalEquivVols(i);
                IList <ResolvedIborCapFloorLeg> caps = capsAndVols.First;
                IList <double> vols  = capsAndVols.Second;
                int            nCaps = caps.Count;
                for (int j = 0; j < nCaps; ++j)
                {
                    ConstantSurface volSurface = ConstantSurface.of(Surfaces.normalVolatilityByExpiryStrike("test", ACT_ACT_ISDA), vols[j]);
                    NormalIborCapletFloorletExpiryStrikeVolatilities constVol = NormalIborCapletFloorletExpiryStrikeVolatilities.of(USD_LIBOR_3M, CALIBRATION_TIME, volSurface);
                    double priceOrg   = LEG_PRICER_NORMAL.presentValue(caps[j], RATES_PROVIDER, constVol).Amount;
                    double priceCalib = LEG_PRICER_BLACK.presentValue(caps[j], RATES_PROVIDER, resVol).Amount;
                    assertEquals(priceOrg, priceCalib, Math.Max(priceOrg, 1d) * TOL);
                }
            }
            assertEquals(res.ChiSquare, 0d);
        }
Exemple #5
0
 private System.Func <Surface, IborCapletFloorletVolatilities> createShiftedBlackVolatilitiesFunction(IborIndex index, ZonedDateTime calibrationDateTime, Curve shiftCurve)
 {
     System.Func <Surface, IborCapletFloorletVolatilities> func = (Surface s) =>
     {
         return(ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(index, calibrationDateTime, s, shiftCurve));
     };
     return(func);
 }
Exemple #6
0
 //-----------------------------------------------------------------------
 public override bool Equals(object obj)
 {
     if (obj == this)
     {
         return(true);
     }
     if (obj != null && obj.GetType() == this.GetType())
     {
         ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities other = (ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities)obj;
         return(JodaBeanUtils.equal(index, other.index) && JodaBeanUtils.equal(valuationDateTime, other.valuationDateTime) && JodaBeanUtils.equal(surface, other.surface) && JodaBeanUtils.equal(shiftCurve, other.shiftCurve));
     }
     return(false);
 }
        private void testSurfaceSensitivity(CurrencyParameterSensitivity computed, ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities vols, System.Func <IborCapletFloorletVolatilities, CurrencyAmount> valueFn)
        {
            double pvBase = valueFn(vols).Amount;
            InterpolatedNodalSurface surfaceBase = (InterpolatedNodalSurface)vols.Surface;
            int nParams = surfaceBase.ParameterCount;

            for (int i = 0; i < nParams; i++)
            {
                DoubleArray zBumped = surfaceBase.ZValues.with(i, surfaceBase.ZValues.get(i) + EPS_FD);
                InterpolatedNodalSurface surfaceBumped = surfaceBase.withZValues(zBumped);
                ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities volsBumped = ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(vols.Index, vols.ValuationDateTime, surfaceBumped, vols.ShiftCurve);
                double fd = (valueFn(volsBumped).Amount - pvBase) / EPS_FD;
                assertEquals(computed.Sensitivity.get(i), fd, NOTIONAL * EPS_FD);
            }
        }
        public virtual void test_getter()
        {
            assertEquals(VOLS.ValuationDate, VAL_DATE);
            assertEquals(VOLS.Index, GBP_LIBOR_3M);
            assertEquals(VOLS.Surface, SURFACE);
            assertEquals(VOLS.ParameterCount, TIME.size());
            assertEquals(VOLS.findData(CURVE.Name).get(), CURVE);
            assertEquals(VOLS.findData(SURFACE.Name).get(), SURFACE);
            assertFalse(VOLS.findData(CurveName.of("foo")).Present);
            int    nParams  = VOLS.ParameterCount;
            double newValue = 152d;

            for (int i = 0; i < nParams; ++i)
            {
                assertEquals(VOLS.getParameter(i), SURFACE.getParameter(i));
                assertEquals(VOLS.getParameterMetadata(i), SURFACE.getParameterMetadata(i));
                assertEquals(VOLS.withParameter(i, newValue), ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(GBP_LIBOR_3M, VAL_DATE_TIME, SURFACE.withParameter(i, newValue), CURVE));
                assertEquals(VOLS.withPerturbation((n, v, m) => 2d * v), ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(GBP_LIBOR_3M, VAL_DATE_TIME, SURFACE.withPerturbation((n, v, m) => 2d * v), CURVE));
            }
        }
        //-------------------------------------------------------------------------
        public virtual void test_presentValue_formula()
        {
            CurrencyAmount computedCaplet   = PRICER.presentValue(CAPLET_LONG, RATES, VOLS);
            CurrencyAmount computedFloorlet = PRICER.presentValue(FLOORLET_SHORT, RATES, VOLS);
            double         forward          = RATES.iborIndexRates(EUR_EURIBOR_3M).rate(RATE_COMP.Observation);
            double         expiry           = VOLS.relativeTime(CAPLET_LONG.FixingDateTime);
            double         volatility       = VOLS.volatility(expiry, STRIKE, forward);
            double         df               = RATES.discountFactor(EUR, CAPLET_LONG.PaymentDate);
            double         expectedCaplet   = NOTIONAL * df * CAPLET_LONG.YearFraction * BlackFormulaRepository.price(forward + SHIFT, STRIKE + SHIFT, expiry, volatility, CALL.Call);
            double         expectedFloorlet = -NOTIONAL *df *FLOORLET_SHORT.YearFraction *BlackFormulaRepository.price(forward + SHIFT, STRIKE + SHIFT, expiry, volatility, PUT.Call);

            assertEquals(computedCaplet.Currency, EUR);
            assertEquals(computedCaplet.Amount, expectedCaplet, NOTIONAL * TOL);
            assertEquals(computedFloorlet.Currency, EUR);
            assertEquals(computedFloorlet.Amount, expectedFloorlet, NOTIONAL * TOL);
            // consistency with shifted Black
            ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities vols = ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(EUR_EURIBOR_3M, VALUATION, ConstantSurface.of("constVol", volatility).withMetadata(Surfaces.blackVolatilityByExpiryStrike("costVol", DayCounts.ACT_ACT_ISDA)), IborCapletFloorletSabrRateVolatilityDataSet.CURVE_CONST_SHIFT);
            CurrencyAmount computedCapletBlack   = PRICER_BASE.presentValue(CAPLET_LONG, RATES, vols);
            CurrencyAmount computedFloorletBlack = PRICER_BASE.presentValue(FLOORLET_SHORT, RATES, vols);

            assertEquals(computedCaplet.Amount, computedCapletBlack.Amount, NOTIONAL * TOL);
            assertEquals(computedFloorlet.Amount, computedFloorletBlack.Amount, NOTIONAL * TOL);
        }
        public virtual void recovery_test_blackSurface_shift()
        {
            SurfaceIborCapletFloorletVolatilityBootstrapDefinition definition = SurfaceIborCapletFloorletVolatilityBootstrapDefinition.of(IborCapletFloorletVolatilitiesName.of("test"), USD_LIBOR_3M, ACT_ACT_ISDA, LINEAR, LINEAR, ConstantCurve.of("Black shift", 0.02));
            DoubleArray   strikes = createBlackStrikes();
            RawOptionData data    = RawOptionData.of(createBlackMaturities(), strikes, ValueType.STRIKE, createFullBlackDataMatrix(), ValueType.BLACK_VOLATILITY);
            IborCapletFloorletVolatilityCalibrationResult          res    = CALIBRATOR.calibrate(definition, CALIBRATION_TIME, data, RATES_PROVIDER);
            ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities resVol = (ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities)res.Volatilities;

            for (int i = 0; i < strikes.size(); ++i)
            {
                Pair <IList <ResolvedIborCapFloorLeg>, IList <double> > capsAndVols = getCapsBlackVols(i);
                IList <ResolvedIborCapFloorLeg> caps = capsAndVols.First;
                IList <double> vols  = capsAndVols.Second;
                int            nCaps = caps.Count;
                for (int j = 0; j < nCaps; ++j)
                {
                    ConstantSurface volSurface = ConstantSurface.of(Surfaces.blackVolatilityByExpiryStrike("test", ACT_ACT_ISDA), vols[j]);
                    BlackIborCapletFloorletExpiryStrikeVolatilities constVol = BlackIborCapletFloorletExpiryStrikeVolatilities.of(USD_LIBOR_3M, CALIBRATION_TIME, volSurface);
                    double priceOrg   = LEG_PRICER_BLACK.presentValue(caps[j], RATES_PROVIDER, constVol).Amount;
                    double priceCalib = LEG_PRICER_BLACK.presentValue(caps[j], RATES_PROVIDER, resVol).Amount;
                    assertEquals(priceOrg, priceCalib, Math.Max(priceOrg, 1d) * TOL);
                }
            }
            assertEquals(res.ChiSquare, 0d);
            assertEquals(resVol.Index, USD_LIBOR_3M);
            assertEquals(resVol.Name, definition.Name);
            assertEquals(resVol.ValuationDateTime, CALIBRATION_TIME);
            assertEquals(resVol.ShiftCurve, definition.ShiftCurve.get());
            InterpolatedNodalSurface surface = (InterpolatedNodalSurface)resVol.Surface;

            for (int i = 0; i < surface.ParameterCount; ++i)
            {
                GenericVolatilitySurfacePeriodParameterMetadata metadata = (GenericVolatilitySurfacePeriodParameterMetadata)surface.getParameterMetadata(i);
                assertEquals(metadata.Strike.Value + 0.02, surface.YValues.get(i));
            }
        }
        //-------------------------------------------------------------------------
        public virtual void test_presentValueSensitivity()
        {
            PointSensitivityBuilder        pointCaplet      = PRICER.presentValueSensitivityRatesStickyModel(CAPLET_LONG, RATES, VOLS);
            CurrencyParameterSensitivities computedCaplet   = RATES.parameterSensitivity(pointCaplet.build());
            PointSensitivityBuilder        pointFloorlet    = PRICER.presentValueSensitivityRatesStickyModel(FLOORLET_SHORT, RATES, VOLS);
            CurrencyParameterSensitivities computedFloorlet = RATES.parameterSensitivity(pointFloorlet.build());
            CurrencyParameterSensitivities expectedCaplet   = FD_CAL.sensitivity(RATES, p => PRICER_BASE.presentValue(CAPLET_LONG, p, VOLS));
            CurrencyParameterSensitivities expectedFloorlet = FD_CAL.sensitivity(RATES, p => PRICER_BASE.presentValue(FLOORLET_SHORT, p, VOLS));

            assertTrue(computedCaplet.equalWithTolerance(expectedCaplet, EPS_FD * NOTIONAL * 50d));
            assertTrue(computedFloorlet.equalWithTolerance(expectedFloorlet, EPS_FD * NOTIONAL * 50d));
            // consistency with shifted Black
            PointSensitivityBuilder pointCapletBase   = PRICER.presentValueSensitivityRates(CAPLET_LONG, RATES, VOLS);
            PointSensitivityBuilder pointFloorletBase = PRICER.presentValueSensitivityRates(FLOORLET_SHORT, RATES, VOLS);
            double forward    = RATES.iborIndexRates(EUR_EURIBOR_3M).rate(RATE_COMP.Observation);
            double expiry     = VOLS.relativeTime(CAPLET_LONG.FixingDateTime);
            double volatility = VOLS.volatility(expiry, STRIKE, forward);
            ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities vols = ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(EUR_EURIBOR_3M, VALUATION, ConstantSurface.of("constVol", volatility).withMetadata(Surfaces.blackVolatilityByExpiryStrike("costVol", DayCounts.ACT_ACT_ISDA)), IborCapletFloorletSabrRateVolatilityDataSet.CURVE_CONST_SHIFT);
            PointSensitivityBuilder pointCapletExp   = PRICER_BASE.presentValueSensitivityRates(CAPLET_LONG, RATES, vols);
            PointSensitivityBuilder pointFloorletExp = PRICER_BASE.presentValueSensitivityRates(FLOORLET_SHORT, RATES, vols);

            assertEquals(pointCapletBase, pointCapletExp);
            assertEquals(pointFloorletBase, pointFloorletExp);
        }
 /// <summary>
 /// Creates shifted Black volatilities provider with specified date and index.
 /// </summary>
 /// <param name="valuationDate">  the valuation date </param>
 /// <param name="index">  the index </param>
 /// <returns>  the volatilities provider </returns>
 public static ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities createShiftedBlackVolatilities(ZonedDateTime valuationDate, IborIndex index)
 {
     return(ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(index, valuationDate, SHIFTED_BLACK_SURFACE_EXP_STR, SHIFT_CURVE));
 }
        //-------------------------------------------------------------------------
        public override IborCapletFloorletVolatilityCalibrationResult calibrate(IborCapletFloorletVolatilityDefinition definition, ZonedDateTime calibrationDateTime, RawOptionData capFloorData, RatesProvider ratesProvider)
        {
            ArgChecker.isTrue(ratesProvider.ValuationDate.Equals(calibrationDateTime.toLocalDate()), "valuationDate of ratesProvider should be coherent to calibrationDateTime");
            ArgChecker.isTrue(definition is SurfaceIborCapletFloorletVolatilityBootstrapDefinition, "definition should be SurfaceIborCapletFloorletVolatilityBootstrapDefinition");
            SurfaceIborCapletFloorletVolatilityBootstrapDefinition bsDefinition = (SurfaceIborCapletFloorletVolatilityBootstrapDefinition)definition;
            IborIndex index           = bsDefinition.Index;
            LocalDate calibrationDate = calibrationDateTime.toLocalDate();
            LocalDate baseDate        = index.EffectiveDateOffset.adjust(calibrationDate, ReferenceData);
            LocalDate startDate       = baseDate.plus(index.Tenor);

            System.Func <Surface, IborCapletFloorletVolatilities> volatilitiesFunction = this.volatilitiesFunction(bsDefinition, calibrationDateTime, capFloorData);
            SurfaceMetadata metadata                = bsDefinition.createMetadata(capFloorData);
            IList <Period>  expiries                = capFloorData.Expiries;
            int             nExpiries               = expiries.Count;
            DoubleArray     strikes                 = capFloorData.Strikes;
            DoubleMatrix    errorsMatrix            = capFloorData.Error.orElse(DoubleMatrix.filled(nExpiries, strikes.size(), 1d));
            IList <double>  timeList                = new List <double>();
            IList <double>  strikeList              = new List <double>();
            IList <double>  volList                 = new List <double>();
            IList <ResolvedIborCapFloorLeg> capList = new List <ResolvedIborCapFloorLeg>();
            IList <double> priceList                = new List <double>();
            IList <double> errorList                = new List <double>();

            int[] startIndex = new int[nExpiries + 1];
            for (int i = 0; i < nExpiries; ++i)
            {
                LocalDate   endDate        = baseDate.plus(expiries[i]);
                DoubleArray volatilityData = capFloorData.Data.row(i);
                DoubleArray errors         = errorsMatrix.row(i);
                reduceRawData(bsDefinition, ratesProvider, strikes, volatilityData, errors, startDate, endDate, metadata, volatilitiesFunction, timeList, strikeList, volList, capList, priceList, errorList);
                startIndex[i + 1] = volList.Count;
                ArgChecker.isTrue(startIndex[i + 1] > startIndex[i], "no valid option data for {}", expiries[i]);
            }
            int nTotal = startIndex[nExpiries];
            IborCapletFloorletVolatilities vols;
            int           start;
            ZonedDateTime prevExpiry;
            DoubleArray   initialVol = DoubleArray.copyOf(volList);

            if (bsDefinition.ShiftCurve.Present)
            {
                Curve       shiftCurve    = bsDefinition.ShiftCurve.get();
                DoubleArray strikeShifted = DoubleArray.of(nTotal, n => strikeList[n] + shiftCurve.yValue(timeList[n]));
                if (capFloorData.DataType.Equals(NORMAL_VOLATILITY))
                {   // correct initial surface
                    metadata   = Surfaces.blackVolatilityByExpiryStrike(bsDefinition.Name.Name, bsDefinition.DayCount).withParameterMetadata(metadata.ParameterMetadata.get());
                    initialVol = DoubleArray.of(nTotal, n => volList[n] / (ratesProvider.iborIndexRates(index).rate(capList[n].FinalPeriod.IborRate.Observation) + shiftCurve.yValue(timeList[n])));
                }
                InterpolatedNodalSurface surface = InterpolatedNodalSurface.of(metadata, DoubleArray.copyOf(timeList), strikeShifted, initialVol, bsDefinition.Interpolator);
                vols       = ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(index, calibrationDateTime, surface, bsDefinition.ShiftCurve.get());
                start      = 0;
                prevExpiry = calibrationDateTime.minusDays(1L);   // included if calibrationDateTime == fixingDateTime
            }
            else
            {
                InterpolatedNodalSurface surface = InterpolatedNodalSurface.of(metadata, DoubleArray.copyOf(timeList), DoubleArray.copyOf(strikeList), initialVol, bsDefinition.Interpolator);
                vols       = volatilitiesFunction(surface);
                start      = 1;
                prevExpiry = capList[startIndex[1] - 1].FinalFixingDateTime;
            }
            for (int i = start; i < nExpiries; ++i)
            {
                for (int j = startIndex[i]; j < startIndex[i + 1]; ++j)
                {
                    System.Func <double, double[]> func   = getValueVegaFunction(capList[j], ratesProvider, vols, prevExpiry, j);
                    GenericImpliedVolatiltySolver  solver = new GenericImpliedVolatiltySolver(func);
                    double priceFixed = i == 0 ? 0d : this.priceFixed(capList[j], ratesProvider, vols, prevExpiry);
                    double capletVol  = solver.impliedVolatility(priceList[j] - priceFixed, initialVol.get(j));
                    vols = vols.withParameter(j, capletVol);
                }
                prevExpiry = capList[startIndex[i + 1] - 1].FinalFixingDateTime;
            }
            return(IborCapletFloorletVolatilityCalibrationResult.ofRootFind(vols));
        }