//------------------------------------------------------------------------- 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); }
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); }
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 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); }
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)); } } }
/// <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)); }