public static ResolvedIborCapFloorLeg[][] ReturnRectangularResolvedIborCapFloorLegArray(int size1, int size2) { ResolvedIborCapFloorLeg[][] newArray = new ResolvedIborCapFloorLeg[size1][]; for (int array1 = 0; array1 < size1; array1++) { newArray[array1] = new ResolvedIborCapFloorLeg[size2]; } return(newArray); }
//------------------------------------------------------------------------- // price and vega function private System.Func <double, double[]> getValueVegaFunction(ResolvedIborCapFloorLeg cap, RatesProvider ratesProvider, IborCapletFloorletVolatilities vols, ZonedDateTime prevExpiry, int nodeIndex) { VolatilityIborCapletFloorletPeriodPricer periodPricer = LegPricer.PeriodPricer; System.Func <double, double[]> priceAndVegaFunction = (double?x) => { IborCapletFloorletVolatilities newVols = vols.withParameter(nodeIndex, x.Value); double price = cap.CapletFloorletPeriods.Where(p => p.FixingDateTime.isAfter(prevExpiry)).Select(p => periodPricer.presentValue(p, ratesProvider, newVols).Amount).Sum(); PointSensitivities point = cap.CapletFloorletPeriods.Where(p => p.FixingDateTime.isAfter(prevExpiry)).Select(p => periodPricer.presentValueSensitivityModelParamsVolatility(p, ratesProvider, newVols)).Aggregate((c1, c2) => c1.combinedWith(c2)).get().build(); CurrencyParameterSensitivities sensi = newVols.parameterSensitivity(point); double vega = sensi.Sensitivities.get(0).Sensitivity.get(nodeIndex); return(new double[] { price, vega }); }; return(priceAndVegaFunction); }
//------------------------------------------------------------------------- // create complete lists of caps, volatilities, strikes, expiries protected internal virtual void reduceRawData(IborCapletFloorletVolatilityDefinition definition, RatesProvider ratesProvider, DoubleArray strikes, DoubleArray volatilityData, DoubleArray errors, LocalDate startDate, LocalDate endDate, SurfaceMetadata metadata, System.Func <Surface, IborCapletFloorletVolatilities> volatilityFunction, IList <double> timeList, IList <double> strikeList, IList <double> volList, IList <ResolvedIborCapFloorLeg> capList, IList <double> priceList, IList <double> errorList) { int nStrikes = strikes.size(); for (int i = 0; i < nStrikes; ++i) { if (Double.isFinite(volatilityData.get(i))) { ResolvedIborCapFloorLeg capFloor = definition.createCap(startDate, endDate, strikes.get(i)).resolve(referenceData); capList.Add(capFloor); strikeList.Add(strikes.get(i)); volList.Add(volatilityData.get(i)); ConstantSurface constVolSurface = ConstantSurface.of(metadata, volatilityData.get(i)); IborCapletFloorletVolatilities vols = volatilityFunction(constVolSurface); timeList.Add(vols.relativeTime(capFloor.FinalFixingDateTime)); priceList.Add(pricer.presentValue(capFloor, ratesProvider, vols).Amount); errorList.Add(errors.get(i)); } } }
//------------------------------------------------------------------------- // computes initial guess for each time step private DoubleArray computeInitialValues(RatesProvider ratesProvider, Curve betaCurve, Curve shiftCurve, IList <double> timeList, IList <double> volList, IList <ResolvedIborCapFloorLeg> capList, int[] startIndex, int postion, bool betaFixed, ValueType valueType) { IList <double> vols = volList.subList(startIndex[postion], startIndex[postion + 1]); ResolvedIborCapFloorLeg cap = capList[startIndex[postion]]; double fwd = ratesProvider.iborIndexRates(cap.Index).rate(cap.FinalPeriod.IborRate.Observation); double shift = shiftCurve.yValue(timeList[startIndex[postion]]); double factor = valueType.Equals(ValueType.BLACK_VOLATILITY) ? 1d : 1d / (fwd + shift); IList <double> volsEquiv = vols.Select(v => v * factor).ToList(); double nuFirst; double betaInitial = betaFixed ? betaCurve.yValue(timeList[startIndex[postion]]) : 0.5d; double alphaInitial = DoubleArray.copyOf(volsEquiv).min() * Math.Pow(fwd, 1d - betaInitial); if (alphaInitial == volsEquiv[0] || alphaInitial == volsEquiv[volsEquiv.Count - 1]) { nuFirst = 0.1d; alphaInitial *= 0.95d; } else { nuFirst = 1d; } return(DoubleArray.of(alphaInitial, betaInitial, -0.5 * betaInitial + 0.5 * (1d - betaInitial), nuFirst)); }
//------------------------------------------------------------------------- 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 DirectIborCapletFloorletVolatilityDefinition, "definition should be DirectIborCapletFloorletVolatilityDefinition"); DirectIborCapletFloorletVolatilityDefinition directDefinition = (DirectIborCapletFloorletVolatilityDefinition)definition; // unpack cap data, create node caps IborIndex index = directDefinition.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(directDefinition, calibrationDateTime, capFloorData); SurfaceMetadata metadata = directDefinition.createMetadata(capFloorData); IList <Period> expiries = capFloorData.Expiries; DoubleArray strikes = capFloorData.Strikes; int nExpiries = expiries.Count; 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>(); DoubleMatrix errorMatrix = capFloorData.Error.orElse(DoubleMatrix.filled(nExpiries, strikes.size(), 1d)); int[] startIndex = new int[nExpiries + 1]; for (int i = 0; i < nExpiries; ++i) { LocalDate endDate = baseDate.plus(expiries[i]); DoubleArray volatilityForTime = capFloorData.Data.row(i); DoubleArray errorForTime = errorMatrix.row(i); reduceRawData(directDefinition, ratesProvider, capFloorData.Strikes, volatilityForTime, errorForTime, 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]); } // create caplet nodes and initial caplet vol surface ResolvedIborCapFloorLeg cap = capList[capList.Count - 1]; int nCaplets = cap.CapletFloorletPeriods.size(); DoubleArray capletExpiries = DoubleArray.of(nCaplets, n => directDefinition.DayCount.relativeYearFraction(calibrationDate, cap.CapletFloorletPeriods.get(n).FixingDateTime.toLocalDate())); Triple <DoubleArray, DoubleArray, DoubleArray> capletNodes; DoubleArray initialVols = DoubleArray.copyOf(volList); if (directDefinition.ShiftCurve.Present) { metadata = Surfaces.blackVolatilityByExpiryStrike(directDefinition.Name.Name, directDefinition.DayCount); Curve shiftCurve = directDefinition.ShiftCurve.get(); if (capFloorData.DataType.Equals(NORMAL_VOLATILITY)) { initialVols = DoubleArray.of(capList.Count, n => volList[n] / (ratesProvider.iborIndexRates(index).rate(capList[n].FinalPeriod.IborRate.Observation) + shiftCurve.yValue(timeList[n]))); } InterpolatedNodalSurface capVolSurface = InterpolatedNodalSurface.of(metadata, DoubleArray.copyOf(timeList), DoubleArray.copyOf(strikeList), initialVols, INTERPOLATOR); capletNodes = createCapletNodes(capVolSurface, capletExpiries, strikes, directDefinition.ShiftCurve.get()); volatilitiesFunction = createShiftedBlackVolatilitiesFunction(index, calibrationDateTime, shiftCurve); } else { InterpolatedNodalSurface capVolSurface = InterpolatedNodalSurface.of(metadata, DoubleArray.copyOf(timeList), DoubleArray.copyOf(strikeList), initialVols, INTERPOLATOR); capletNodes = createCapletNodes(capVolSurface, capletExpiries, strikes); } InterpolatedNodalSurface baseSurface = InterpolatedNodalSurface.of(metadata, capletNodes.First, capletNodes.Second, capletNodes.Third, INTERPOLATOR); DoubleMatrix penaltyMatrix = directDefinition.computePenaltyMatrix(strikes, capletExpiries); // solve least square LeastSquareResults res = solver.solve(DoubleArray.copyOf(priceList), DoubleArray.copyOf(errorList), getPriceFunction(capList, ratesProvider, volatilitiesFunction, baseSurface), getJacobianFunction(capList, ratesProvider, volatilitiesFunction, baseSurface), capletNodes.Third, penaltyMatrix, POSITIVE); InterpolatedNodalSurface resSurface = InterpolatedNodalSurface.of(metadata, capletNodes.First, capletNodes.Second, res.FitParameters, directDefinition.Interpolator); return(IborCapletFloorletVolatilityCalibrationResult.ofLeastSquare(volatilitiesFunction(resSurface), res.ChiSq)); }
// sum of caplet prices which are already fixed private double priceFixed(ResolvedIborCapFloorLeg cap, RatesProvider ratesProvider, IborCapletFloorletVolatilities vols, ZonedDateTime prevExpiry) { VolatilityIborCapletFloorletPeriodPricer periodPricer = LegPricer.PeriodPricer; return(cap.CapletFloorletPeriods.Where(p => !p.FixingDateTime.isAfter(prevExpiry)).Select(p => periodPricer.presentValue(p, ratesProvider, vols).Amount).Sum()); }