/// <summary> /// Calculates the par spread curve sensitivity for a swap. /// <para> /// The par spread is the common spread on all payments of the first leg for which the total swap present value is 0. /// </para> /// <para> /// The par spread is computed with respect to the first leg. For that leg, all the payments have a unique /// accrual period (no compounding) and no FX reset. /// /// </para> /// </summary> /// <param name="swap"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the par spread curve sensitivity of the swap product </returns> public virtual PointSensitivityBuilder parSpreadSensitivity(ResolvedSwap swap, RatesProvider provider) { ResolvedSwapLeg referenceLeg = swap.Legs.get(0); Currency ccyReferenceLeg = referenceLeg.Currency; double convertedPv = presentValue(swap, ccyReferenceLeg, provider).Amount; PointSensitivityBuilder convertedPvDr = presentValueSensitivity(swap, ccyReferenceLeg, provider); // try one payment compounding, typically for inflation swaps Triple <bool, int, double> fixedCompounded = checkFixedCompounded(referenceLeg); if (fixedCompounded.First) { double df = provider.discountFactor(ccyReferenceLeg, referenceLeg.PaymentPeriods.get(0).PaymentDate); PointSensitivityBuilder dfDr = provider.discountFactors(ccyReferenceLeg).zeroRatePointSensitivity(referenceLeg.PaymentPeriods.get(0).PaymentDate); double referenceConvertedPv = legPricer.presentValue(referenceLeg, provider).Amount; PointSensitivityBuilder referenceConvertedPvDr = legPricer.presentValueSensitivity(referenceLeg, provider); double notional = ((RatePaymentPeriod)referenceLeg.PaymentPeriods.get(0)).Notional; PointSensitivityBuilder dParSpreadDr = convertedPvDr.combinedWith(referenceConvertedPvDr.multipliedBy(-1)).multipliedBy(-1.0d / (df * notional)).combinedWith(dfDr.multipliedBy((convertedPv - referenceConvertedPv) / (df * df * notional))).multipliedBy(1.0d / fixedCompounded.Second * Math.Pow(-(convertedPv - referenceConvertedPv) / (df * notional) + 1.0d, 1.0d / fixedCompounded.Second - 1.0d)); return(dParSpreadDr); } double pvbp = legPricer.pvbp(referenceLeg, provider); // Backward sweep double convertedPvBar = -1d / pvbp; double pvbpBar = convertedPv / (pvbp * pvbp); PointSensitivityBuilder pvbpDr = legPricer.pvbpSensitivity(referenceLeg, provider); return(convertedPvDr.multipliedBy(convertedPvBar).combinedWith(pvbpDr.multipliedBy(pvbpBar))); }
//------------------------------------------------------------------------- public virtual void test_combinedWith() { PointSensitivityBuilder @base = PointSensitivityBuilder.none(); PointSensitivityBuilder ibor = DummyPointSensitivity.of(GBP, date(2015, 6, 30), 2.0d); assertSame(@base.combinedWith(ibor), ibor); // returns other }
private PointSensitivityBuilder rateForwardSensitivity(OvernightAveragedRateComputation computation, OvernightIndexRates rates) { OvernightIndex index = computation.Index; HolidayCalendar calendar = computation.FixingCalendar; LocalDate startFixingDate = computation.StartDate; LocalDate endFixingDateP1 = computation.EndDate; LocalDate endFixingDate = calendar.previous(endFixingDateP1); LocalDate onRateEndDate = computation.calculateMaturityFromFixing(endFixingDate); LocalDate onRateStartDate = computation.calculateEffectiveFromFixing(startFixingDate); LocalDate lastNonCutOffMatDate = onRateEndDate; int cutoffOffset = computation.RateCutOffDays > 1 ? computation.RateCutOffDays : 1; PointSensitivityBuilder combinedPointSensitivityBuilder = PointSensitivityBuilder.none(); double accrualFactorTotal = index.DayCount.yearFraction(onRateStartDate, onRateEndDate); if (cutoffOffset > 1) { // Cut-off period IList <double> noCutOffAccrualFactorList = new List <double>(); LocalDate currentFixingDate = endFixingDateP1; LocalDate cutOffEffectiveDate; for (int i = 0; i < cutoffOffset; i++) { currentFixingDate = calendar.previous(currentFixingDate); cutOffEffectiveDate = computation.calculateEffectiveFromFixing(currentFixingDate); lastNonCutOffMatDate = computation.calculateMaturityFromEffective(cutOffEffectiveDate); double accrualFactor = index.DayCount.yearFraction(cutOffEffectiveDate, lastNonCutOffMatDate); noCutOffAccrualFactorList.Add(accrualFactor); } OvernightIndexObservation lastIndexObs = computation.observeOn(currentFixingDate); PointSensitivityBuilder forwardRateCutOffSensitivity = rates.ratePointSensitivity(lastIndexObs); double totalAccrualFactor = 0.0; for (int i = 0; i < cutoffOffset - 1; i++) { totalAccrualFactor += noCutOffAccrualFactorList[i]; } forwardRateCutOffSensitivity = forwardRateCutOffSensitivity.multipliedBy(totalAccrualFactor); combinedPointSensitivityBuilder = combinedPointSensitivityBuilder.combinedWith(forwardRateCutOffSensitivity); } // Approximated part OvernightIndexObservation indexObs = computation.observeOn(onRateStartDate); PointSensitivityBuilder approximatedInterestAndSensitivity = approximatedInterestSensitivity(indexObs, lastNonCutOffMatDate, rates); combinedPointSensitivityBuilder = combinedPointSensitivityBuilder.combinedWith(approximatedInterestAndSensitivity); combinedPointSensitivityBuilder = combinedPointSensitivityBuilder.multipliedBy(1.0 / accrualFactorTotal); // final rate return(combinedPointSensitivityBuilder); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the Present Value of a Basis Point curve sensitivity for a fixed swap leg. /// <para> /// The Present Value of a Basis Point is the value of the leg when the rate is equal to 1. /// A better name would be "Present Value of 1". /// The quantity is also known as "physical annuity" or "level". /// </para> /// <para> /// Each period must not have FX reset or compounding. /// They must not be of type <seealso cref="KnownAmountSwapPaymentPeriod"/>. /// /// </para> /// </summary> /// <param name="fixedLeg"> the swap fixed leg </param> /// <param name="provider"> the rates provider </param> /// <returns> the Present Value of a Basis Point sensitivity to the curves </returns> public virtual PointSensitivityBuilder pvbpSensitivity(ResolvedSwapLeg fixedLeg, RatesProvider provider) { PointSensitivityBuilder builder = PointSensitivityBuilder.none(); foreach (SwapPaymentPeriod period in fixedLeg.PaymentPeriods) { builder = builder.combinedWith(paymentPeriodPricer.pvbpSensitivity(period, provider)); } return(builder); }
// calculate present or forecast value sensitivity for the swap private static PointSensitivityBuilder swapValueSensitivity(ResolvedSwap swap, RatesProvider provider, System.Func <ResolvedSwapLeg, RatesProvider, PointSensitivityBuilder> legFn) { PointSensitivityBuilder builder = PointSensitivityBuilder.none(); foreach (ResolvedSwapLeg leg in swap.Legs) { builder = builder.combinedWith(legFn(leg, provider)); } return(builder); }
// calculate present or forecast value sensitivity for a leg private PointSensitivityBuilder legValueSensitivity(ResolvedSwapLeg leg, RatesProvider provider, System.Func <SwapPaymentPeriod, RatesProvider, PointSensitivityBuilder> periodFn, System.Func <SwapPaymentEvent, RatesProvider, PointSensitivityBuilder> eventFn) { PointSensitivityBuilder builder = PointSensitivityBuilder.none(); foreach (SwapPaymentPeriod period in leg.PaymentPeriods) { if (!period.PaymentDate.isBefore(provider.ValuationDate)) { builder = builder.combinedWith(periodFn(period, provider)); } } foreach (SwapPaymentEvent @event in leg.PaymentEvents) { if ([email protected](provider.ValuationDate)) { builder = builder.combinedWith(eventFn(@event, provider)); } } return(builder); }
/// <summary> /// Calculates the present value sensitivity of the swap product converted in a given currency. /// <para> /// The present value sensitivity of the product is the sensitivity of the present value to /// the underlying curves. /// /// </para> /// </summary> /// <param name="swap"> the product </param> /// <param name="currency"> the currency to convert to </param> /// <param name="provider"> the rates provider </param> /// <returns> the present value curve sensitivity of the swap product converted in the given currency </returns> public virtual PointSensitivityBuilder presentValueSensitivity(ResolvedSwap swap, Currency currency, RatesProvider provider) { PointSensitivityBuilder builder = PointSensitivityBuilder.none(); foreach (ResolvedSwapLeg leg in swap.Legs) { PointSensitivityBuilder ls = legPricer.presentValueSensitivity(leg, provider); PointSensitivityBuilder lsConverted = ls.withCurrency(currency).multipliedBy(provider.fxRate(leg.Currency, currency)); builder = builder.combinedWith(lsConverted); } return(builder); }
// Calculate the total rate sensitivity internal PointSensitivityBuilder calculateRateSensitivity() { double factor = pastCompositionFactor() * valuationCompositionFactor() / accrualFactorTotal; ObjDoublePair <PointSensitivityBuilder> compositionFactorAndSensitivityNonCutoff = this.compositionFactorAndSensitivityNonCutoff(); ObjDoublePair <PointSensitivityBuilder> compositionFactorAndSensitivityCutoff = this.compositionFactorAndSensitivityCutoff(); PointSensitivityBuilder combinedPointSensitivity = compositionFactorAndSensitivityNonCutoff.First.multipliedBy(compositionFactorAndSensitivityCutoff.Second * factor); combinedPointSensitivity = combinedPointSensitivity.combinedWith(compositionFactorAndSensitivityCutoff.First.multipliedBy(compositionFactorAndSensitivityNonCutoff.Second * factor)); return(combinedPointSensitivity); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value curve sensitivity of the CMS product. /// <para> /// The present value sensitivity of the product is the sensitivity of the present value to the underlying curves. /// /// </para> /// </summary> /// <param name="cms"> the CMS product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the swaption volatilities </param> /// <returns> the present value sensitivity </returns> public virtual PointSensitivityBuilder presentValueSensitivityRates(ResolvedCms cms, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { PointSensitivityBuilder pvSensiCmsLeg = cmsLegPricer.presentValueSensitivityRates(cms.CmsLeg, ratesProvider, swaptionVolatilities); if (!cms.PayLeg.Present) { return(pvSensiCmsLeg); } PointSensitivityBuilder pvSensiPayLeg = payLegPricer.presentValueSensitivity(cms.PayLeg.get(), ratesProvider); return(pvSensiCmsLeg.combinedWith(pvSensiPayLeg)); }
// Calculate the total rate sensitivity. internal PointSensitivityBuilder calculateRateSensitivity() { // call these methods to ensure mutable fixedPeriod variable is updated pastAccumulation(); valuationDateAccumulation(); // calculate sensitivity PointSensitivityBuilder combinedPointSensitivity = approximatedForwardAccumulationSensitivity(); PointSensitivityBuilder cutOffAccumulationSensitivity = this.cutOffAccumulationSensitivity(); combinedPointSensitivity = combinedPointSensitivity.combinedWith(cutOffAccumulationSensitivity); combinedPointSensitivity = combinedPointSensitivity.multipliedBy(1.0d / accrualFactorTotal); return(combinedPointSensitivity); }
public virtual void test_presentValueSensitivitySabrParameter_afterPay() { PointSensitivityBuilder computed = LEG_PRICER.presentValueSensitivityModelParamsSabr(FLOOR_LEG, RATES_PROVIDER_AFTER_PERIOD, VOLATILITIES_AFTER_PERIOD); PointSensitivityBuilder expected = PointSensitivityBuilder.none(); IList <CmsPeriod> cms = FLOOR_LEG.CmsPeriods; int size = cms.Count; for (int i = 0; i < size; ++i) { expected = expected.combinedWith(PERIOD_PRICER.presentValueSensitivityModelParamsSabr(cms[i], RATES_PROVIDER_AFTER_PERIOD, VOLATILITIES_AFTER_PERIOD)); } assertEquals(computed, expected); }
// calculates the present value curve sensitivity of the periods composing the leg in the currency of the swap leg internal virtual PointSensitivityBuilder presentValueSensitivityPeriodsInternal(ResolvedSwapLeg leg, RatesProvider provider) { PointSensitivityBuilder builder = PointSensitivityBuilder.none(); foreach (SwapPaymentPeriod period in leg.PaymentPeriods) { if (!period.PaymentDate.isBefore(provider.ValuationDate)) { builder = builder.combinedWith(paymentPeriodPricer.presentValueSensitivity(period, provider)); } } return(builder); }
// Accumulated rate sensitivity - cutoff part if not fixed internal PointSensitivityBuilder cutOffAccumulationSensitivity() { PointSensitivityBuilder combinedPointSensitivityBuilder = PointSensitivityBuilder.none(); int nbPeriodNotCutOff = nbPeriods - cutoffOffset + 1; for (int i = Math.Max(fixedPeriod, nbPeriodNotCutOff); i < nbPeriods; i++) { OvernightIndexObservation obs = observations[i]; PointSensitivityBuilder forwardRateSensitivity = rates.ratePointSensitivity(obs).multipliedBy(obs.YearFraction); combinedPointSensitivityBuilder = combinedPointSensitivityBuilder.combinedWith(forwardRateSensitivity); } return(combinedPointSensitivityBuilder); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the swaption product. /// <para> /// The present value sensitivity of the product is the sensitivity of the present value to /// the underlying curves. /// /// </para> /// </summary> /// <param name="swaption"> the product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="hwProvider"> the Hull-White model parameter provider </param> /// <returns> the point sensitivity to the rate curves </returns> public virtual PointSensitivityBuilder presentValueSensitivityRates(ResolvedSwaption swaption, RatesProvider ratesProvider, HullWhiteOneFactorPiecewiseConstantParametersProvider hwProvider) { validate(swaption, ratesProvider, hwProvider); ResolvedSwap swap = swaption.Underlying; LocalDate expiryDate = swaption.ExpiryDate; if (expiryDate.isBefore(ratesProvider.ValuationDate)) { // Option has expired already return(PointSensitivityBuilder.none()); } ImmutableMap <Payment, PointSensitivityBuilder> cashFlowEquivSensi = CashFlowEquivalentCalculator.cashFlowEquivalentAndSensitivitySwap(swap, ratesProvider); ImmutableList <Payment> list = cashFlowEquivSensi.Keys.asList(); ImmutableList <PointSensitivityBuilder> listSensi = cashFlowEquivSensi.values().asList(); int nPayments = list.size(); double[] alpha = new double[nPayments]; double[] discountedCashFlow = new double[nPayments]; for (int loopcf = 0; loopcf < nPayments; loopcf++) { Payment payment = list.get(loopcf); alpha[loopcf] = hwProvider.alpha(ratesProvider.ValuationDate, expiryDate, expiryDate, payment.Date); discountedCashFlow[loopcf] = paymentPricer.presentValueAmount(payment, ratesProvider); } double omega = (swap.getLegs(SwapLegType.FIXED).get(0).PayReceive.Pay ? -1d : 1d); double kappa = computeKappa(hwProvider, discountedCashFlow, alpha, omega); PointSensitivityBuilder point = PointSensitivityBuilder.none(); for (int loopcf = 0; loopcf < nPayments; loopcf++) { Payment payment = list.get(loopcf); double cdf = NORMAL.getCDF(omega * (kappa + alpha[loopcf])); point = point.combinedWith(paymentPricer.presentValueSensitivity(payment, ratesProvider).multipliedBy(cdf)); if (!listSensi.get(loopcf).Equals(PointSensitivityBuilder.none())) { point = point.combinedWith(listSensi.get(loopcf).multipliedBy(cdf * ratesProvider.discountFactor(payment.Currency, payment.Date))); } } return(swaption.LongShort.Long ? point : point.multipliedBy(-1d)); }
internal virtual PointSensitivityBuilder riskyAnnuitySensitivity(ResolvedCds cds, CreditDiscountFactors discountFactors, LegalEntitySurvivalProbabilities survivalProbabilities, LocalDate referenceDate, LocalDate stepinDate, LocalDate effectiveStartDate) { double pv = 0d; PointSensitivityBuilder pvSensi = PointSensitivityBuilder.none(); foreach (CreditCouponPaymentPeriod coupon in cds.PaymentPeriods) { if (stepinDate.isBefore(coupon.EndDate)) { double q = survivalProbabilities.survivalProbability(coupon.EffectiveEndDate); PointSensitivityBuilder qSensi = survivalProbabilities.zeroRatePointSensitivity(coupon.EffectiveEndDate); double p = discountFactors.discountFactor(coupon.PaymentDate); PointSensitivityBuilder pSensi = discountFactors.zeroRatePointSensitivity(coupon.PaymentDate); pv += coupon.YearFraction * p * q; pvSensi = pvSensi.combinedWith(pSensi.multipliedBy(coupon.YearFraction * q).combinedWith(qSensi.multipliedBy(coupon.YearFraction * p))); } } if (cds.PaymentOnDefault.AccruedInterest) { // This is needed so that the code is consistent with ISDA C when the Markit `fix' is used. LocalDate start = cds.PaymentPeriods.size() == 1 ? effectiveStartDate : cds.AccrualStartDate; DoubleArray integrationSchedule = DoublesScheduleGenerator.getIntegrationsPoints(discountFactors.relativeYearFraction(start), discountFactors.relativeYearFraction(cds.ProtectionEndDate), discountFactors.ParameterKeys, survivalProbabilities.ParameterKeys); foreach (CreditCouponPaymentPeriod coupon in cds.PaymentPeriods) { Pair <double, PointSensitivityBuilder> pvAndSensi = singlePeriodAccrualOnDefaultSensitivity(coupon, effectiveStartDate, integrationSchedule, discountFactors, survivalProbabilities); pv += pvAndSensi.First; pvSensi = pvSensi.combinedWith(pvAndSensi.Second); } } double df = discountFactors.discountFactor(referenceDate); PointSensitivityBuilder dfSensi = discountFactors.zeroRatePointSensitivity(referenceDate).multipliedBy(-pv / (df * df)); pvSensi = pvSensi.multipliedBy(1d / df); return(dfSensi.combinedWith(pvSensi)); }
/// <summary> /// Calculates the par spread sensitivity of the product. /// <para> /// The par spread sensitivity of the product is the sensitivity of par spread to the underlying curves. /// The resulting sensitivity is based on the currency of the CDS product. /// /// </para> /// </summary> /// <param name="cds"> the product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="referenceDate"> the reference date </param> /// <param name="refData"> the reference data </param> /// <returns> the par spread </returns> public virtual PointSensitivityBuilder parSpreadSensitivity(ResolvedCds cds, CreditRatesProvider ratesProvider, LocalDate referenceDate, ReferenceData refData) { ArgChecker.isTrue(cds.ProtectionEndDate.isAfter(ratesProvider.ValuationDate), "CDS already expired"); LocalDate stepinDate = cds.StepinDateOffset.adjust(ratesProvider.ValuationDate, refData); LocalDate effectiveStartDate = cds.calculateEffectiveStartDate(stepinDate); double recoveryRate = this.recoveryRate(cds, ratesProvider); Pair <CreditDiscountFactors, LegalEntitySurvivalProbabilities> rates = reduceDiscountFactors(cds, ratesProvider); double protectionLeg = this.protectionLeg(cds, rates.First, rates.Second, referenceDate, effectiveStartDate, recoveryRate); double riskyAnnuityInv = 1d / riskyAnnuity(cds, rates.First, rates.Second, referenceDate, stepinDate, effectiveStartDate, PriceType.CLEAN); PointSensitivityBuilder protectionLegSensi = protectionLegSensitivity(cds, rates.First, rates.Second, referenceDate, effectiveStartDate, recoveryRate).multipliedBy(riskyAnnuityInv); PointSensitivityBuilder riskyAnnuitySensi = riskyAnnuitySensitivity(cds, rates.First, rates.Second, referenceDate, stepinDate, effectiveStartDate).multipliedBy(-protectionLeg * riskyAnnuityInv * riskyAnnuityInv); return(protectionLegSensi.combinedWith(riskyAnnuitySensi)); }
/// <summary> /// Calculates the present value sensitivity of the foreign exchange vanilla option product. /// <para> /// The present value sensitivity of the product is the sensitivity of <seealso cref="#presentValue"/> to /// the underlying curves. /// </para> /// <para> /// The volatility is fixed in this sensitivity computation. /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the present value curve sensitivity of the product </returns> public virtual PointSensitivities presentValueSensitivityRatesStickyStrike(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { if (volatilities.relativeTime(option.Expiry) < 0d) { return(PointSensitivities.empty()); } ResolvedFxSingle underlying = option.Underlying; double fwdDelta = undiscountedDelta(option, ratesProvider, volatilities); double discountFactor = ratesProvider.discountFactor(option.CounterCurrency, underlying.PaymentDate); double notional = signedNotional(option); PointSensitivityBuilder fwdSensi = fxPricer.forwardFxRatePointSensitivity(option.PutCall.Call ? underlying : underlying.inverse(), ratesProvider).multipliedBy(notional * discountFactor * fwdDelta); double fwdPrice = undiscountedPrice(option, ratesProvider, volatilities); PointSensitivityBuilder dscSensi = ratesProvider.discountFactors(option.CounterCurrency).zeroRatePointSensitivity(underlying.PaymentDate).multipliedBy(notional * fwdPrice); return(fwdSensi.combinedWith(dscSensi).build().convertedTo(option.CounterCurrency, ratesProvider)); }
/// <summary> /// Calculates the price sensitivity of the product. /// <para> /// The price sensitivity of the product is the sensitivity of price to the underlying curves. /// /// </para> /// </summary> /// <param name="cds"> the product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="referenceDate"> the reference date </param> /// <param name="refData"> the reference data </param> /// <returns> the present value sensitivity </returns> public virtual PointSensitivityBuilder priceSensitivity(ResolvedCds cds, CreditRatesProvider ratesProvider, LocalDate referenceDate, ReferenceData refData) { if (isExpired(cds, ratesProvider)) { return(PointSensitivityBuilder.none()); } LocalDate stepinDate = cds.StepinDateOffset.adjust(ratesProvider.ValuationDate, refData); LocalDate effectiveStartDate = cds.calculateEffectiveStartDate(stepinDate); double recoveryRate = this.recoveryRate(cds, ratesProvider); Pair <CreditDiscountFactors, LegalEntitySurvivalProbabilities> rates = reduceDiscountFactors(cds, ratesProvider); PointSensitivityBuilder protectionLegSensi = protectionLegSensitivity(cds, rates.First, rates.Second, referenceDate, effectiveStartDate, recoveryRate); PointSensitivityBuilder riskyAnnuitySensi = riskyAnnuitySensitivity(cds, rates.First, rates.Second, referenceDate, stepinDate, effectiveStartDate).multipliedBy(-cds.FixedRate); return(protectionLegSensi.combinedWith(riskyAnnuitySensi)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the foreign exchange vanilla option product. /// <para> /// The present value sensitivity of the product is the sensitivity of <seealso cref="#presentValue"/> to /// the underlying curves. /// </para> /// <para> /// The implied strikes and weights are fixed in this sensitivity computation. /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the present value curve sensitivity of the product </returns> public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyStrike(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities) { validate(ratesProvider, volatilities); double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry <= 0d) { return(PointSensitivityBuilder.none()); } ResolvedFxSingle underlyingFx = option.Underlying; Currency ccyCounter = option.CounterCurrency; double df = ratesProvider.discountFactor(ccyCounter, underlyingFx.PaymentDate); FxRate forward = fxPricer.forwardFxRate(underlyingFx, ratesProvider); CurrencyPair currencyPair = underlyingFx.CurrencyPair; double forwardRate = forward.fxRate(currencyPair); double strikeRate = option.Strike; bool isCall = option.PutCall.Call; SmileDeltaParameters smileAtTime = volatilities.Smile.smileForExpiry(timeToExpiry); double[] strikes = smileAtTime.strike(forwardRate).toArray(); double[] vols = smileAtTime.Volatility.toArray(); double volAtm = vols[1]; double[] x = vannaVolgaWeights(forwardRate, strikeRate, timeToExpiry, volAtm, strikes); double priceFwd = BlackFormulaRepository.price(forwardRate, strikeRate, timeToExpiry, volAtm, isCall); double deltaFwd = BlackFormulaRepository.delta(forwardRate, strikeRate, timeToExpiry, volAtm, isCall); for (int i = 0; i < 3; i += 2) { double priceFwdAtm = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, volAtm, isCall); double priceFwdSmile = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, vols[i], isCall); priceFwd += x[i] * (priceFwdSmile - priceFwdAtm); double deltaFwdAtm = BlackFormulaRepository.delta(forwardRate, strikes[i], timeToExpiry, volAtm, isCall); double deltaFwdSmile = BlackFormulaRepository.delta(forwardRate, strikes[i], timeToExpiry, vols[i], isCall); deltaFwd += x[i] * (deltaFwdSmile - deltaFwdAtm); } double signedNotional = this.signedNotional(option); PointSensitivityBuilder dfSensi = ratesProvider.discountFactors(ccyCounter).zeroRatePointSensitivity(underlyingFx.PaymentDate).multipliedBy(priceFwd * signedNotional); PointSensitivityBuilder fwdSensi = fxPricer.forwardFxRatePointSensitivity(option.PutCall.Call ? underlyingFx : underlyingFx.inverse(), ratesProvider).multipliedBy(df * deltaFwd * signedNotional); return(dfSensi.combinedWith(fwdSensi)); }
/// <summary> /// Calculates the present value sensitivity of the product. /// <para> /// The present value sensitivity of the product is the sensitivity of present value to the underlying curves. /// /// </para> /// </summary> /// <param name="cdsIndex"> the product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="referenceDate"> the reference date </param> /// <param name="refData"> the reference data </param> /// <returns> the present value sensitivity </returns> public virtual PointSensitivityBuilder presentValueSensitivity(ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, ReferenceData refData) { if (isExpired(cdsIndex, ratesProvider)) { return(PointSensitivityBuilder.none()); } ResolvedCds cds = cdsIndex.toSingleNameCds(); LocalDate stepinDate = cds.StepinDateOffset.adjust(ratesProvider.ValuationDate, refData); LocalDate effectiveStartDate = cds.calculateEffectiveStartDate(stepinDate); double recoveryRate = underlyingPricer.recoveryRate(cds, ratesProvider); Triple <CreditDiscountFactors, LegalEntitySurvivalProbabilities, double> rates = reduceDiscountFactors(cds, ratesProvider); double signedNotional = cds.BuySell.normalize(cds.Notional); PointSensitivityBuilder protectionLegSensi = underlyingPricer.protectionLegSensitivity(cds, rates.First, rates.Second, referenceDate, effectiveStartDate, recoveryRate); protectionLegSensi = protectionLegSensi.multipliedBy(signedNotional * rates.Third); PointSensitivityBuilder riskyAnnuitySensi = underlyingPricer.riskyAnnuitySensitivity(cds, rates.First, rates.Second, referenceDate, stepinDate, effectiveStartDate); riskyAnnuitySensi = riskyAnnuitySensi.multipliedBy(-cds.FixedRate * signedNotional * rates.Third); return(protectionLegSensi.combinedWith(riskyAnnuitySensi)); }
public virtual void pointAndParameterFx() { ImmutableRatesProvider test = ImmutableRatesProvider.builder(VAL_DATE).fxRateProvider(FX_MATRIX).discountCurve(GBP, DISCOUNT_CURVE_GBP).discountCurve(USD, DISCOUNT_CURVE_USD).build(); ImmutableRatesProvider test_gbp_up = ImmutableRatesProvider.builder(VAL_DATE).fxRateProvider(FX_MATRIX).discountCurve(GBP, DISCOUNT_CURVE_GBP_UP).discountCurve(USD, DISCOUNT_CURVE_USD).build(); ImmutableRatesProvider test_gbp_dw = ImmutableRatesProvider.builder(VAL_DATE).fxRateProvider(FX_MATRIX).discountCurve(GBP, DISCOUNT_CURVE_GBP_DOWN).discountCurve(USD, DISCOUNT_CURVE_USD).build(); ImmutableRatesProvider test_usd_up = ImmutableRatesProvider.builder(VAL_DATE).fxRateProvider(FX_MATRIX).discountCurve(GBP, DISCOUNT_CURVE_GBP).discountCurve(USD, DISCOUNT_CURVE_USD_UP).build(); ImmutableRatesProvider test_usd_dw = ImmutableRatesProvider.builder(VAL_DATE).fxRateProvider(FX_MATRIX).discountCurve(GBP, DISCOUNT_CURVE_GBP).discountCurve(USD, DISCOUNT_CURVE_USD_DOWN).build(); LocalDate matuirtyDate = GBP_USD_WM.calculateMaturityFromFixing(VAL_DATE, REF_DATA); double maturityTime = DAY_COUNT.relativeYearFraction(VAL_DATE, matuirtyDate); // GBP based FxIndexObservation obs = FxIndexObservation.of(GBP_USD_WM, VAL_DATE, REF_DATA); PointSensitivityBuilder sensiBuildCmpGBP = test.fxIndexRates(GBP_USD_WM).ratePointSensitivity(obs, GBP); FxIndexSensitivity sensiBuildExpGBP = FxIndexSensitivity.of(obs, GBP, USD, 1.0); assertTrue(sensiBuildCmpGBP.Equals(sensiBuildExpGBP)); double sense_gbp1 = 0.5 * (test_gbp_up.fxIndexRates(GBP_USD_WM).rate(obs, GBP) - test_gbp_dw.fxIndexRates(GBP_USD_WM).rate(obs, GBP)) / EPS_FD * (-maturityTime * GBP_DSC); double sense_usd1 = 0.5 * (test_usd_up.fxIndexRates(GBP_USD_WM).rate(obs, GBP) - test_usd_dw.fxIndexRates(GBP_USD_WM).rate(obs, GBP)) / EPS_FD * (-maturityTime * USD_DSC); PointSensitivityBuilder sensiBuildDecGBP = ZeroRateSensitivity.of(GBP, maturityTime, USD, sense_gbp1); sensiBuildDecGBP = sensiBuildDecGBP.combinedWith(ZeroRateSensitivity.of(USD, maturityTime, USD, sense_usd1)); CurrencyParameterSensitivities paramSensiCmpGBP = test.parameterSensitivity(sensiBuildCmpGBP.build().normalized()); CurrencyParameterSensitivities paramSensiExpGBP = test.parameterSensitivity(sensiBuildDecGBP.build().normalized()); assertTrue(paramSensiCmpGBP.equalWithTolerance(paramSensiExpGBP, EPS_FD)); // USD based PointSensitivityBuilder sensiBuildCmpUSD = test.fxIndexRates(GBP_USD_WM).ratePointSensitivity(obs, USD); FxIndexSensitivity sensiBuildExpUSD = FxIndexSensitivity.of(obs, USD, GBP, 1.0); assertTrue(sensiBuildCmpUSD.Equals(sensiBuildExpUSD)); double sense_gbp2 = 0.5 * (test_gbp_up.fxIndexRates(GBP_USD_WM).rate(obs, USD) - test_gbp_dw.fxIndexRates(GBP_USD_WM).rate(obs, USD)) / EPS_FD * (-maturityTime * GBP_DSC); double sense_usd2 = 0.5 * (test_usd_up.fxIndexRates(GBP_USD_WM).rate(obs, USD) - test_usd_dw.fxIndexRates(GBP_USD_WM).rate(obs, USD)) / EPS_FD * (-maturityTime * USD_DSC); PointSensitivityBuilder sensiBuildDecUSD = ZeroRateSensitivity.of(GBP, maturityTime, GBP, sense_gbp2); sensiBuildDecUSD = sensiBuildDecUSD.combinedWith(ZeroRateSensitivity.of(USD, maturityTime, GBP, sense_usd2)); CurrencyParameterSensitivities paramSensiCmpUSD = test.parameterSensitivity(sensiBuildCmpUSD.build().normalized()); CurrencyParameterSensitivities paramSensiExpUSD = test.parameterSensitivity(sensiBuildDecUSD.build().normalized()); assertTrue(paramSensiCmpUSD.equalWithTolerance(paramSensiExpUSD, EPS_FD)); }
/// <summary> /// Computes the present value sensitivity to the black volatilities used in the pricing. /// <para> /// The implied strikes and weights are fixed in this sensitivity computation. /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the present value sensitivity </returns> public virtual PointSensitivityBuilder presentValueSensitivityModelParamsVolatility(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities) { validate(ratesProvider, volatilities); double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry <= 0d) { return(PointSensitivityBuilder.none()); } ResolvedFxSingle underlyingFx = option.Underlying; Currency ccyCounter = option.CounterCurrency; double df = ratesProvider.discountFactor(ccyCounter, underlyingFx.PaymentDate); FxRate forward = fxPricer.forwardFxRate(underlyingFx, ratesProvider); CurrencyPair currencyPair = underlyingFx.CurrencyPair; double forwardRate = forward.fxRate(currencyPair); double strikeRate = option.Strike; SmileDeltaParameters smileAtTime = volatilities.Smile.smileForExpiry(timeToExpiry); double[] strikes = smileAtTime.strike(forwardRate).toArray(); double[] vols = smileAtTime.Volatility.toArray(); double volAtm = vols[1]; double[] x = vannaVolgaWeights(forwardRate, strikeRate, timeToExpiry, volAtm, strikes); double vegaAtm = BlackFormulaRepository.vega(forwardRate, strikeRate, timeToExpiry, volAtm); double signedNotional = this.signedNotional(option); PointSensitivityBuilder sensiSmile = PointSensitivityBuilder.none(); for (int i = 0; i < 3; i += 2) { double vegaFwdAtm = BlackFormulaRepository.vega(forwardRate, strikes[i], timeToExpiry, volAtm); vegaAtm -= x[i] * vegaFwdAtm; double vegaFwdSmile = BlackFormulaRepository.vega(forwardRate, strikes[i], timeToExpiry, vols[i]); sensiSmile = sensiSmile.combinedWith(FxOptionSensitivity.of(volatilities.Name, currencyPair, timeToExpiry, strikes[i], forwardRate, ccyCounter, df * signedNotional * x[i] * vegaFwdSmile)); } FxOptionSensitivity sensiAtm = FxOptionSensitivity.of(volatilities.Name, currencyPair, timeToExpiry, strikes[1], forwardRate, ccyCounter, df * signedNotional * vegaAtm); return(sensiAtm.combinedWith(sensiSmile)); }
/// <summary> /// Calculates the par rate curve sensitivity for a swap with a fixed leg. /// <para> /// The par rate is the common rate on all payments of the fixed leg for which the total swap present value is 0. /// </para> /// <para> /// At least one leg must be a fixed leg. The par rate will be computed with respect to the first fixed leg. /// All the payments in that leg should be fixed payments with a unique accrual period (no compounding) and no FX reset. /// /// </para> /// </summary> /// <param name="swap"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the par rate curve sensitivity of the swap product </returns> public virtual PointSensitivityBuilder parRateSensitivity(ResolvedSwap swap, RatesProvider provider) { ResolvedSwapLeg fixedLeg = this.fixedLeg(swap); Currency ccyFixedLeg = fixedLeg.Currency; // other payments (not fixed leg coupons) converted in fixed leg currency double otherLegsConvertedPv = 0.0; foreach (ResolvedSwapLeg leg in swap.Legs) { if (leg != fixedLeg) { double pvLocal = legPricer.presentValueInternal(leg, provider); otherLegsConvertedPv += (pvLocal * provider.fxRate(leg.Currency, ccyFixedLeg)); } } double fixedLegEventsPv = legPricer.presentValueEventsInternal(fixedLeg, provider); double pvbpFixedLeg = legPricer.pvbp(fixedLeg, provider); // Backward sweep double otherLegsConvertedPvBar = -1.0d / pvbpFixedLeg; double fixedLegEventsPvBar = -1.0d / pvbpFixedLeg; double pvbpFixedLegBar = (otherLegsConvertedPv + fixedLegEventsPv) / (pvbpFixedLeg * pvbpFixedLeg); PointSensitivityBuilder pvbpFixedLegDr = legPricer.pvbpSensitivity(fixedLeg, provider); PointSensitivityBuilder fixedLegEventsPvDr = legPricer.presentValueSensitivityEventsInternal(fixedLeg, provider); PointSensitivityBuilder otherLegsConvertedPvDr = PointSensitivityBuilder.none(); foreach (ResolvedSwapLeg leg in swap.Legs) { if (leg != fixedLeg) { PointSensitivityBuilder pvLegDr = legPricer.presentValueSensitivity(leg, provider).multipliedBy(provider.fxRate(leg.Currency, ccyFixedLeg)); otherLegsConvertedPvDr = otherLegsConvertedPvDr.combinedWith(pvLegDr); } } otherLegsConvertedPvDr = otherLegsConvertedPvDr.withCurrency(ccyFixedLeg); return(pvbpFixedLegDr.multipliedBy(pvbpFixedLegBar).combinedWith(fixedLegEventsPvDr.multipliedBy(fixedLegEventsPvBar)).combinedWith(otherLegsConvertedPvDr.multipliedBy(otherLegsConvertedPvBar))); }
/// <summary> /// Computes cash flow equivalent and sensitivity of Ibor leg. /// <para> /// The return type is a map of {@code NotionalExchange} and {@code PointSensitivityBuilder}. /// /// </para> /// </summary> /// <param name="iborLeg"> the Ibor leg </param> /// <param name="ratesProvider"> the rates provider </param> /// <returns> the cash flow equivalent and sensitivity </returns> public static ImmutableMap <Payment, PointSensitivityBuilder> cashFlowEquivalentAndSensitivityIborLeg(ResolvedSwapLeg iborLeg, RatesProvider ratesProvider) { ArgChecker.isTrue(iborLeg.Type.Equals(SwapLegType.IBOR), "Leg type should be IBOR"); ArgChecker.isTrue(iborLeg.PaymentEvents.Empty, "PaymentEvent should be empty"); IDictionary <Payment, PointSensitivityBuilder> res = new Dictionary <Payment, PointSensitivityBuilder>(); foreach (SwapPaymentPeriod paymentPeriod in iborLeg.PaymentPeriods) { ArgChecker.isTrue(paymentPeriod is RatePaymentPeriod, "rate payment should be RatePaymentPeriod"); RatePaymentPeriod ratePaymentPeriod = (RatePaymentPeriod)paymentPeriod; ArgChecker.isTrue(ratePaymentPeriod.AccrualPeriods.size() == 1, "rate payment should not be compounding"); RateAccrualPeriod rateAccrualPeriod = ratePaymentPeriod.AccrualPeriods.get(0); CurrencyAmount notional = ratePaymentPeriod.NotionalAmount; LocalDate paymentDate = ratePaymentPeriod.PaymentDate; IborIndexObservation obs = ((IborRateComputation)rateAccrualPeriod.RateComputation).Observation; IborIndex index = obs.Index; LocalDate fixingStartDate = obs.EffectiveDate; double fixingYearFraction = obs.YearFraction; double factorIndex = (1d + fixingYearFraction * ratesProvider.iborIndexRates(index).rate(obs)); double dfPayment = ratesProvider.discountFactor(paymentPeriod.Currency, paymentPeriod.PaymentDate); double dfStart = ratesProvider.discountFactor(paymentPeriod.Currency, fixingStartDate); double beta = factorIndex * dfPayment / dfStart; double ycRatio = rateAccrualPeriod.YearFraction / fixingYearFraction; Payment payStart = Payment.of(notional.multipliedBy(beta * ycRatio), fixingStartDate); Payment payEnd = Payment.of(notional.multipliedBy(-ycRatio), paymentDate); double factor = ycRatio * notional.Amount / dfStart; PointSensitivityBuilder factorIndexSensi = ratesProvider.iborIndexRates(index).ratePointSensitivity(obs).multipliedBy(fixingYearFraction * dfPayment * factor); PointSensitivityBuilder dfPaymentSensitivity = ratesProvider.discountFactors(paymentPeriod.Currency).zeroRatePointSensitivity(paymentPeriod.PaymentDate).multipliedBy(factorIndex * factor); PointSensitivityBuilder dfStartSensitivity = ratesProvider.discountFactors(paymentPeriod.Currency).zeroRatePointSensitivity(fixingStartDate).multipliedBy(-factorIndex * dfPayment * factor / dfStart); res[payStart] = factorIndexSensi.combinedWith(dfPaymentSensitivity).combinedWith(dfStartSensitivity); res[payEnd] = PointSensitivityBuilder.none(); } return(ImmutableMap.copyOf(res)); }
private Pair <double, PointSensitivityBuilder> singlePeriodAccrualOnDefaultSensitivity(CreditCouponPaymentPeriod coupon, LocalDate effectiveStartDate, DoubleArray integrationSchedule, CreditDiscountFactors discountFactors, LegalEntitySurvivalProbabilities survivalProbabilities) { LocalDate start = coupon.EffectiveStartDate.isBefore(effectiveStartDate) ? effectiveStartDate : coupon.EffectiveStartDate; if (!start.isBefore(coupon.EffectiveEndDate)) { return(Pair.of(0d, PointSensitivityBuilder.none())); //this coupon has already expired } DoubleArray knots = DoublesScheduleGenerator.truncateSetInclusive(discountFactors.relativeYearFraction(start), discountFactors.relativeYearFraction(coupon.EffectiveEndDate), integrationSchedule); // pv double pv = 0d; //JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final': //ORIGINAL LINE: final int nItems = knots.size(); int nItems = knots.size(); double[] dhrtBar = new double[nItems - 1]; double[] dhtBar = new double[nItems - 1]; double[] bBar = new double[nItems]; double[] p = new double[nItems]; double[] q = new double[nItems]; double t = knots.get(0); double ht0 = survivalProbabilities.zeroRate(t) * t; double rt0 = discountFactors.zeroRate(t) * t; q[0] = Math.Exp(-ht0); p[0] = Math.Exp(-rt0); double b0 = q[0] * p[0]; double effStart = discountFactors.relativeYearFraction(coupon.EffectiveStartDate); double t0 = t - effStart + omega; for (int i = 1; i < nItems; ++i) { t = knots.get(i); double ht1 = survivalProbabilities.zeroRate(t) * t; double rt1 = discountFactors.zeroRate(t) * t; q[i] = Math.Exp(-ht1); p[i] = Math.Exp(-rt1); double b1 = q[i] * p[i]; double dt = knots.get(i) - knots.get(i - 1); double dht = ht1 - ht0; double drt = rt1 - rt0; double dhrt = dht + drt; double tPv; if (formula == AccrualOnDefaultFormula.MARKIT_FIX) { if (Math.Abs(dhrt) < SMALL) { double eps = epsilonP(-dhrt); tPv = dht * dt * b0 * eps; dhtBar[i - 1] = dt * b0 * eps; dhrtBar[i - 1] = -dht *dt *b0 *epsilonPP(-dhrt); bBar[i - 1] += dht * eps; } else { tPv = dht * dt / dhrt * ((b0 - b1) / dhrt - b1); dhtBar[i - 1] = dt / dhrt * ((b0 - b1) / dhrt - b1); dhrtBar[i - 1] = dht * dt / (dhrt * dhrt) * (b1 - 2d * (b0 - b1) / dhrt); bBar[i - 1] += dht * dt / (dhrt * dhrt); bBar[i] += -dht * dt / dhrt * (1d + 1d / dhrt); } } else { double t1 = t - effStart + omega; if (Math.Abs(dhrt) < SMALL) { double eps = epsilon(-dhrt); double epsp = epsilonP(-dhrt); tPv = dht * b0 * (t0 * eps + dt * epsp); dhtBar[i - 1] = b0 * (t0 * eps + dt * epsp); dhrtBar[i - 1] = -dht * b0 * (t0 * epsp + dt * epsilonPP(-dhrt)); bBar[i - 1] += dht * (t0 * eps + dt * epsp); } else { tPv = dht / dhrt * (t0 * b0 - t1 * b1 + dt / dhrt * (b0 - b1)); dhtBar[i - 1] = (t0 * b0 - t1 * b1 + dt / dhrt * (b0 - b1)) / dhrt; dhrtBar[i - 1] = dht / (dhrt * dhrt) * (-2d * dt / dhrt * (b0 - b1) - t0 * b0 + t1 * b1); bBar[i - 1] += dht / dhrt * (t0 + dt / dhrt); bBar[i] += dht / dhrt * (-t1 - dt / dhrt); } t0 = t1; } pv += tPv; ht0 = ht1; rt0 = rt1; b0 = b1; } double yfRatio = coupon.YearFraction / discountFactors.DayCount.relativeYearFraction(coupon.StartDate, coupon.EndDate); // pv sensitivity PointSensitivityBuilder qSensiFirst = survivalProbabilities.zeroRatePointSensitivity(knots.get(0)).multipliedBy(yfRatio * ((dhrtBar[0] + dhtBar[0]) / q[0] + bBar[0] * p[0])); PointSensitivityBuilder pSensiFirst = discountFactors.zeroRatePointSensitivity(knots.get(0)).multipliedBy(yfRatio * (dhrtBar[0] / p[0] + bBar[0] * q[0])); PointSensitivityBuilder pvSensi = pSensiFirst.combinedWith(qSensiFirst); for (int i = 1; i < nItems - 1; ++i) { PointSensitivityBuilder qSensi = survivalProbabilities.zeroRatePointSensitivity(knots.get(i)).multipliedBy(yfRatio * (-(dhrtBar[i - 1] + dhtBar[i - 1]) / q[i] + (dhrtBar[i] + dhtBar[i]) / q[i] + bBar[i] * p[i])); PointSensitivityBuilder pSensi = discountFactors.zeroRatePointSensitivity(knots.get(i)).multipliedBy(yfRatio * (-dhrtBar[i - 1] / p[i] + dhrtBar[i] / p[i] + bBar[i] * q[i])); pvSensi = pvSensi.combinedWith(pSensi).combinedWith(qSensi); } if (nItems > 1) { PointSensitivityBuilder qSensiLast = survivalProbabilities.zeroRatePointSensitivity(knots.get(nItems - 1)).multipliedBy(yfRatio * (-(dhrtBar[nItems - 2] + dhtBar[nItems - 2]) / q[nItems - 1] + bBar[nItems - 1] * p[nItems - 1])); PointSensitivityBuilder pSensiLast = discountFactors.zeroRatePointSensitivity(knots.get(nItems - 1)).multipliedBy(yfRatio * (-dhrtBar[nItems - 2] / p[nItems - 1] + bBar[nItems - 1] * q[nItems - 1])); pvSensi = pvSensi.combinedWith(pSensiLast).combinedWith(qSensiLast); } return(Pair.of(yfRatio * pv, pvSensi)); }
//------------------------------------------------------------------------- /// <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)); }
//------------------------------------------------------------------------- internal virtual PointSensitivityBuilder protectionLegSensitivity(ResolvedCds cds, CreditDiscountFactors discountFactors, LegalEntitySurvivalProbabilities survivalProbabilities, LocalDate referenceDate, LocalDate effectiveStartDate, double recoveryRate) { DoubleArray integrationSchedule = DoublesScheduleGenerator.getIntegrationsPoints(discountFactors.relativeYearFraction(effectiveStartDate), discountFactors.relativeYearFraction(cds.ProtectionEndDate), discountFactors.ParameterKeys, survivalProbabilities.ParameterKeys); int n = integrationSchedule.size(); double[] dht = new double[n - 1]; double[] drt = new double[n - 1]; double[] dhrt = new double[n - 1]; double[] p = new double[n]; double[] q = new double[n]; // pv double pv = 0d; double ht0 = survivalProbabilities.zeroRate(integrationSchedule.get(0)) * integrationSchedule.get(0); double rt0 = discountFactors.zeroRate(integrationSchedule.get(0)) * integrationSchedule.get(0); p[0] = Math.Exp(-rt0); q[0] = Math.Exp(-ht0); double b0 = p[0] * q[0]; for (int i = 1; i < n; ++i) { double ht1 = survivalProbabilities.zeroRate(integrationSchedule.get(i)) * integrationSchedule.get(i); double rt1 = discountFactors.zeroRate(integrationSchedule.get(i)) * integrationSchedule.get(i); p[i] = Math.Exp(-rt1); q[i] = Math.Exp(-ht1); double b1 = p[i] * q[i]; dht[i - 1] = ht1 - ht0; drt[i - 1] = rt1 - rt0; dhrt[i - 1] = dht[i - 1] + drt[i - 1]; double dPv = 0d; if (Math.Abs(dhrt[i - 1]) < SMALL) { double eps = epsilon(-dhrt[i - 1]); dPv = dht[i - 1] * b0 * eps; } else { dPv = (b0 - b1) * dht[i - 1] / dhrt[i - 1]; } pv += dPv; ht0 = ht1; rt0 = rt1; b0 = b1; } double df = discountFactors.discountFactor(referenceDate); // pv sensitivity double factor = (1d - recoveryRate) / df; double eps0 = computeExtendedEpsilon(-dhrt[0], p[1], q[1], p[0], q[0]); PointSensitivityBuilder pvSensi = discountFactors.zeroRatePointSensitivity(integrationSchedule.get(0)).multipliedBy(-dht[0] * q[0] * eps0 * factor); pvSensi = pvSensi.combinedWith(survivalProbabilities.zeroRatePointSensitivity(integrationSchedule.get(0)).multipliedBy(factor * (drt[0] * p[0] * eps0 + p[0]))); for (int i = 1; i < n - 1; ++i) { double epsp = computeExtendedEpsilon(-dhrt[i], p[i + 1], q[i + 1], p[i], q[i]); double epsm = computeExtendedEpsilon(dhrt[i - 1], p[i - 1], q[i - 1], p[i], q[i]); PointSensitivityBuilder pSensi = discountFactors.zeroRatePointSensitivity(integrationSchedule.get(i)).multipliedBy(factor * (-dht[i] * q[i] * epsp - dht[i - 1] * q[i] * epsm)); PointSensitivityBuilder qSensi = survivalProbabilities.zeroRatePointSensitivity(integrationSchedule.get(i)).multipliedBy(factor * (drt[i - 1] * p[i] * epsm + drt[i] * p[i] * epsp)); pvSensi = pvSensi.combinedWith(pSensi).combinedWith(qSensi); } if (n > 1) { double epsLast = computeExtendedEpsilon(dhrt[n - 2], p[n - 2], q[n - 2], p[n - 1], q[n - 1]); pvSensi = pvSensi.combinedWith(discountFactors.zeroRatePointSensitivity(integrationSchedule.get(n - 1)).multipliedBy(-dht[n - 2] * q[n - 1] * epsLast * factor)); pvSensi = pvSensi.combinedWith(survivalProbabilities.zeroRatePointSensitivity(integrationSchedule.get(n - 1)).multipliedBy(factor * (drt[n - 2] * p[n - 1] * epsLast - p[n - 1]))); } PointSensitivityBuilder dfSensi = discountFactors.zeroRatePointSensitivity(referenceDate).multipliedBy(-pv * factor / df); return(dfSensi.combinedWith(pvSensi)); }