/// <summary> /// Calibrate trinomial tree to Black volatilities. /// <para> /// {@code timeToExpiry} determines the coverage of the resulting trinomial tree. /// Thus this should match the time to expiry of the target instrument to price using the calibrated tree. /// /// </para> /// </summary> /// <param name="timeToExpiry"> the time to expiry </param> /// <param name="currencyPair"> the currency pair </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the trinomial tree data </returns> public virtual RecombiningTrinomialTreeData calibrateTrinomialTree(double timeToExpiry, CurrencyPair currencyPair, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { validate(ratesProvider, volatilities); if (timeToExpiry <= 0d) { throw new System.ArgumentException("option expired"); } Currency ccyBase = currencyPair.Base; Currency ccyCounter = currencyPair.Counter; double todayFx = ratesProvider.fxRate(currencyPair); DiscountFactors baseDiscountFactors = ratesProvider.discountFactors(ccyBase); DiscountFactors counterDiscountFactors = ratesProvider.discountFactors(ccyCounter); System.Func <double, double> interestRate = (double?t) => { return(counterDiscountFactors.zeroRate(t.Value)); }; System.Func <double, double> dividendRate = (double?t) => { return(baseDiscountFactors.zeroRate(t.Value)); }; System.Func <DoublesPair, double> impliedVolSurface = (DoublesPair tk) => { double dfBase = baseDiscountFactors.discountFactor(tk.First); double dfCounter = counterDiscountFactors.discountFactor(tk.First); double forward = todayFx * dfBase / dfCounter; return(volatilities.volatility(currencyPair, tk.First, tk.Second, forward)); }; ImpliedTrinomialTreeLocalVolatilityCalculator localVol = new ImpliedTrinomialTreeLocalVolatilityCalculator(nSteps, timeToExpiry); return(localVol.calibrateImpliedVolatility(impliedVolSurface, todayFx, interestRate, dividendRate)); }
public virtual PointSensitivityBuilder pvbpSensitivity(RatePaymentPeriod paymentPeriod, RatesProvider provider) { ArgChecker.isTrue(!paymentPeriod.FxReset.Present, "FX reset is not supported"); int accPeriodCount = paymentPeriod.AccrualPeriods.size(); ArgChecker.isTrue(accPeriodCount == 1 || paymentPeriod.CompoundingMethod.Equals(CompoundingMethod.FLAT), "Only one accrued period or Flat compounding supported"); // no compounding if (accPeriodCount == 1) { RateAccrualPeriod accrualPeriod = paymentPeriod.AccrualPeriods.get(0); DiscountFactors discountFactors = provider.discountFactors(paymentPeriod.Currency); return(discountFactors.zeroRatePointSensitivity(paymentPeriod.PaymentDate).multipliedBy(accrualPeriod.YearFraction * paymentPeriod.Notional)); } else { // Flat compounding switch (paymentPeriod.CompoundingMethod) { case FLAT: return(pvbpSensitivtyCompoundedFlat(paymentPeriod, provider)); default: throw new System.NotSupportedException("PVBP not implemented yet for non FLAT compounding"); } } }
//------------------------------------------------------------------------- /// <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> /// Calculates the deposit fair rate given the start and end time and the accrual factor. /// <para> /// When the deposit has already started the number may not be meaningful as the remaining period /// is not in line with the accrual factor. /// /// </para> /// </summary> /// <param name="deposit"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the par rate </returns> public virtual double parRate(ResolvedTermDeposit deposit, RatesProvider provider) { Currency currency = deposit.Currency; DiscountFactors discountFactors = provider.discountFactors(currency); double dfStart = discountFactors.discountFactor(deposit.StartDate); double dfEnd = discountFactors.discountFactor(deposit.EndDate); double accrualFactor = deposit.YearFraction; return((dfStart / dfEnd - 1d) / accrualFactor); }
/// <summary> /// Calculates the present value sensitivity of the Ibor fixing product. /// <para> /// The present value sensitivity of the product is the sensitivity of the present value to /// the underlying curves. /// /// </para> /// </summary> /// <param name="deposit"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the point sensitivity of the present value </returns> public virtual PointSensitivities presentValueSensitivity(ResolvedIborFixingDeposit deposit, RatesProvider provider) { double forwardRate = this.forwardRate(deposit, provider); DiscountFactors discountFactors = provider.discountFactors(deposit.Currency); double discountFactor = discountFactors.discountFactor(deposit.EndDate); // sensitivity PointSensitivityBuilder sensiFwd = forwardRateSensitivity(deposit, provider).multipliedBy(-discountFactor * deposit.Notional * deposit.YearFraction); PointSensitivityBuilder sensiDsc = discountFactors.zeroRatePointSensitivity(deposit.EndDate).multipliedBy(deposit.Notional * deposit.YearFraction * (deposit.FixedRate - forwardRate)); return(sensiFwd.combinedWith(sensiDsc).build()); }
public virtual PointSensitivityBuilder presentValueSensitivity(FxResetNotionalExchange @event, RatesProvider provider) { DiscountFactors discountFactors = provider.discountFactors(@event.Currency); PointSensitivityBuilder sensiDsc = discountFactors.zeroRatePointSensitivity(@event.PaymentDate); sensiDsc = sensiDsc.multipliedBy(forecastValue(@event, provider)); PointSensitivityBuilder sensiFx = forecastValueSensitivity(@event, provider); sensiFx = sensiFx.multipliedBy(discountFactors.discountFactor(@event.PaymentDate)); return(sensiDsc.combinedWith(sensiFx)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the price sensitivity of the deliverable swap futures product. /// <para> /// The price sensitivity of the product is the sensitivity of the price to the underlying curves. /// /// </para> /// </summary> /// <param name="future"> the future </param> /// <param name="ratesProvider"> the rates provider </param> /// <returns> the price curve sensitivity of the product </returns> public PointSensitivities priceSensitivity(ResolvedDsf future, RatesProvider ratesProvider) { ResolvedSwap swap = future.UnderlyingSwap; Currency currency = future.Currency; double pvSwap = swapPricer.presentValue(swap, currency, ratesProvider).Amount; double dfInv = 1d / ratesProvider.discountFactor(currency, future.DeliveryDate); PointSensitivityBuilder sensiSwapPv = swapPricer.presentValueSensitivity(swap, ratesProvider).multipliedBy(dfInv); PointSensitivityBuilder sensiDf = ratesProvider.discountFactors(currency).zeroRatePointSensitivity(future.DeliveryDate).multipliedBy(-pvSwap * dfInv * dfInv); return(sensiSwapPv.combinedWith(sensiDf).build()); }
/// <summary> /// Calculates the par spread curve sensitivity. /// <para> /// The calculation is based on both of initial and final payments. /// Thus the number resulting may not be meaningful when deposit has already started and only the final /// payment remains (no initial payment). /// /// </para> /// </summary> /// <param name="deposit"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the par spread curve sensitivity </returns> public virtual PointSensitivities parSpreadSensitivity(ResolvedTermDeposit deposit, RatesProvider provider) { Currency currency = deposit.Currency; double accrualFactorInv = 1d / deposit.YearFraction; double dfStart = provider.discountFactor(currency, deposit.StartDate); double dfEndInv = 1d / provider.discountFactor(currency, deposit.EndDate); DiscountFactors discountFactors = provider.discountFactors(currency); PointSensitivityBuilder sensStart = discountFactors.zeroRatePointSensitivity(deposit.StartDate).multipliedBy(dfEndInv * accrualFactorInv); PointSensitivityBuilder sensEnd = discountFactors.zeroRatePointSensitivity(deposit.EndDate).multipliedBy(-dfStart * dfEndInv * dfEndInv * accrualFactorInv); return(sensStart.combinedWith(sensEnd).build()); }
/// <summary> /// Calculates the present value sensitivity of the FRA product. /// <para> /// The present value sensitivity of the product is the sensitivity of the present value to /// the underlying curves. /// /// </para> /// </summary> /// <param name="fra"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the point sensitivity of the present value </returns> public virtual PointSensitivities presentValueSensitivity(ResolvedFra fra, RatesProvider provider) { DiscountFactors discountFactors = provider.discountFactors(fra.Currency); double df = discountFactors.discountFactor(fra.PaymentDate); double notional = fra.Notional; double unitAmount = this.unitAmount(fra, provider); double derivative = this.derivative(fra, provider); PointSensitivityBuilder iborSens = forwardRateSensitivity(fra, provider).multipliedBy(derivative * df * notional); PointSensitivityBuilder discSens = discountFactors.zeroRatePointSensitivity(fra.PaymentDate).multipliedBy(unitAmount * notional); return(iborSens.withCurrency(fra.Currency).combinedWith(discSens).build()); }
/// <summary> /// Calculates the present value sensitivity by discounting the final cash flow (nominal + interest) /// and the initial payment (initial amount). /// </summary> /// <param name="deposit"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the point sensitivity of the present value </returns> public virtual PointSensitivities presentValueSensitivity(ResolvedTermDeposit deposit, RatesProvider provider) { Currency currency = deposit.Currency; // backward sweep double dfEndBar = deposit.Notional + deposit.Interest; double dfStartBar = -initialAmount(deposit, provider); // sensitivity DiscountFactors discountFactors = provider.discountFactors(currency); PointSensitivityBuilder sensStart = discountFactors.zeroRatePointSensitivity(deposit.StartDate).multipliedBy(dfStartBar); PointSensitivityBuilder sensEnd = discountFactors.zeroRatePointSensitivity(deposit.EndDate).multipliedBy(dfEndBar); return(sensStart.combinedWith(sensEnd).build()); }
//------------------------------------------------------------------------- public virtual PointSensitivityBuilder presentValueSensitivity(RatePaymentPeriod period, RatesProvider provider) { Currency ccy = period.Currency; DiscountFactors discountFactors = provider.discountFactors(ccy); LocalDate paymentDate = period.PaymentDate; double df = discountFactors.discountFactor(paymentDate); PointSensitivityBuilder forecastSensitivity = forecastValueSensitivity(period, provider); forecastSensitivity = forecastSensitivity.multipliedBy(df); double forecastValue = this.forecastValue(period, provider); PointSensitivityBuilder dscSensitivity = discountFactors.zeroRatePointSensitivity(paymentDate); dscSensitivity = dscSensitivity.multipliedBy(forecastValue); return(forecastSensitivity.combinedWith(dscSensitivity)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value by discounting the final cash flow (nominal + interest) /// and the initial payment (initial amount). /// <para> /// The present value of the product is the value on the valuation date. /// /// </para> /// </summary> /// <param name="deposit"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the present value of the product </returns> public virtual CurrencyAmount presentValue(ResolvedTermDeposit deposit, RatesProvider provider) { Currency currency = deposit.Currency; if (provider.ValuationDate.isAfter(deposit.EndDate)) { return(CurrencyAmount.of(currency, 0.0d)); } DiscountFactors discountFactors = provider.discountFactors(currency); double dfStart = discountFactors.discountFactor(deposit.StartDate); double dfEnd = discountFactors.discountFactor(deposit.EndDate); double pvStart = initialAmount(deposit, provider) * dfStart; double pvEnd = (deposit.Notional + deposit.Interest) * dfEnd; double pv = pvEnd - pvStart; return(CurrencyAmount.of(currency, pv)); }
/// <summary> /// Calculates the par spread sensitivity to the curves. /// <para> /// The sensitivity is reported in the counter currency of the product, but is actually dimensionless. /// /// </para> /// </summary> /// <param name="swap"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the spread curve sensitivity </returns> public virtual PointSensitivities parSpreadSensitivity(ResolvedFxSwap swap, RatesProvider provider) { Payment counterPaymentNear = swap.NearLeg.CounterCurrencyPayment; MultiCurrencyAmount pv = presentValue(swap, provider); double pvCounterCcy = pv.convertedTo(counterPaymentNear.Currency, provider).Amount; double dfEnd = provider.discountFactor(counterPaymentNear.Currency, swap.FarLeg.PaymentDate); double notionalBaseCcy = swap.NearLeg.BaseCurrencyPayment.Amount; double ps = -pvCounterCcy / (notionalBaseCcy * dfEnd); // backward sweep double psBar = 1d; double pvCounterCcyBar = -1d / (notionalBaseCcy * dfEnd) * psBar; double dfEndBar = -ps / dfEnd * psBar; ZeroRateSensitivity ddfEnddr = provider.discountFactors(counterPaymentNear.Currency).zeroRatePointSensitivity(swap.FarLeg.PaymentDate); PointSensitivities result = ddfEnddr.multipliedBy(dfEndBar).build(); PointSensitivities dpvdr = presentValueSensitivity(swap, provider); PointSensitivities dpvdrConverted = dpvdr.convertedTo(counterPaymentNear.Currency, provider); return(result.combinedWith(dpvdrConverted.multipliedBy(pvCounterCcyBar))); }
/// <summary> /// Calculates the present value curve sensitivity of the NDF product. /// <para> /// The present value sensitivity of the product is the sensitivity of the present value to /// the underlying curves. /// /// </para> /// </summary> /// <param name="ndf"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the point sensitivity of the present value </returns> public virtual PointSensitivities presentValueSensitivity(ResolvedFxNdf ndf, RatesProvider provider) { if (provider.ValuationDate.isAfter(ndf.PaymentDate)) { return(PointSensitivities.empty()); } Currency ccySettle = ndf.SettlementCurrency; Currency ccyOther = ndf.NonDeliverableCurrency; double notionalSettle = ndf.SettlementNotional; double agreedRate = ndf.AgreedFxRate.fxRate(ccySettle, ccyOther); double forwardRate = provider.fxIndexRates(ndf.Index).rate(ndf.Observation, ccySettle); double dfSettle = provider.discountFactor(ccySettle, ndf.PaymentDate); double ratio = agreedRate / forwardRate; double dscBar = (1d - ratio) * notionalSettle; PointSensitivityBuilder sensiDsc = provider.discountFactors(ccySettle).zeroRatePointSensitivity(ndf.PaymentDate).multipliedBy(dscBar); double forwardRateBar = dfSettle * notionalSettle * ratio / forwardRate; PointSensitivityBuilder sensiFx = provider.fxIndexRates(ndf.Index).ratePointSensitivity(ndf.Observation, ccySettle).withCurrency(ccySettle).multipliedBy(forwardRateBar); return(sensiDsc.combinedWith(sensiFx).build()); }
// sensitivity to the spread for a payment period with FLAT compounding type private PointSensitivityBuilder pvbpSensitivtyCompoundedFlat(RatePaymentPeriod paymentPeriod, RatesProvider provider) { Currency ccy = paymentPeriod.Currency; int nbCmp = paymentPeriod.AccrualPeriods.size(); double[] rate = paymentPeriod.AccrualPeriods.Select(ap => rawRate(ap, provider)).ToArray(); double df = provider.discountFactor(ccy, paymentPeriod.PaymentDate); double rB1 = 1.0; double[] cpaAccumulatedB1 = new double[nbCmp + 1]; cpaAccumulatedB1[nbCmp] = paymentPeriod.Notional * df * rB1; for (int j = nbCmp - 1; j >= 0; j--) { RateAccrualPeriod accrualPeriod = paymentPeriod.AccrualPeriods.get(j); cpaAccumulatedB1[j] = (1.0d + accrualPeriod.YearFraction * rate[j] * accrualPeriod.Gearing) * cpaAccumulatedB1[j + 1]; } // backward sweep double pvbpB2 = 1.0d; double[] cpaAccumulatedB1B2 = new double[nbCmp + 1]; double[] rateB2 = new double[nbCmp]; for (int j = 0; j < nbCmp; j++) { RateAccrualPeriod accrualPeriod = paymentPeriod.AccrualPeriods.get(j); cpaAccumulatedB1B2[j + 1] += accrualPeriod.YearFraction * pvbpB2; cpaAccumulatedB1B2[j + 1] += (1.0d + accrualPeriod.YearFraction * rate[j] * accrualPeriod.Gearing) * cpaAccumulatedB1B2[j]; rateB2[j] += accrualPeriod.YearFraction * accrualPeriod.Gearing * cpaAccumulatedB1[j + 1] * cpaAccumulatedB1B2[j]; } double dfB2 = paymentPeriod.Notional * rB1 * cpaAccumulatedB1B2[nbCmp]; PointSensitivityBuilder dfdr = provider.discountFactors(ccy).zeroRatePointSensitivity(paymentPeriod.PaymentDate); PointSensitivityBuilder pvbpdr = dfdr.multipliedBy(dfB2); for (int j = 0; j < nbCmp; j++) { RateAccrualPeriod accrualPeriod = paymentPeriod.AccrualPeriods.get(j); pvbpdr = pvbpdr.combinedWith(rateComputationFn.rateSensitivity(accrualPeriod.RateComputation, accrualPeriod.StartDate, accrualPeriod.EndDate, provider).multipliedBy(rateB2[j])); } return(pvbpdr); }