//------------------------------------------------------------------------- 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 SabrIborCapletFloorletVolatilityCalibrationDefinition, "definition should be SabrIborCapletFloorletVolatilityCalibrationDefinition"); SabrIborCapletFloorletVolatilityCalibrationDefinition sabrDefinition = (SabrIborCapletFloorletVolatilityCalibrationDefinition)definition; // unpack cap data, create node caps IborIndex index = sabrDefinition.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(sabrDefinition, calibrationDateTime, capFloorData); SurfaceMetadata metadata = sabrDefinition.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(sabrDefinition, 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 initial caplet vol surface IList <CurveMetadata> metadataList = sabrDefinition.createSabrParameterMetadata(); DoubleArray initialValues = sabrDefinition.createFullInitialValues(); IList <Curve> curveList = sabrDefinition.createSabrParameterCurve(metadataList, initialValues); SabrParameters sabrParamsInitial = SabrParameters.of(curveList[0], curveList[1], curveList[2], curveList[3], sabrDefinition.ShiftCurve, sabrDefinition.SabrVolatilityFormula); SabrParametersIborCapletFloorletVolatilities vols = SabrParametersIborCapletFloorletVolatilities.of(sabrDefinition.Name, index, calibrationDateTime, sabrParamsInitial); // solve least square UncoupledParameterTransforms transform = new UncoupledParameterTransforms(initialValues, sabrDefinition.createFullTransform(TRANSFORMS), new BitArray()); System.Func <DoubleArray, DoubleArray> valueFunction = createPriceFunction(sabrDefinition, ratesProvider, vols, capList, priceList); System.Func <DoubleArray, DoubleMatrix> jacobianFunction = createJacobianFunction(sabrDefinition, ratesProvider, vols, capList, priceList, index.Currency); NonLinearTransformFunction transFunc = new NonLinearTransformFunction(valueFunction, jacobianFunction, transform); LeastSquareResults res = solver.solve(DoubleArray.filled(priceList.Count, 1d), DoubleArray.copyOf(errorList), transFunc.FittingFunction, transFunc.FittingJacobian, transform.transform(initialValues)); LeastSquareResultsWithTransform resTransform = new LeastSquareResultsWithTransform(res, transform); vols = updateParameters(sabrDefinition, vols, resTransform.ModelParameters); return(IborCapletFloorletVolatilityCalibrationResult.ofLeastSquare(vols, res.ChiSq)); }
//------------------------------------------------------------------------- public virtual void test_builder() { ResolvedFxVanillaOption test = sut(); assertEquals(test.Expiry, EXPIRY_DATE_TIME); assertEquals(test.ExpiryDate, EXPIRY_DATE_TIME.toLocalDate()); assertEquals(test.LongShort, LONG); assertEquals(test.CounterCurrency, USD); assertEquals(test.PutCall, CALL); assertEquals(test.Strike, STRIKE); assertEquals(test.Underlying, FX); assertEquals(test.CurrencyPair, FX.CurrencyPair); }
//------------------------------------------------------------------------- public virtual void test_presentValue() { CurrencyAmount computedRec = PRICER.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, HW_PROVIDER); CurrencyAmount computedPay = PRICER.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, HW_PROVIDER); SwapPaymentEventPricer <SwapPaymentEvent> paymentEventPricer = SwapPaymentEventPricer.standard(); ResolvedSwapLeg cashFlowEquiv = CashFlowEquivalentCalculator.cashFlowEquivalentSwap(RSWAP_REC, RATE_PROVIDER); LocalDate expiryDate = MATURITY.toLocalDate(); int nPayments = cashFlowEquiv.PaymentEvents.size(); double[] alpha = new double[nPayments]; double[] discountedCashFlow = new double[nPayments]; for (int loopcf = 0; loopcf < nPayments; loopcf++) { SwapPaymentEvent payment = cashFlowEquiv.PaymentEvents.get(loopcf); alpha[loopcf] = HW_PROVIDER.alpha(RATE_PROVIDER.ValuationDate, expiryDate, expiryDate, payment.PaymentDate); discountedCashFlow[loopcf] = paymentEventPricer.presentValue(payment, RATE_PROVIDER); } double omegaPay = -1d; double kappa = HW_PROVIDER.Model.kappa(DoubleArray.copyOf(discountedCashFlow), DoubleArray.copyOf(alpha)); double expectedRec = 0.0; double expectedPay = 0.0; for (int loopcf = 0; loopcf < nPayments; loopcf++) { expectedRec += discountedCashFlow[loopcf] * NORMAL.getCDF((kappa + alpha[loopcf])); expectedPay += discountedCashFlow[loopcf] * NORMAL.getCDF(omegaPay * (kappa + alpha[loopcf])); } assertEquals(computedRec.Currency, EUR); assertEquals(computedRec.Amount, expectedRec, NOTIONAL * TOL); assertEquals(computedPay.Currency, EUR); assertEquals(computedPay.Amount, expectedPay, NOTIONAL * TOL); }
//------------------------------------------------------------------------- public virtual void test_basics() { assertEquals(VOL_SIMPLE_MONEY_PRICE.ValuationDate, VAL_DATE_TIME.toLocalDate()); assertEquals(VOL_SIMPLE_MONEY_PRICE.ValuationDateTime, VAL_DATE_TIME); assertEquals(VOL_SIMPLE_MONEY_PRICE.Index, EUR_EURIBOR_3M); assertEquals(VOL_SIMPLE_MONEY_PRICE.Name, IborFutureOptionVolatilitiesName.of("Price")); }
//------------------------------------------------------------------------- public double relativeTime(ZonedDateTime dateTime) { ArgChecker.notNull(dateTime, "dateTime"); LocalDate valuationDate = valuationDateTime.toLocalDate(); LocalDate date = dateTime.toLocalDate(); return(dayCount.relativeYearFraction(valuationDate, date)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the time to expiry for the valuation date time. /// </summary> /// <param name="valuationDateTime"> the valuation date time </param> /// <param name="dayCount"> the day count </param> /// <param name="refData"> the reference data </param> /// <returns> the time to expiry </returns> public double timeToExpiry(ZonedDateTime valuationDateTime, DayCount dayCount, ReferenceData refData) { LocalDate valuationDate = valuationDateTime.toLocalDate(); LocalDate spotDate = spotDateOffset.adjust(valuationDate, refData); LocalDate deliveryDate = businessDayAdjustment.adjust(spotDate.plus(tenor), refData); LocalDate expiryDate = expiryDateOffset_Renamed.adjust(deliveryDate, refData); return(dayCount.relativeYearFraction(valuationDate, expiryDate)); }
public virtual void test_expiry() { FxOptionVolatilitiesNode test = FxOptionVolatilitiesNode.of(EUR_GBP, SPOT_DATE_OFFSET, BDA, ValueType.BLACK_VOLATILITY, QUOTE_ID, Tenor.TENOR_3M, STRIKE); ZonedDateTime dateTime = LocalDate.of(2016, 1, 23).atStartOfDay(ZoneId.of("Europe/London")); DaysAdjustment expAdj = DaysAdjustment.ofBusinessDays(-2, LO_TA); double computed = test.timeToExpiry(dateTime, ACT_365F, REF_DATA); double expected = ACT_365F.relativeYearFraction(dateTime.toLocalDate(), expAdj.adjust(BDA.adjust(SPOT_DATE_OFFSET.adjust(dateTime.toLocalDate(), REF_DATA).plus(Tenor.TENOR_3M), REF_DATA), REF_DATA)); assertEquals(computed, expected); }
//------------------------------------------------------------------------- public virtual void test_builder() { ResolvedSwaption test = sut(); assertEquals(test.ExpiryDate, EXPIRY.toLocalDate()); assertEquals(test.Expiry, EXPIRY); assertEquals(test.LongShort, LONG); assertEquals(test.SwaptionSettlement, PHYSICAL_SETTLE); assertEquals(test.Underlying, SWAP); assertEquals(test.Currency, USD); assertEquals(test.Index, USD_LIBOR_3M); }
public virtual void test_builder_min() { IborCapletFloorletPeriod test = IborCapletFloorletPeriod.builder().notional(NOTIONAL).startDate(START).endDate(END).yearFraction(YEAR_FRACTION).caplet(STRIKE).iborRate(RATE_COMP).build(); assertEquals(test.Caplet.Value, STRIKE); assertEquals(test.Floorlet.HasValue, false); assertEquals(test.Strike, STRIKE); assertEquals(test.StartDate, START); assertEquals(test.EndDate, END); assertEquals(test.PaymentDate, test.EndDate); assertEquals(test.Currency, EUR); assertEquals(test.Notional, NOTIONAL); assertEquals(test.IborRate, RATE_COMP); assertEquals(test.Index, EUR_EURIBOR_3M); assertEquals(test.FixingDate, FIXING_TIME_ZONE.toLocalDate()); assertEquals(test.FixingDateTime, FIXING_TIME_ZONE); assertEquals(test.PutCall, PutCall.CALL); assertEquals(test.UnadjustedStartDate, START); assertEquals(test.UnadjustedEndDate, END); assertEquals(test.YearFraction, YEAR_FRACTION); }
public virtual void test_volatilities() { BlackFxOptionSmileVolatilitiesSpecification @base = BlackFxOptionSmileVolatilitiesSpecification.builder().name(VOL_NAME).currencyPair(EUR_GBP).dayCount(ACT_360).nodes(NODES).timeInterpolator(PCHIP).strikeInterpolator(PCHIP).build(); LocalDate date = LocalDate.of(2017, 9, 25); ZonedDateTime dateTime = date.atStartOfDay().atZone(ZoneId.of("Europe/London")); DoubleArray parameters = DoubleArray.of(0.05, -0.05, 0.15, 0.25, 0.1, -0.1); BlackFxOptionSmileVolatilities computed = @base.volatilities(dateTime, parameters, REF_DATA); LocalDate spotDate = SPOT_OFFSET.adjust(dateTime.toLocalDate(), REF_DATA); DaysAdjustment expOffset = DaysAdjustment.ofBusinessDays(-2, TA_LO); DoubleArray expiries = DoubleArray.of(ACT_360.relativeYearFraction(date, expOffset.adjust(BUS_ADJ.adjust(spotDate.plus(Tenor.TENOR_3M), REF_DATA), REF_DATA)), ACT_360.relativeYearFraction(date, expOffset.adjust(BUS_ADJ.adjust(spotDate.plus(Tenor.TENOR_1Y), REF_DATA), REF_DATA))); SmileDeltaTermStructure smiles = InterpolatedStrikeSmileDeltaTermStructure.of(expiries, DoubleArray.of(0.1), DoubleArray.of(0.25, 0.15), DoubleMatrix.ofUnsafe(new double[][] { new double[] { -0.1 }, new double[] { -0.05 } }), DoubleMatrix.ofUnsafe(new double[][] { new double[] { 0.1 }, new double[] { 0.05 } }), ACT_360, PCHIP, FLAT, FLAT, PCHIP, FLAT, FLAT); BlackFxOptionSmileVolatilities expected = BlackFxOptionSmileVolatilities.of(VOL_NAME, EUR_GBP, dateTime, smiles); assertEquals(computed, expected); }
//------------------------------------------------------------------------- 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 SabrIborCapletFloorletVolatilityBootstrapDefinition, "definition should be SabrIborCapletFloorletVolatilityBootstrapDefinition"); SabrIborCapletFloorletVolatilityBootstrapDefinition bsDefinition = (SabrIborCapletFloorletVolatilityBootstrapDefinition)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]); } IList <CurveMetadata> metadataList = bsDefinition.createSabrParameterMetadata(); DoubleArray timeToExpiries = DoubleArray.of(nExpiries, i => timeList[startIndex[i]]); BitArray @fixed = new BitArray(); bool betaFix = false; Curve betaCurve; Curve rhoCurve; if (bsDefinition.BetaCurve.Present) { betaFix = true; @fixed.Set(1, true); betaCurve = bsDefinition.BetaCurve.get(); rhoCurve = InterpolatedNodalCurve.of(metadataList[2], timeToExpiries, DoubleArray.filled(nExpiries), bsDefinition.Interpolator, bsDefinition.ExtrapolatorLeft, bsDefinition.ExtrapolatorRight); } else { @fixed.Set(2, true); betaCurve = InterpolatedNodalCurve.of(metadataList[1], timeToExpiries, DoubleArray.filled(nExpiries), bsDefinition.Interpolator, bsDefinition.ExtrapolatorLeft, bsDefinition.ExtrapolatorRight); rhoCurve = bsDefinition.RhoCurve.get(); } InterpolatedNodalCurve alphaCurve = InterpolatedNodalCurve.of(metadataList[0], timeToExpiries, DoubleArray.filled(nExpiries), bsDefinition.Interpolator, bsDefinition.ExtrapolatorLeft, bsDefinition.ExtrapolatorRight); InterpolatedNodalCurve nuCurve = InterpolatedNodalCurve.of(metadataList[3], timeToExpiries, DoubleArray.filled(nExpiries), bsDefinition.Interpolator, bsDefinition.ExtrapolatorLeft, bsDefinition.ExtrapolatorRight); Curve shiftCurve = bsDefinition.ShiftCurve; SabrParameters sabrParams = SabrParameters.of(alphaCurve, betaCurve, rhoCurve, nuCurve, shiftCurve, bsDefinition.SabrVolatilityFormula); SabrParametersIborCapletFloorletVolatilities vols = SabrParametersIborCapletFloorletVolatilities.of(bsDefinition.Name, index, calibrationDateTime, sabrParams); double totalChiSq = 0d; ZonedDateTime prevExpiry = calibrationDateTime.minusDays(1L); // included if calibrationDateTime == fixingDateTime for (int i = 0; i < nExpiries; ++i) { DoubleArray start = computeInitialValues(ratesProvider, betaCurve, shiftCurve, timeList, volList, capList, startIndex, i, betaFix, capFloorData.DataType); UncoupledParameterTransforms transform = new UncoupledParameterTransforms(start, TRANSFORMS, @fixed); int nCaplets = startIndex[i + 1] - startIndex[i]; int currentStart = startIndex[i]; System.Func <DoubleArray, DoubleArray> valueFunction = createPriceFunction(ratesProvider, vols, prevExpiry, capList, priceList, startIndex, nExpiries, i, nCaplets, betaFix); System.Func <DoubleArray, DoubleMatrix> jacobianFunction = createJacobianFunction(ratesProvider, vols, prevExpiry, capList, priceList, index.Currency, startIndex, nExpiries, i, nCaplets, betaFix); NonLinearTransformFunction transFunc = new NonLinearTransformFunction(valueFunction, jacobianFunction, transform); DoubleArray adjustedPrices = this.adjustedPrices(ratesProvider, vols, prevExpiry, capList, priceList, startIndex, i, nCaplets); DoubleArray errors = DoubleArray.of(nCaplets, n => errorList[currentStart + n]); LeastSquareResults res = solver.solve(adjustedPrices, errors, transFunc.FittingFunction, transFunc.FittingJacobian, transform.transform(start)); LeastSquareResultsWithTransform resTransform = new LeastSquareResultsWithTransform(res, transform); vols = updateParameters(vols, nExpiries, i, betaFix, resTransform.ModelParameters); totalChiSq += res.ChiSq; prevExpiry = capList[startIndex[i + 1] - 1].FinalFixingDateTime; } return(IborCapletFloorletVolatilityCalibrationResult.ofLeastSquare(vols, totalChiSq)); }
/// <summary> /// Computes the present value sensitivity to SABR parameters by replication in SABR framework with extrapolation on the right. /// </summary> /// <param name="cmsPeriod"> the CMS </param> /// <param name="provider"> the rates provider </param> /// <param name="swaptionVolatilities"> the swaption volatilities </param> /// <returns> the present value sensitivity </returns> public PointSensitivityBuilder presentValueSensitivityModelParamsSabr(CmsPeriod cmsPeriod, RatesProvider provider, SabrSwaptionVolatilities swaptionVolatilities) { Currency ccy = cmsPeriod.Currency; SwapIndex index = cmsPeriod.Index; ResolvedSwap swap = cmsPeriod.UnderlyingSwap; double dfPayment = provider.discountFactor(ccy, cmsPeriod.PaymentDate); ZonedDateTime valuationDate = swaptionVolatilities.ValuationDateTime; LocalDate fixingDate = cmsPeriod.FixingDate; ZonedDateTime expiryDate = fixingDate.atTime(index.FixingTime).atZone(index.FixingZone); double tenor = swaptionVolatilities.tenor(swap.StartDate, swap.EndDate); if (provider.ValuationDate.isAfter(cmsPeriod.PaymentDate)) { return(PointSensitivityBuilder.none()); } if (!fixingDate.isAfter(valuationDate.toLocalDate())) { double?fixedRate = provider.timeSeries(cmsPeriod.Index).get(fixingDate); if (fixedRate.HasValue) { return(PointSensitivityBuilder.none()); } else if (fixingDate.isBefore(valuationDate.toLocalDate())) { throw new System.ArgumentException(Messages.format("Unable to get fixing for {} on date {}, no time-series supplied", cmsPeriod.Index, fixingDate)); } } double expiryTime = swaptionVolatilities.relativeTime(expiryDate); double shift = swaptionVolatilities.shift(expiryTime, tenor); double strikeCpn = cmsPeriod.CmsPeriodType.Equals(CmsPeriodType.COUPON) ? -shift : cmsPeriod.Strike; double forward = swapPricer.parRate(swap, provider); double eta = index.Template.Convention.FixedLeg.DayCount.relativeYearFraction(cmsPeriod.PaymentDate, swap.StartDate); CmsIntegrantProvider intProv = new CmsIntegrantProvider(this, cmsPeriod, swap, swaptionVolatilities, forward, strikeCpn, expiryTime, tenor, cutOffStrike, eta); double factor = dfPayment / intProv.h(forward) * intProv.g(forward); double factor2 = factor * intProv.k(strikeCpn); double[] strikePartPrice = intProv.SabrExtrapolation.priceAdjointSabr(Math.Max(0d, strikeCpn + shift), intProv.PutCall).Derivatives.multipliedBy(factor2).toArray(); RungeKuttaIntegrator1D integrator = new RungeKuttaIntegrator1D(ABS_TOL, REL_TOL_VEGA, NUM_ITER); double[] totalSensi = new double[4]; for (int loopparameter = 0; loopparameter < 4; loopparameter++) { double integralPart = 0d; System.Func <double, double> integrant = intProv.integrantVega(loopparameter); try { if (intProv.PutCall.Call) { integralPart = dfPayment * integrateCall(integrator, integrant, swaptionVolatilities, forward, strikeCpn, expiryTime, tenor); } else { integralPart = -dfPayment *integrator.integrate(integrant, -shift + ZERO_SHIFT, strikeCpn); } } catch (Exception e) { throw new Exception(e); } totalSensi[loopparameter] = (strikePartPrice[loopparameter] + integralPart) * cmsPeriod.Notional * cmsPeriod.YearFraction; } SwaptionVolatilitiesName name = swaptionVolatilities.Name; return(PointSensitivityBuilder.of(SwaptionSabrSensitivity.of(name, expiryTime, tenor, ALPHA, ccy, totalSensi[0]), SwaptionSabrSensitivity.of(name, expiryTime, tenor, BETA, ccy, totalSensi[1]), SwaptionSabrSensitivity.of(name, expiryTime, tenor, RHO, ccy, totalSensi[2]), SwaptionSabrSensitivity.of(name, expiryTime, tenor, NU, ccy, totalSensi[3]))); }
//------------------------------------------------------------------------- /// <summary> /// Computes the present value curve sensitivity by replication in SABR framework with extrapolation on the right. /// </summary> /// <param name="cmsPeriod"> the CMS </param> /// <param name="provider"> the rates provider </param> /// <param name="swaptionVolatilities"> the swaption volatilities </param> /// <returns> the present value sensitivity </returns> public PointSensitivityBuilder presentValueSensitivityRates(CmsPeriod cmsPeriod, RatesProvider provider, SabrSwaptionVolatilities swaptionVolatilities) { Currency ccy = cmsPeriod.Currency; if (provider.ValuationDate.isAfter(cmsPeriod.PaymentDate)) { return(PointSensitivityBuilder.none()); } SwapIndex index = cmsPeriod.Index; ResolvedSwap swap = cmsPeriod.UnderlyingSwap; double dfPayment = provider.discountFactor(ccy, cmsPeriod.PaymentDate); ZonedDateTime valuationDate = swaptionVolatilities.ValuationDateTime; LocalDate fixingDate = cmsPeriod.FixingDate; double expiryTime = swaptionVolatilities.relativeTime(fixingDate.atTime(index.FixingTime).atZone(index.FixingZone)); double tenor = swaptionVolatilities.tenor(swap.StartDate, swap.EndDate); double shift = swaptionVolatilities.shift(expiryTime, tenor); double strikeCpn = cmsPeriod.CmsPeriodType.Equals(CmsPeriodType.COUPON) ? -shift : cmsPeriod.Strike; if (!fixingDate.isAfter(valuationDate.toLocalDate())) { double?fixedRate = provider.timeSeries(cmsPeriod.Index).get(fixingDate); if (fixedRate.HasValue) { double payoff = payOff(cmsPeriod.CmsPeriodType, strikeCpn, fixedRate.Value); return(provider.discountFactors(ccy).zeroRatePointSensitivity(cmsPeriod.PaymentDate).multipliedBy(payoff * cmsPeriod.Notional * cmsPeriod.YearFraction)); } else if (fixingDate.isBefore(valuationDate.toLocalDate())) { throw new System.ArgumentException(Messages.format("Unable to get fixing for {} on date {}, no time-series supplied", cmsPeriod.Index, fixingDate)); } } double forward = swapPricer.parRate(swap, provider); double eta = index.Template.Convention.FixedLeg.DayCount.relativeYearFraction(cmsPeriod.PaymentDate, swap.StartDate); CmsDeltaIntegrantProvider intProv = new CmsDeltaIntegrantProvider(this, cmsPeriod, swap, swaptionVolatilities, forward, strikeCpn, expiryTime, tenor, cutOffStrike, eta); RungeKuttaIntegrator1D integrator = new RungeKuttaIntegrator1D(ABS_TOL, REL_TOL, NUM_ITER); double[] bs = intProv.bsbsp(strikeCpn); double[] n = intProv.Nnp; double strikePartPrice = intProv.k(strikeCpn) * n[0] * bs[0]; double integralPartPrice = 0d; double integralPart = 0d; System.Func <double, double> integrant = intProv.integrant(); System.Func <double, double> integrantDelta = intProv.integrantDelta(); try { if (intProv.PutCall.Call) { integralPartPrice = integrateCall(integrator, integrant, swaptionVolatilities, forward, strikeCpn, expiryTime, tenor); integralPart = dfPayment * integrateCall(integrator, integrantDelta, swaptionVolatilities, forward, strikeCpn, expiryTime, tenor); } else { integralPartPrice = -integrator.integrate(integrant, -shift + ZERO_SHIFT, strikeCpn).Value; integralPart = -dfPayment *integrator.integrate(integrantDelta, -shift, strikeCpn); } } catch (Exception e) { throw new MathException(e); } double deltaPD = strikePartPrice + integralPartPrice; if (cmsPeriod.CmsPeriodType.Equals(CmsPeriodType.COUPON)) { deltaPD -= shift; } deltaPD *= cmsPeriod.Notional * cmsPeriod.YearFraction; double strikePart = dfPayment * intProv.k(strikeCpn) * (n[1] * bs[0] + n[0] * bs[1]); double deltaFwd = (strikePart + integralPart) * cmsPeriod.Notional * cmsPeriod.YearFraction; PointSensitivityBuilder sensiFwd = swapPricer.parRateSensitivity(swap, provider).multipliedBy(deltaFwd); PointSensitivityBuilder sensiDf = provider.discountFactors(ccy).zeroRatePointSensitivity(cmsPeriod.PaymentDate).multipliedBy(deltaPD); return(sensiFwd.combinedWith(sensiDf)); }
//------------------------------------------------------------------------- /// <summary> /// Computes the present value by replication in SABR framework with extrapolation on the right. /// </summary> /// <param name="cmsPeriod"> the CMS </param> /// <param name="provider"> the rates provider </param> /// <param name="swaptionVolatilities"> the swaption volatilities </param> /// <returns> the present value </returns> public CurrencyAmount presentValue(CmsPeriod cmsPeriod, RatesProvider provider, SabrSwaptionVolatilities swaptionVolatilities) { Currency ccy = cmsPeriod.Currency; if (provider.ValuationDate.isAfter(cmsPeriod.PaymentDate)) { return(CurrencyAmount.zero(ccy)); } SwapIndex index = cmsPeriod.Index; ResolvedSwap swap = cmsPeriod.UnderlyingSwap; double dfPayment = provider.discountFactor(ccy, cmsPeriod.PaymentDate); ZonedDateTime valuationDate = swaptionVolatilities.ValuationDateTime; LocalDate fixingDate = cmsPeriod.FixingDate; double expiryTime = swaptionVolatilities.relativeTime(fixingDate.atTime(index.FixingTime).atZone(index.FixingZone)); double tenor = swaptionVolatilities.tenor(swap.StartDate, swap.EndDate); double shift = swaptionVolatilities.shift(expiryTime, tenor); double strikeCpn = cmsPeriod.CmsPeriodType.Equals(CmsPeriodType.COUPON) ? -shift : cmsPeriod.Strike; if (!fixingDate.isAfter(valuationDate.toLocalDate())) { double?fixedRate = provider.timeSeries(cmsPeriod.Index).get(fixingDate); if (fixedRate.HasValue) { double payoff = payOff(cmsPeriod.CmsPeriodType, strikeCpn, fixedRate.Value); return(CurrencyAmount.of(ccy, dfPayment * payoff * cmsPeriod.Notional * cmsPeriod.YearFraction)); } else if (fixingDate.isBefore(valuationDate.toLocalDate())) { throw new System.ArgumentException(Messages.format("Unable to get fixing for {} on date {}, no time-series supplied", cmsPeriod.Index, fixingDate)); } } double forward = swapPricer.parRate(swap, provider); if (expiryTime < MIN_TIME) { double payoff = payOff(cmsPeriod.CmsPeriodType, strikeCpn, forward); return(CurrencyAmount.of(ccy, dfPayment * payoff * cmsPeriod.Notional * cmsPeriod.YearFraction)); } double eta = index.Template.Convention.FixedLeg.DayCount.relativeYearFraction(cmsPeriod.PaymentDate, swap.StartDate); CmsIntegrantProvider intProv = new CmsIntegrantProvider(this, cmsPeriod, swap, swaptionVolatilities, forward, strikeCpn, expiryTime, tenor, cutOffStrike, eta); double factor = dfPayment / intProv.h(forward) * intProv.g(forward); double strikePart = factor * intProv.k(strikeCpn) * intProv.bs(strikeCpn); RungeKuttaIntegrator1D integrator = new RungeKuttaIntegrator1D(ABS_TOL, REL_TOL, NUM_ITER); double integralPart = 0d; System.Func <double, double> integrant = intProv.integrant(); try { if (intProv.PutCall.Call) { integralPart = dfPayment * integrateCall(integrator, integrant, swaptionVolatilities, forward, strikeCpn, expiryTime, tenor); } else { integralPart = -dfPayment *integrator.integrate(integrant, -shift + ZERO_SHIFT, strikeCpn); } } catch (Exception e) { throw new MathException(e); } double priceCMS = (strikePart + integralPart); if (cmsPeriod.CmsPeriodType.Equals(CmsPeriodType.COUPON)) { priceCMS -= dfPayment * shift; } priceCMS *= cmsPeriod.Notional * cmsPeriod.YearFraction; return(CurrencyAmount.of(ccy, priceCMS)); }
//------------------------------------------------------------------------- public double relativeTime(ZonedDateTime zonedDateTime) { ArgChecker.notNull(zonedDateTime, "date"); return(dayCount.relativeYearFraction(valuationDateTime.toLocalDate(), zonedDateTime.toLocalDate())); }
//------------------------------------------------------------------------- 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)); }
/// <summary> /// Computes the present value sensitivity to strike by replication in SABR framework with extrapolation on the right. /// </summary> /// <param name="cmsPeriod"> the CMS </param> /// <param name="provider"> the rates provider </param> /// <param name="swaptionVolatilities"> the swaption volatilities </param> /// <returns> the present value sensitivity </returns> public double presentValueSensitivityStrike(CmsPeriod cmsPeriod, RatesProvider provider, SabrSwaptionVolatilities swaptionVolatilities) { ArgChecker.isFalse(cmsPeriod.CmsPeriodType.Equals(CmsPeriodType.COUPON), "presentValueSensitivityStrike is not relevant for CMS coupon"); Currency ccy = cmsPeriod.Currency; SwapIndex index = cmsPeriod.Index; if (provider.ValuationDate.isAfter(cmsPeriod.PaymentDate)) { return(0d); } ResolvedSwap swap = cmsPeriod.UnderlyingSwap; double dfPayment = provider.discountFactor(ccy, cmsPeriod.PaymentDate); ZonedDateTime valuationDate = swaptionVolatilities.ValuationDateTime; LocalDate fixingDate = cmsPeriod.FixingDate; double tenor = swaptionVolatilities.tenor(swap.StartDate, swap.EndDate); ZonedDateTime expiryDate = fixingDate.atTime(index.FixingTime).atZone(index.FixingZone); double expiryTime = swaptionVolatilities.relativeTime(expiryDate); double strike = cmsPeriod.Strike; double shift = swaptionVolatilities.shift(expiryTime, tenor); if (!fixingDate.isAfter(valuationDate.toLocalDate())) { double?fixedRate = provider.timeSeries(cmsPeriod.Index).get(fixingDate); if (fixedRate.HasValue) { double payoff = 0d; switch (cmsPeriod.CmsPeriodType) { case CAPLET: payoff = fixedRate.Value >= strike ? -1d : 0d; break; case FLOORLET: payoff = fixedRate.Value < strike ? 1d : 0d; break; default: throw new System.ArgumentException("unsupported CMS type"); } return(payoff * cmsPeriod.Notional * cmsPeriod.YearFraction * dfPayment); } else if (fixingDate.isBefore(valuationDate.toLocalDate())) { throw new System.ArgumentException(Messages.format("Unable to get fixing for {} on date {}, no time-series supplied", cmsPeriod.Index, fixingDate)); } } double forward = swapPricer.parRate(swap, provider); double eta = index.Template.Convention.FixedLeg.DayCount.relativeYearFraction(cmsPeriod.PaymentDate, swap.StartDate); CmsIntegrantProvider intProv = new CmsIntegrantProvider(this, cmsPeriod, swap, swaptionVolatilities, forward, strike, expiryTime, tenor, cutOffStrike, eta); double factor = dfPayment * intProv.g(forward) / intProv.h(forward); RungeKuttaIntegrator1D integrator = new RungeKuttaIntegrator1D(ABS_TOL, REL_TOL_STRIKE, NUM_ITER); double[] kpkpp = intProv.kpkpp(strike); double firstPart; double thirdPart; System.Func <double, double> integrant = intProv.integrantDualDelta(); if (intProv.PutCall.Call) { firstPart = -kpkpp[0] * intProv.bs(strike); thirdPart = integrateCall(integrator, integrant, swaptionVolatilities, forward, strike, expiryTime, tenor); } else { firstPart = -kpkpp[0] * intProv.bs(strike); thirdPart = -integrator.integrate(integrant, -shift + ZERO_SHIFT, strike).Value; } double secondPart = intProv.k(strike) * intProv.SabrExtrapolation.priceDerivativeStrike(strike + shift, intProv.PutCall); return(cmsPeriod.Notional * cmsPeriod.YearFraction * factor * (firstPart + secondPart + thirdPart)); }
//------------------------------------------------------------------------- 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)); }