//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the Ibor caplet/floorlet to the rate curves. /// <para> /// The present value sensitivity is computed in a "sticky model parameter" style, i.e. the sensitivity to the /// curve nodes with the SABR model parameters unchanged. This sensitivity does not include a potential /// re-calibration of the model parameters to the raw market data. /// /// </para> /// </summary> /// <param name="period"> the Ibor caplet/floorlet period </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the volatilities </param> /// <returns> the point sensitivity to the rate curves </returns> public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyModel(IborCapletFloorletPeriod period, RatesProvider ratesProvider, SabrIborCapletFloorletVolatilities volatilities) { Currency currency = period.Currency; if (ratesProvider.ValuationDate.isAfter(period.PaymentDate)) { return(PointSensitivityBuilder.none()); } double expiry = volatilities.relativeTime(period.FixingDateTime); PutCall putCall = period.PutCall; double strike = period.Strike; double indexRate = ratesProvider.iborIndexRates(period.Index).rate(period.IborRate.Observation); PointSensitivityBuilder dfSensi = ratesProvider.discountFactors(currency).zeroRatePointSensitivity(period.PaymentDate); double factor = period.Notional * period.YearFraction; if (expiry < 0d) { // option expired already, but not yet paid double sign = putCall.Call ? 1d : -1d; double payoff = Math.Max(sign * (indexRate - strike), 0d); return(dfSensi.multipliedBy(payoff * factor)); } ValueDerivatives volatilityAdj = volatilities.volatilityAdjoint(expiry, strike, indexRate); PointSensitivityBuilder indexRateSensiSensi = ratesProvider.iborIndexRates(period.Index).ratePointSensitivity(period.IborRate.Observation); double df = ratesProvider.discountFactor(currency, period.PaymentDate); double fwdPv = factor * volatilities.price(expiry, putCall, strike, indexRate, volatilityAdj.Value); double fwdDelta = factor * volatilities.priceDelta(expiry, putCall, strike, indexRate, volatilityAdj.Value); double fwdVega = factor * volatilities.priceVega(expiry, putCall, strike, indexRate, volatilityAdj.Value); return(dfSensi.multipliedBy(fwdPv).combinedWith(indexRateSensiSensi.multipliedBy(fwdDelta * df + fwdVega * volatilityAdj.getDerivative(0) * df))); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value rates sensitivity of the Ibor caplet/floorlet. /// <para> /// The present value rates sensitivity of the caplet/floorlet is the sensitivity /// of the present value to the underlying curves. /// /// </para> /// </summary> /// <param name="period"> the Ibor caplet/floorlet period </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the volatilities </param> /// <returns> the present value curve sensitivity </returns> public virtual PointSensitivityBuilder presentValueSensitivityRates(IborCapletFloorletPeriod period, RatesProvider ratesProvider, IborCapletFloorletVolatilities volatilities) { validate(volatilities); Currency currency = period.Currency; if (ratesProvider.ValuationDate.isAfter(period.PaymentDate)) { return(PointSensitivityBuilder.none()); } double expiry = volatilities.relativeTime(period.FixingDateTime); PutCall putCall = period.PutCall; double strike = period.Strike; double indexRate = ratesProvider.iborIndexRates(period.Index).rate(period.IborRate.Observation); PointSensitivityBuilder dfSensi = ratesProvider.discountFactors(currency).zeroRatePointSensitivity(period.PaymentDate); if (expiry < 0d) { // Option has expired already double sign = putCall.Call ? 1d : -1d; double payoff = Math.Max(sign * (indexRate - strike), 0d); return(dfSensi.multipliedBy(payoff * period.YearFraction * period.Notional)); } PointSensitivityBuilder indexRateSensiSensi = ratesProvider.iborIndexRates(period.Index).ratePointSensitivity(period.IborRate.Observation); double volatility = volatilities.volatility(expiry, strike, indexRate); double df = ratesProvider.discountFactor(currency, period.PaymentDate); double factor = period.Notional * period.YearFraction; double fwdPv = factor * volatilities.price(expiry, putCall, strike, indexRate, volatility); double fwdDelta = factor * volatilities.priceDelta(expiry, putCall, strike, indexRate, volatility); return(dfSensi.multipliedBy(fwdPv).combinedWith(indexRateSensiSensi.multipliedBy(fwdDelta * df))); }
/// <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))); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the swaption to the rate curves. /// <para> /// The present value sensitivity is computed in a "sticky strike" style, i.e. the sensitivity to the /// curve nodes with the volatility at the swaption strike unchanged. This sensitivity does not include a potential /// change of volatility due to the implicit change of forward rate or moneyness. /// /// </para> /// </summary> /// <param name="swaption"> the swaption </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the volatilities </param> /// <returns> the point sensitivity to the rate curves </returns> public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyStrike(ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.Expiry); ResolvedSwap underlying = swaption.Underlying; ResolvedSwapLeg fixedLeg = this.fixedLeg(underlying); if (expiry < 0d) { // Option has expired already return(PointSensitivityBuilder.none()); } double forward = SwapPricer.parRate(underlying, ratesProvider); double pvbp = SwapPricer.LegPricer.pvbp(fixedLeg, ratesProvider); double strike = SwapPricer.LegPricer.couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); PutCall putCall = PutCall.ofPut(fixedLeg.PayReceive.Receive); double price = swaptionVolatilities.price(expiry, tenor, putCall, strike, forward, volatility); double delta = swaptionVolatilities.priceDelta(expiry, tenor, putCall, strike, forward, volatility); // Backward sweep PointSensitivityBuilder pvbpDr = SwapPricer.LegPricer.pvbpSensitivity(fixedLeg, ratesProvider); PointSensitivityBuilder forwardDr = SwapPricer.parRateSensitivity(underlying, ratesProvider); double sign = swaption.LongShort.sign(); return(pvbpDr.multipliedBy(price * sign * Math.Sign(pvbp)).combinedWith(forwardDr.multipliedBy(delta * Math.Abs(pvbp) * sign))); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the swaption product to the rate curves. /// <para> /// The present value sensitivity is computed in a "sticky model parameter" style, i.e. the sensitivity to the /// curve nodes with the SABR model parameters unchanged. This sensitivity does not include a potential /// re-calibration of the model parameters to the raw market data. /// /// </para> /// </summary> /// <param name="swaption"> the swaption product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the volatilities </param> /// <returns> the point sensitivity to the rate curves </returns> public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyModel(ResolvedSwaption swaption, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); ZonedDateTime expiryDateTime = swaption.Expiry; double expiry = swaptionVolatilities.relativeTime(expiryDateTime); ResolvedSwap underlying = swaption.Underlying; ResolvedSwapLeg fixedLeg = this.fixedLeg(underlying); if (expiry < 0d) { // Option has expired already return(PointSensitivityBuilder.none()); } double forward = SwapPricer.parRate(underlying, ratesProvider); double pvbp = SwapPricer.LegPricer.pvbp(fixedLeg, ratesProvider); double strike = SwapPricer.LegPricer.couponEquivalent(fixedLeg, ratesProvider, pvbp); double tenor = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate); double shift = swaptionVolatilities.shift(expiry, tenor); ValueDerivatives volatilityAdj = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward); bool isCall = fixedLeg.PayReceive.Pay; // Payer at strike is exercise when rate > strike, i.e. call on rate // Backward sweep PointSensitivityBuilder pvbpDr = SwapPricer.LegPricer.pvbpSensitivity(fixedLeg, ratesProvider); PointSensitivityBuilder forwardDr = SwapPricer.parRateSensitivity(underlying, ratesProvider); double shiftedForward = forward + shift; double shiftedStrike = strike + shift; double price = BlackFormulaRepository.price(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value, isCall); double delta = BlackFormulaRepository.delta(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value, isCall); double vega = BlackFormulaRepository.vega(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value); double sign = swaption.LongShort.sign(); return(pvbpDr.multipliedBy(price * sign * Math.Sign(pvbp)).combinedWith(forwardDr.multipliedBy((delta + vega * volatilityAdj.getDerivative(0)) * Math.Abs(pvbp) * sign))); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the swaption to the rate curves. /// <para> /// The present value sensitivity is computed in a "sticky strike" style, i.e. the sensitivity to the /// curve nodes with the volatility at the swaption strike unchanged. This sensitivity does not include a potential /// change of volatility due to the implicit change of forward rate or moneyness. /// /// </para> /// </summary> /// <param name="swaption"> the swaption </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the volatilities </param> /// <returns> the point sensitivity to the rate curves </returns> public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyStrike(ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.Expiry); ResolvedSwap underlying = swaption.Underlying; ResolvedSwapLeg fixedLeg = this.fixedLeg(underlying); if (expiry < 0d) { // Option has expired already return(PointSensitivityBuilder.none()); } double forward = SwapPricer.parRate(underlying, ratesProvider); ValueDerivatives annuityDerivative = SwapPricer.LegPricer.annuityCashDerivative(fixedLeg, forward); double annuityCash = annuityDerivative.Value; double annuityCashDr = annuityDerivative.getDerivative(0); LocalDate settlementDate = ((CashSwaptionSettlement)swaption.SwaptionSettlement).SettlementDate; double discountSettle = ratesProvider.discountFactor(fixedLeg.Currency, settlementDate); double strike = calculateStrike(fixedLeg); double tenor = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); PutCall putCall = PutCall.ofPut(fixedLeg.PayReceive.Receive); double price = swaptionVolatilities.price(expiry, tenor, putCall, strike, forward, volatility); double delta = swaptionVolatilities.priceDelta(expiry, tenor, putCall, strike, forward, volatility); // Backward sweep PointSensitivityBuilder forwardSensi = SwapPricer.parRateSensitivity(underlying, ratesProvider); PointSensitivityBuilder discountSettleSensi = ratesProvider.discountFactors(fixedLeg.Currency).zeroRatePointSensitivity(settlementDate); double sign = swaption.LongShort.sign(); return(forwardSensi.multipliedBy(sign * discountSettle * (annuityCash * delta + annuityCashDr * price)).combinedWith(discountSettleSensi.multipliedBy(sign * annuityCash * price))); }
// Compute the accrued interest sensitivity on a given period by approximation private static PointSensitivityBuilder approximatedInterestSensitivity(OvernightIndexObservation observation, LocalDate endDate, OvernightIndexRates rates) { DayCount dayCount = observation.Index.DayCount; double remainingFixingAccrualFactor = dayCount.yearFraction(observation.EffectiveDate, endDate); double forwardRate = rates.periodRate(observation, endDate); PointSensitivityBuilder forwardRateSensitivity = rates.periodRatePointSensitivity(observation, endDate); double rateExp = 1.0 + forwardRate * remainingFixingAccrualFactor; forwardRateSensitivity = forwardRateSensitivity.multipliedBy(remainingFixingAccrualFactor / rateExp); return(forwardRateSensitivity); }
//------------------------------------------------------------------------- /// <summary> /// Computes the present value curve sensitivity by simple forward rate estimation. /// </summary> /// <param name="cmsPeriod"> the CMS </param> /// <param name="provider"> the rates provider </param> /// <returns> the present value sensitivity </returns> public virtual PointSensitivityBuilder presentValueSensitivity(CmsPeriod cmsPeriod, RatesProvider provider) { Currency ccy = cmsPeriod.Currency; LocalDate valuationDate = provider.ValuationDate; if (valuationDate.isAfter(cmsPeriod.PaymentDate)) { return(PointSensitivityBuilder.none()); } LocalDate fixingDate = cmsPeriod.FixingDate; double dfPayment = provider.discountFactor(ccy, cmsPeriod.PaymentDate); if (!fixingDate.isAfter(valuationDate)) { // Using fixing double?fixedRate = provider.timeSeries(cmsPeriod.Index).get(fixingDate); if (fixedRate.HasValue) { double payoff = 0d; switch (cmsPeriod.CmsPeriodType) { case CAPLET: payoff = Math.Max(fixedRate.Value - cmsPeriod.Strike, 0d); break; case FLOORLET: payoff = Math.Max(cmsPeriod.Strike - fixedRate.Value, 0d); break; case COUPON: payoff = fixedRate.Value; break; default: throw new System.ArgumentException("unsupported CMS type"); } return(provider.discountFactors(ccy).zeroRatePointSensitivity(cmsPeriod.PaymentDate).multipliedBy(payoff * cmsPeriod.Notional * cmsPeriod.YearFraction)); } else if (fixingDate.isBefore(valuationDate)) { throw new System.ArgumentException(Messages.format("Unable to get fixing for {} on date {}, no time-series supplied", cmsPeriod.Index, fixingDate)); } } if (!cmsPeriod.CmsPeriodType.Equals(CmsPeriodType.COUPON)) { throw new System.ArgumentException("Unable to price cap or floor in this pricer"); } // Using forward ResolvedSwap swap = cmsPeriod.UnderlyingSwap; ZeroRateSensitivity dfPaymentdr = provider.discountFactors(ccy).zeroRatePointSensitivity(cmsPeriod.PaymentDate); double forward = swapPricer.parRate(swap, provider); PointSensitivityBuilder forwardSensi = swapPricer.parRateSensitivity(swap, provider); return(forwardSensi.multipliedBy(dfPayment).combinedWith(dfPaymentdr.multipliedBy(forward)).multipliedBy(cmsPeriod.Notional * cmsPeriod.YearFraction)); }
// 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); }
// Composition - forward part in non-cutoff period; past/valuation date case dealt with in previous methods internal ObjDoublePair <PointSensitivityBuilder> compositionFactorAndSensitivityNonCutoff() { if (!nextFixing.isAfter(lastFixingNonCutoff)) { OvernightIndexObservation obs = computation.observeOn(nextFixing); LocalDate startDate = obs.EffectiveDate; LocalDate endDate = computation.calculateMaturityFromFixing(lastFixingNonCutoff); double accrualFactor = dayCount.yearFraction(startDate, endDate); double rate = rates.periodRate(obs, endDate); PointSensitivityBuilder rateSensitivity = rates.periodRatePointSensitivity(obs, endDate); rateSensitivity = rateSensitivity.multipliedBy(accrualFactor); return(ObjDoublePair.of(rateSensitivity, 1.0d + accrualFactor * rate)); } return(ObjDoublePair.of(PointSensitivityBuilder.none(), 1.0d)); }
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); }
// Composition - forward part in the cutoff period; past/valuation date case dealt with in previous methods internal ObjDoublePair <PointSensitivityBuilder> compositionFactorAndSensitivityCutoff() { OvernightIndexObservation obs = computation.observeOn(lastFixingNonCutoff); if (!nextFixing.isAfter(lastFixingNonCutoff)) { double rate = rates.rate(obs); double compositionFactor = 1.0d; double compositionFactorDerivative = 0.0; for (int i = 0; i < cutoffOffset - 1; i++) { compositionFactor *= 1.0d + accrualFactorCutoff[i] * rate; compositionFactorDerivative += accrualFactorCutoff[i] / (1.0d + accrualFactorCutoff[i] * rate); } compositionFactorDerivative *= compositionFactor; PointSensitivityBuilder rateSensitivity = cutoffOffset <= 1 ? PointSensitivityBuilder.none() : rates.ratePointSensitivity(obs); rateSensitivity = rateSensitivity.multipliedBy(compositionFactorDerivative); return(ObjDoublePair.of(rateSensitivity, compositionFactor)); } return(ObjDoublePair.of(PointSensitivityBuilder.none(), 1.0d)); }
public virtual void test_presentValueSensitivityRatesStickyStrike_parity() { CurrencyParameterSensitivities pvSensiRecLong = RATE_PROVIDER.parameterSensitivity(PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS).build()); CurrencyParameterSensitivities pvSensiRecShort = RATE_PROVIDER.parameterSensitivity(PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_REC_SHORT, RATE_PROVIDER, VOLS).build()); CurrencyParameterSensitivities pvSensiPayLong = RATE_PROVIDER.parameterSensitivity(PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_PAY_LONG, RATE_PROVIDER, VOLS).build()); CurrencyParameterSensitivities pvSensiPayShort = RATE_PROVIDER.parameterSensitivity(PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS).build()); assertTrue(pvSensiRecLong.equalWithTolerance(pvSensiRecShort.multipliedBy(-1d), NOTIONAL * TOL)); assertTrue(pvSensiPayLong.equalWithTolerance(pvSensiPayShort.multipliedBy(-1d), NOTIONAL * TOL)); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); PointSensitivityBuilder forwardSensi = PRICER_SWAP.parRateSensitivity(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.LegPricer.annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double annuityCashDeriv = PRICER_SWAP.LegPricer.annuityCashDerivative(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward).getDerivative(0); double discount = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE); PointSensitivityBuilder discountSensi = RATE_PROVIDER.discountFactors(USD).zeroRatePointSensitivity(SETTLE_DATE); PointSensitivities expecedPoint = discountSensi.multipliedBy(annuityCash * (forward - STRIKE)).combinedWith(forwardSensi.multipliedBy(discount * annuityCash + discount * annuityCashDeriv * (forward - STRIKE))).build(); CurrencyParameterSensitivities expected = RATE_PROVIDER.parameterSensitivity(expecedPoint); assertTrue(expected.equalWithTolerance(pvSensiPayLong.combinedWith(pvSensiRecLong.multipliedBy(-1d)), NOTIONAL * TOL)); assertTrue(expected.equalWithTolerance(pvSensiRecShort.combinedWith(pvSensiPayShort.multipliedBy(-1d)), NOTIONAL * TOL)); }
/// <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)); }
//------------------------------------------------------------------------- /// <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 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))); }
//------------------------------------------------------------------------- public virtual void test_multipliedBy() { PointSensitivityBuilder @base = PointSensitivityBuilder.none(); assertSame(@base.multipliedBy(2.0), @base); // no effect }