public CmsDeltaIntegrantProvider(SabrExtrapolationReplicationCmsPeriodPricer outerInstance, CmsPeriod cmsPeriod, ResolvedSwap swap, SabrSwaptionVolatilities swaptionVolatilities, double forward, double strike, double timeToExpiry, double tenor, double cutOffStrike, double eta) : base(outerInstance, cmsPeriod, swap, swaptionVolatilities, forward, strike, timeToExpiry, tenor, cutOffStrike, eta) { this.outerInstance = outerInstance; this.nnp_Renamed = nnp(forward); }
/// <summary> /// Explains the present value of the CMS period. /// <para> /// This returns explanatory information about the calculation. /// /// </para> /// </summary> /// <param name="period"> the product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the volatilities </param> /// <param name="builder"> the builder to populate </param> public void explainPresentValue(CmsPeriod period, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities, ExplainMapBuilder builder) { string type = period.CmsPeriodType.ToString(); Currency ccy = period.Currency; LocalDate paymentDate = period.PaymentDate; builder.put(ExplainKey.ENTRY_TYPE, "Cms" + type + "Period"); builder.put(ExplainKey.STRIKE_VALUE, period.Strike); builder.put(ExplainKey.NOTIONAL, CurrencyAmount.of(ccy, period.Notional)); builder.put(ExplainKey.PAYMENT_DATE, period.PaymentDate); builder.put(ExplainKey.DISCOUNT_FACTOR, ratesProvider.discountFactor(ccy, paymentDate)); builder.put(ExplainKey.START_DATE, period.StartDate); builder.put(ExplainKey.END_DATE, period.EndDate); builder.put(ExplainKey.FIXING_DATE, period.FixingDate); builder.put(ExplainKey.ACCRUAL_YEAR_FRACTION, period.YearFraction); builder.put(ExplainKey.PRESENT_VALUE, presentValue(period, ratesProvider, swaptionVolatilities)); builder.put(ExplainKey.FORWARD_RATE, swapPricer.parRate(period.UnderlyingSwap, ratesProvider)); builder.put(ExplainKey.CONVEXITY_ADJUSTED_RATE, adjustedForwardRate(period, ratesProvider, swaptionVolatilities)); }
public CmsIntegrantProvider(SabrExtrapolationReplicationCmsPeriodPricer outerInstance, CmsPeriod cmsPeriod, ResolvedSwap swap, SabrSwaptionVolatilities swaptionVolatilities, double forward, double strike, double timeToExpiry, double tenor, double cutOffStrike, double eta) { this.outerInstance = outerInstance; ResolvedSwapLeg fixedLeg = swap.getLegs(SwapLegType.FIXED).get(0); this.nbFixedPeriod = fixedLeg.PaymentPeriods.size(); this.nbFixedPaymentYear = (int)(long)Math.Round(1d / ((RatePaymentPeriod)fixedLeg.PaymentPeriods.get(0)).AccrualPeriods.get(0).YearFraction, MidpointRounding.AwayFromZero); this.tau = 1d / nbFixedPaymentYear; this.eta = eta; SabrFormulaData sabrPoint = SabrFormulaData.of(swaptionVolatilities.alpha(timeToExpiry, tenor), swaptionVolatilities.beta(timeToExpiry, tenor), swaptionVolatilities.rho(timeToExpiry, tenor), swaptionVolatilities.nu(timeToExpiry, tenor)); this.shift = swaptionVolatilities.shift(timeToExpiry, tenor); this.sabrExtrapolation = SabrExtrapolationRightFunction.of(forward + shift, timeToExpiry, sabrPoint, cutOffStrike + shift, outerInstance.mu); this.putCall = cmsPeriod.CmsPeriodType.Equals(CmsPeriodType.FLOORLET) ? PutCall.PUT : PutCall.CALL; this.strike = strike; this.factor = g(forward) / h(forward); this.g0 = new double[4]; g0[0] = nbFixedPeriod * tau; g0[1] = -0.5 * nbFixedPeriod * (nbFixedPeriod + 1.0d) * tau * tau; g0[2] = -2.0d / 3.0d * g0[1] * (nbFixedPeriod + 2.0d) * tau; g0[3] = -3.0d / 4.0d * g0[2] * (nbFixedPeriod + 2.0d) * tau; }
//------------------------------------------------------------------------- /// <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)); }
private double integrateCall(RungeKuttaIntegrator1D integrator, System.Func <double, double> integrant, SabrSwaptionVolatilities swaptionVolatilities, double forward, double strike, double expiryTime, double tenor) { double res; double vol = swaptionVolatilities.volatility(expiryTime, tenor, forward, forward); double upper0 = Math.Max(forward * Math.Exp(6d * vol * Math.Sqrt(expiryTime)), Math.Max(cutOffStrike, 2d * strike)); // To ensure that the integral covers a good part of the smile double upper = Math.Min(upper0, 1d); // To ensure that we don't miss the meaningful part res = integrator.integrate(integrant, strike, upper).Value; double reminder = integrant(upper) * upper; double error = reminder / res; int count = 0; while (Math.Abs(error) > integrator.RelativeTolerance && count < MAX_COUNT) { res += integrator.integrate(integrant, upper, 2d * upper).Value; upper *= 2d; reminder = integrant(upper) * upper; error = reminder / res; ++count; if (count == MAX_COUNT) { log.info("Maximum iteration count, " + MAX_COUNT + ", has been reached. Relative error is greater than " + integrator.RelativeTolerance); } } return(res); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the swaption trade 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="trade"> the swaption trade </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 PointSensitivities presentValueSensitivityRatesStickyModel(ResolvedSwaptionTrade trade, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { // product ResolvedSwaption product = trade.Product; PointSensitivityBuilder pointSens = isCash(product) ? cashParYieldPricer.presentValueSensitivityRatesStickyModel(product, ratesProvider, swaptionVolatilities) : physicalPricer.presentValueSensitivityRatesStickyModel(product, ratesProvider, swaptionVolatilities); // premium Payment premium = trade.Premium; PointSensitivityBuilder pvcsPremium = paymentPricer.presentValueSensitivity(premium, ratesProvider); // total return(pointSens.combinedWith(pvcsPremium).build()); }
//------------------------------------------------------------------------- /// <summary> /// Computes the currency exposure of the swaption trade. /// </summary> /// <param name="trade"> the swaption trade </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the volatilities </param> /// <returns> the currency exposure </returns> public virtual MultiCurrencyAmount currencyExposure(ResolvedSwaptionTrade trade, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { return(MultiCurrencyAmount.of(presentValue(trade, ratesProvider, swaptionVolatilities))); }
/// <summary> /// Computes the adjusted forward rate for a CMS coupon. /// <para> /// The adjusted forward rate, is the number such that, multiplied by the notional, the year fraction and the payment /// date discount factor, it produces the present value. In other terms, it is the number which used in the same /// formula used for Ibor coupon pricing will provide the correct present value. /// </para> /// <para> /// For period already fixed, this number will be equal to the swap index fixing. /// </para> /// <para> /// For cap or floor the result is the adjusted forward rate for the coupon equivalent to the cap/floor, /// i.e. the coupon with the same dates and index but with no cap or floor strike. /// /// </para> /// </summary> /// <param name="cmsPeriod"> the CMS period, which should be of the type <seealso cref="CmsPeriodType#COUPON"/> </param> /// <param name="provider"> the rates provider </param> /// <param name="swaptionVolatilities"> the swaption volatilities </param> /// <returns> the adjusted forward rate </returns> public double adjustedForwardRate(CmsPeriod cmsPeriod, RatesProvider provider, SabrSwaptionVolatilities swaptionVolatilities) { CmsPeriod coupon = cmsPeriod.toCouponEquivalent(); Currency ccy = cmsPeriod.Currency; double dfPayment = provider.discountFactor(ccy, coupon.PaymentDate); double pv = presentValue(coupon, provider, swaptionVolatilities).Amount; return(pv / (coupon.Notional * coupon.YearFraction * dfPayment)); }
/// <summary> /// Computes the adjustment to the forward rate for a CMS coupon. /// <para> /// The adjustment to the forward rate, is the quantity that need to be added to the forward rate to obtain the /// adjusted forward rate. The adjusted forward rate is the number which used in the same formula used for /// Ibor coupon pricing (forward * notional * accrual factor * discount factor) will provide the correct present value. /// </para> /// <para> /// For cap or floor the result is the adjustment to the forward rate for the coupon equivalent to the cap/floor, /// i.e. the coupon with the same dates and index but with no cap or floor strike. /// /// </para> /// </summary> /// <param name="cmsPeriod"> the CMS period, which should be of the type <seealso cref="CmsPeriodType#COUPON"/> </param> /// <param name="provider"> the rates provider </param> /// <param name="swaptionVolatilities"> the swaption volatilities </param> /// <returns> the adjusted forward rate </returns> public double adjustmentToForwardRate(CmsPeriod cmsPeriod, RatesProvider provider, SabrSwaptionVolatilities swaptionVolatilities) { CmsPeriod coupon = cmsPeriod.toCouponEquivalent(); double adjustedForwardRate = this.adjustedForwardRate(coupon, provider, swaptionVolatilities); double forward = swapPricer.parRate(coupon.UnderlyingSwap, provider); return(adjustedForwardRate - forward); }
//------------------------------------------------------------------------- /// <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); 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 shift = swaptionVolatilities.shift(expiry, tenor); ValueDerivatives volatilityAdj = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward); bool isCall = fixedLeg.PayReceive.Pay; 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); 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 + vega * volatilityAdj.getDerivative(0)) + annuityCashDr * price)).combinedWith(discountSettleSensi.multipliedBy(sign * annuityCash * price))); }
//------------------------------------------------------------------------- /// <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)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value of the CMS product. /// <para> /// The present value of the product is the value on the valuation date. /// </para> /// <para> /// CMS leg and pay leg are typically in the same currency. Thus the present value is expressed as a /// single currency amount in most cases. /// /// </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 </returns> public virtual MultiCurrencyAmount presentValue(ResolvedCms cms, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { CurrencyAmount pvCmsLeg = cmsLegPricer.presentValue(cms.CmsLeg, ratesProvider, swaptionVolatilities); if (!cms.PayLeg.Present) { return(MultiCurrencyAmount.of(pvCmsLeg)); } CurrencyAmount pvPayLeg = payLegPricer.presentValue(cms.PayLeg.get(), ratesProvider); return(MultiCurrencyAmount.of(pvCmsLeg).plus(pvPayLeg)); }
/// <summary> /// Calculates the present value sensitivity to the strike value. /// <para> /// The present value sensitivity of the product is the sensitivity of the present value to the strike value. /// This is not relevant for CMS coupons and an exception is thrown in the underlying pricer. /// /// </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 double presentValueSensitivityStrike(ResolvedCms cms, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { return(cmsLegPricer.presentValueSensitivityStrike(cms.CmsLeg, ratesProvider, swaptionVolatilities)); }
/// <summary> /// Calculates the present value sensitivity to the SABR model parameters. /// <para> /// The present value sensitivity of the product is the sensitivity of the present value /// to the SABR model parameters, alpha, beta, rho and nu. /// /// </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 presentValueSensitivityModelParamsSabr(ResolvedCms cms, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { return(cmsLegPricer.presentValueSensitivityModelParamsSabr(cms.CmsLeg, ratesProvider, swaptionVolatilities)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity to the SABR model parameters of the swaption product. /// <para> /// The sensitivity of the present value to the SABR model parameters, alpha, beta, rho and nu. /// /// </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 SABR model parameters </returns> public virtual PointSensitivityBuilder presentValueSensitivityModelParamsSabr(ResolvedSwaption swaption, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.Expiry); ResolvedSwap underlying = swaption.Underlying; ResolvedSwapLeg fixedLeg = this.fixedLeg(underlying); double tenor = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate); double shift = swaptionVolatilities.shift(expiry, tenor); double pvbp = SwapPricer.LegPricer.pvbp(fixedLeg, ratesProvider); double strike = SwapPricer.LegPricer.couponEquivalent(fixedLeg, ratesProvider, pvbp); if (expiry < 0d) { // Option has expired already return(PointSensitivityBuilder.none()); } double forward = SwapPricer.parRate(underlying, ratesProvider); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); DoubleArray derivative = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward).Derivatives; // Backward sweep double vega = Math.Abs(pvbp) * BlackFormulaRepository.vega(forward + shift, strike + shift, expiry, volatility) * swaption.LongShort.sign(); // sensitivities Currency ccy = fixedLeg.Currency; SwaptionVolatilitiesName name = swaptionVolatilities.Name; return(PointSensitivityBuilder.of(SwaptionSabrSensitivity.of(name, expiry, tenor, ALPHA, ccy, vega * derivative.get(2)), SwaptionSabrSensitivity.of(name, expiry, tenor, BETA, ccy, vega * derivative.get(3)), SwaptionSabrSensitivity.of(name, expiry, tenor, RHO, ccy, vega * derivative.get(4)), SwaptionSabrSensitivity.of(name, expiry, tenor, NU, ccy, vega * derivative.get(5)))); }
//------------------------------------------------------------------------- /// <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> /// 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> /// 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> /// Calculates the present value sensitivity to the SABR model parameters of the swaption trade. /// <para> /// The sensitivity of the present value to the SABR model parameters, alpha, beta, rho and nu. /// /// </para> /// </summary> /// <param name="trade"> the swaption trade </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the volatilities </param> /// <returns> the point sensitivity to the SABR model parameters </returns> public virtual PointSensitivities presentValueSensitivityModelParamsSabr(ResolvedSwaptionTrade trade, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { ResolvedSwaption product = trade.Product; PointSensitivityBuilder pointSens = isCash(product) ? cashParYieldPricer.presentValueSensitivityModelParamsSabr(product, ratesProvider, swaptionVolatilities) : physicalPricer.presentValueSensitivityModelParamsSabr(product, ratesProvider, swaptionVolatilities); return(pointSens.build()); }
/// <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)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value of the swaption trade. /// <para> /// The result is expressed using the currency of the swaption. /// /// </para> /// </summary> /// <param name="trade"> the swaption trade </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the volatilities </param> /// <returns> the present value </returns> public virtual CurrencyAmount presentValue(ResolvedSwaptionTrade trade, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { // product ResolvedSwaption product = trade.Product; CurrencyAmount pvProduct = isCash(product) ? cashParYieldPricer.presentValue(product, ratesProvider, swaptionVolatilities) : physicalPricer.presentValue(product, ratesProvider, swaptionVolatilities); // premium Payment premium = trade.Premium; CurrencyAmount pvPremium = paymentPricer.presentValue(premium, ratesProvider); // total return(pvProduct.plus(pvPremium)); }
//------------------------------------------------------------------------- /// <summary> /// Explains the present value of the CMS product. /// <para> /// This returns explanatory information about the calculation. /// /// </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 explain PV map </returns> public virtual ExplainMap explainPresentValue(ResolvedCms cms, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { ExplainMapBuilder builder = ExplainMap.builder(); builder.put(ExplainKey.ENTRY_TYPE, "CmsSwap"); IList <ExplainMap> legsExplain = new List <ExplainMap>(); legsExplain.Add(cmsLegPricer.explainPresentValue(cms.CmsLeg, ratesProvider, swaptionVolatilities)); if (cms.PayLeg.Present) { legsExplain.Add(payLegPricer.explainPresentValue(cms.PayLeg.get(), ratesProvider)); } builder.put(ExplainKey.LEGS, legsExplain); return(builder.build()); }