public virtual void test_price_presentValue_atExpiry() { double df = RATES_PROVIDER_EXPIRY.discountFactor(USD, PAYMENT_DATE); double forward = PRICER.DiscountingFxSingleProductPricer.forwardFxRate(FX_PRODUCT_HIGH, RATES_PROVIDER_EXPIRY).fxRate(CURRENCY_PAIR); double priceCallOtm = PRICER.price(CALL_OTM, RATES_PROVIDER_EXPIRY, VOLS_EXPIRY); CurrencyAmount pvCallOtm = PRICER.presentValue(CALL_OTM, RATES_PROVIDER_EXPIRY, VOLS_EXPIRY); assertEquals(priceCallOtm, 0d, TOL); assertEquals(pvCallOtm.Amount, 0d, NOTIONAL * TOL); double priceCallItm = PRICER.price(CALL_ITM, RATES_PROVIDER_EXPIRY, VOLS_EXPIRY); CurrencyAmount pvCallItm = PRICER.presentValue(CALL_ITM, RATES_PROVIDER_EXPIRY, VOLS_EXPIRY); assertEquals(priceCallItm, df * (forward - STRIKE_RATE_LOW), TOL); assertEquals(pvCallItm.Amount, df * (forward - STRIKE_RATE_LOW) * NOTIONAL, NOTIONAL * TOL); double pricePutOtm = PRICER.price(PUT_OTM, RATES_PROVIDER_EXPIRY, VOLS_EXPIRY); CurrencyAmount pvPutOtm = PRICER.presentValue(PUT_OTM, RATES_PROVIDER_EXPIRY, VOLS_EXPIRY); assertEquals(pricePutOtm, 0d, TOL); assertEquals(pvPutOtm.Amount, 0d, NOTIONAL * TOL); double pricePutItm = PRICER.price(PUT_ITM, RATES_PROVIDER_EXPIRY, VOLS_EXPIRY); CurrencyAmount pvPutItm = PRICER.presentValue(PUT_ITM, RATES_PROVIDER_EXPIRY, VOLS_EXPIRY); assertEquals(pricePutItm, df * (STRIKE_RATE_HIGH - forward), TOL); assertEquals(pvPutItm.Amount, df * (STRIKE_RATE_HIGH - forward) * NOTIONAL, NOTIONAL * TOL); }
public virtual void test_presentValue() { CurrencyAmount computed = PRICER.presentValue(NDF, PROVIDER); double dscUsd = PROVIDER.discountFactor(USD, NDF.PaymentDate); double dscKrw = PROVIDER.discountFactor(KRW, NDF.PaymentDate); double expected = NOMINAL_USD * (dscUsd - dscKrw * FX_RATE / PROVIDER.fxRate(CurrencyPair.of(USD, KRW))); assertEquals(computed.Currency, USD); assertEquals(computed.Amount, expected, NOMINAL_USD * TOL); }
/// <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()); }
//------------------------------------------------------------------------- public virtual void test_presentValue_beforeStart() { MultiCurrencyAmount computed = PRICER.presentValue(SWAP_PRODUCT, PROVIDER); double expected_usd = NOMINAL_USD * (PROVIDER.discountFactor(USD, PAYMENT_DATE_NEAR) - PROVIDER.discountFactor(USD, PAYMENT_DATE_FAR)); double expected_krw = NOMINAL_USD * (-FX_RATE * PROVIDER.discountFactor(KRW, PAYMENT_DATE_NEAR) + (FX_RATE + FX_FWD_POINTS) * PROVIDER.discountFactor(KRW, PAYMENT_DATE_FAR)); assertEquals(computed.getAmount(USD).Amount, expected_usd, NOMINAL_USD * TOL); assertEquals(computed.getAmount(KRW).Amount, expected_krw, NOMINAL_USD * FX_RATE * TOL); // currency exposure MultiCurrencyAmount exposure = PRICER.currencyExposure(SWAP_PRODUCT, PROVIDER); assertEquals(exposure, computed); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the currency exposure by discounting each payment in its own currency. /// </summary> /// <param name="ndf"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the currency exposure </returns> public virtual MultiCurrencyAmount currencyExposure(ResolvedFxNdf ndf, RatesProvider provider) { if (provider.ValuationDate.isAfter(ndf.PaymentDate)) { return(MultiCurrencyAmount.empty()); } Currency ccySettle = ndf.SettlementCurrency; CurrencyAmount notionalSettle = ndf.SettlementCurrencyNotional; double dfSettle = provider.discountFactor(ccySettle, ndf.PaymentDate); Currency ccyOther = ndf.NonDeliverableCurrency; double agreedRate = ndf.AgreedFxRate.fxRate(ccySettle, ccyOther); double dfOther = provider.discountFactor(ccyOther, ndf.PaymentDate); return(MultiCurrencyAmount.of(notionalSettle.multipliedBy(dfSettle)).plus(CurrencyAmount.of(ccyOther, -notionalSettle.Amount * agreedRate * dfOther))); }
//------------------------------------------------------------------------- public virtual void explainPresentValue(FxResetNotionalExchange @event, RatesProvider provider, ExplainMapBuilder builder) { Currency currency = @event.Currency; LocalDate paymentDate = @event.PaymentDate; builder.put(ExplainKey.ENTRY_TYPE, "FxResetNotionalExchange"); builder.put(ExplainKey.PAYMENT_DATE, paymentDate); builder.put(ExplainKey.PAYMENT_CURRENCY, currency); builder.put(ExplainKey.TRADE_NOTIONAL, @event.NotionalAmount); if (paymentDate.isBefore(provider.ValuationDate)) { builder.put(ExplainKey.COMPLETED, true); builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.zero(currency)); builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.zero(currency)); } else { builder.addListEntry(ExplainKey.OBSERVATIONS, child => { child.put(ExplainKey.ENTRY_TYPE, "FxObservation"); child.put(ExplainKey.INDEX, @event.Observation.Index); child.put(ExplainKey.FIXING_DATE, @event.Observation.FixingDate); child.put(ExplainKey.INDEX_VALUE, fxRate(@event, provider)); }); builder.put(ExplainKey.DISCOUNT_FACTOR, provider.discountFactor(currency, paymentDate)); builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.of(currency, forecastValue(@event, provider))); builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.of(currency, presentValue(@event, provider))); } }
//------------------------------------------------------------------------- public virtual double presentValue(RatePaymentPeriod period, RatesProvider provider) { // forecastValue * discountFactor double df = provider.discountFactor(period.Currency, period.PaymentDate); return(forecastValue(period, provider) * df); }
public virtual double pvbp(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); double df = provider.discountFactor(paymentPeriod.Currency, paymentPeriod.PaymentDate); return(df * accrualPeriod.YearFraction * paymentPeriod.Notional); } else { // Flat compounding switch (paymentPeriod.CompoundingMethod) { case FLAT: return(pvbpCompoundedFlat(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))); }
//------------------------------------------------------------------------- public virtual void explainPresentValue(KnownAmountSwapPaymentPeriod period, RatesProvider provider, ExplainMapBuilder builder) { Currency currency = period.Currency; LocalDate paymentDate = period.PaymentDate; builder.put(ExplainKey.ENTRY_TYPE, "KnownAmountPaymentPeriod"); builder.put(ExplainKey.PAYMENT_DATE, paymentDate); builder.put(ExplainKey.PAYMENT_CURRENCY, currency); builder.put(ExplainKey.START_DATE, period.StartDate); builder.put(ExplainKey.UNADJUSTED_START_DATE, period.UnadjustedStartDate); builder.put(ExplainKey.END_DATE, period.EndDate); builder.put(ExplainKey.UNADJUSTED_END_DATE, period.UnadjustedEndDate); builder.put(ExplainKey.DAYS, (int)DAYS.between(period.StartDate, period.EndDate)); if (paymentDate.isBefore(provider.ValuationDate)) { builder.put(ExplainKey.COMPLETED, true); builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.zero(currency)); builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.zero(currency)); } else { builder.put(ExplainKey.DISCOUNT_FACTOR, provider.discountFactor(currency, paymentDate)); builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.of(currency, forecastValue(period, provider))); builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.of(currency, presentValue(period, provider))); } }
//------------------------------------------------------------------------- public virtual double presentValue(FxResetNotionalExchange @event, RatesProvider provider) { // forecastValue * discountFactor double df = provider.discountFactor(@event.Currency, @event.PaymentDate); return(forecastValue(@event, provider) * df); }
//------------------------------------------------------------------------- /// <summary> /// Explains the present value of the FRA product. /// <para> /// This returns explanatory information about the calculation. /// /// </para> /// </summary> /// <param name="fra"> the FRA product for which present value should be computed </param> /// <param name="provider"> the rates provider </param> /// <returns> the explanatory information </returns> public virtual ExplainMap explainPresentValue(ResolvedFra fra, RatesProvider provider) { ExplainMapBuilder builder = ExplainMap.builder(); Currency currency = fra.Currency; builder.put(ExplainKey.ENTRY_TYPE, "FRA"); builder.put(ExplainKey.PAYMENT_DATE, fra.PaymentDate); builder.put(ExplainKey.START_DATE, fra.StartDate); builder.put(ExplainKey.END_DATE, fra.EndDate); builder.put(ExplainKey.ACCRUAL_YEAR_FRACTION, fra.YearFraction); builder.put(ExplainKey.DAYS, (int)DAYS.between(fra.StartDate, fra.EndDate)); builder.put(ExplainKey.PAYMENT_CURRENCY, currency); builder.put(ExplainKey.NOTIONAL, CurrencyAmount.of(currency, fra.Notional)); builder.put(ExplainKey.TRADE_NOTIONAL, CurrencyAmount.of(currency, fra.Notional)); if (fra.PaymentDate.isBefore(provider.ValuationDate)) { builder.put(ExplainKey.COMPLETED, true); builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.zero(currency)); builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.zero(currency)); } else { double rate = rateComputationFn.explainRate(fra.FloatingRate, fra.StartDate, fra.EndDate, provider, builder); builder.put(ExplainKey.FIXED_RATE, fra.FixedRate); builder.put(ExplainKey.DISCOUNT_FACTOR, provider.discountFactor(currency, fra.PaymentDate)); builder.put(ExplainKey.PAY_OFF_RATE, rate); builder.put(ExplainKey.UNIT_AMOUNT, unitAmount(fra, provider)); builder.put(ExplainKey.FORECAST_VALUE, forecastValue(fra, provider)); builder.put(ExplainKey.PRESENT_VALUE, presentValue(fra, provider)); } return(builder.build()); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value of the FRA product. /// <para> /// The present value of the product is the value on the valuation date. /// This is the discounted forecast value. /// /// </para> /// </summary> /// <param name="fra"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the present value of the product </returns> public virtual CurrencyAmount presentValue(ResolvedFra fra, RatesProvider provider) { // forecastValue * discountFactor double df = provider.discountFactor(fra.Currency, fra.PaymentDate); double pv = forecastValue0(fra, provider) * df; return(CurrencyAmount.of(fra.Currency, pv)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the price of the deliverable swap futures product. /// <para> /// The price of the product is the price on the valuation date. /// /// </para> /// </summary> /// <param name="future"> the future </param> /// <param name="ratesProvider"> the rates provider </param> /// <returns> the price of the product, in decimal form </returns> public double price(ResolvedDsf future, RatesProvider ratesProvider) { ResolvedSwap swap = future.UnderlyingSwap; Currency currency = future.Currency; CurrencyAmount pvSwap = swapPricer.presentValue(swap, currency, ratesProvider); double df = ratesProvider.discountFactor(currency, future.DeliveryDate); return(1d + pvSwap.Amount / df); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the future cash flow of the FRA product. /// <para> /// There is only one cash flow on the payment date for the FRA product. /// The expected currency amount of the cash flow is the same as <seealso cref="#forecastValue(ResolvedFra, RatesProvider)"/>. /// /// </para> /// </summary> /// <param name="fra"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the cash flows </returns> public virtual CashFlows cashFlows(ResolvedFra fra, RatesProvider provider) { LocalDate paymentDate = fra.PaymentDate; double forecastValue = forecastValue0(fra, provider); double df = provider.discountFactor(fra.Currency, paymentDate); CashFlow cashFlow = CashFlow.ofForecastValue(paymentDate, fra.Currency, forecastValue, df); return(CashFlows.of(cashFlow)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the par spread. /// <para> /// The par spread is the spread that should be added to the FX forward points to have a zero value. /// /// </para> /// </summary> /// <param name="swap"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the spread </returns> public virtual double parSpread(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; return(-pvCounterCcy / (notionalBaseCcy * dfEnd)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the par spread. /// <para> /// This is the spread that should be added to the FX points to have a zero value. /// /// </para> /// </summary> /// <param name="fx"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the spread </returns> public virtual double parSpread(ResolvedFxSingle fx, RatesProvider provider) { Payment basePayment = fx.BaseCurrencyPayment; Payment counterPayment = fx.CounterCurrencyPayment; MultiCurrencyAmount pv = presentValue(fx, provider); double pvCounterCcy = pv.convertedTo(counterPayment.Currency, provider).Amount; double dfEnd = provider.discountFactor(counterPayment.Currency, fx.PaymentDate); double notionalBaseCcy = basePayment.Amount; return(pvCounterCcy / (notionalBaseCcy * dfEnd)); }
//------------------------------------------------------------------------- /// <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()); }
//------------------------------------------------------------------------- public virtual void currency_exposure_GBP() { LocalDate startDate = LocalDate.of(2016, 8, 2); LocalDate fixingDate = LocalDate.of(2016, 11, 2); LocalDate endDate = LocalDate.of(2016, 11, 4); double yearFraction = 0.25; double rate = 0.10; RateAccrualPeriod accrual = RateAccrualPeriod.builder().startDate(startDate).endDate(endDate).yearFraction(yearFraction).rateComputation(FixedRateComputation.of(rate)).build(); double notional = 1000000; RatePaymentPeriod fixedFx = RatePaymentPeriod.builder().accrualPeriods(accrual).fxReset(FxReset.of(FxIndexObservation.of(FxIndices.GBP_USD_WM, fixingDate, REF_DATA), GBP)).notional(notional).paymentDate(endDate).dayCount(DayCounts.ONE_ONE).currency(USD).build(); // 1_000_000 GBP paid in USD at maturity PointSensitivityBuilder pts = PERIOD_PRICER.presentValueSensitivity(fixedFx, PROVIDER); MultiCurrencyAmount ceComputed = PERIOD_PRICER.currencyExposure(fixedFx, PROVIDER); double dfGbp = PROVIDER.discountFactor(GBP, endDate); double ceGbpExpected = notional * yearFraction * rate * dfGbp; assertEquals(ceComputed.getAmount(GBP).Amount, ceGbpExpected, 1.0E-6); MultiCurrencyAmount ceWithoutPvComputed = PROVIDER.currencyExposure(pts.build().convertedTo(GBP, PROVIDER)); CurrencyAmount pvComputed = CurrencyAmount.of(USD, PERIOD_PRICER.presentValue(fixedFx, PROVIDER)); MultiCurrencyAmount ceComputed2 = ceWithoutPvComputed.plus(pvComputed); assertEquals(ceComputed2.getAmount(GBP).Amount, ceGbpExpected, TOLERANCE); assertEquals(ceComputed2.getAmount(USD).Amount, 0.0, TOLERANCE); }
public virtual void test_presentValue() { MultiCurrencyAmount computed = PRICER.presentValue(FWD, PROVIDER); double expected1 = NOMINAL_USD * PROVIDER.discountFactor(USD, PAYMENT_DATE); double expected2 = -NOMINAL_USD *FX_RATE *PROVIDER.discountFactor(KRW, PAYMENT_DATE); assertEquals(computed.getAmount(USD).Amount, expected1, NOMINAL_USD * TOL); assertEquals(computed.getAmount(KRW).Amount, expected2, NOMINAL_USD * TOL); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value of the Ibor fixing deposit product. /// <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(ResolvedIborFixingDeposit deposit, RatesProvider provider) { Currency currency = deposit.Currency; if (provider.ValuationDate.isAfter(deposit.EndDate)) { return(CurrencyAmount.of(currency, 0.0d)); } double forwardRate = this.forwardRate(deposit, provider); double discountFactor = provider.discountFactor(currency, deposit.EndDate); double fv = deposit.Notional * deposit.YearFraction * (deposit.FixedRate - forwardRate); double pv = discountFactor * fv; return(CurrencyAmount.of(currency, pv)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value of the NDF product. /// <para> /// The present value of the product is the value on the valuation date. /// The present value is returned in the settlement currency. /// /// </para> /// </summary> /// <param name="ndf"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the present value of the product in the settlement currency </returns> public virtual CurrencyAmount presentValue(ResolvedFxNdf ndf, RatesProvider provider) { Currency ccySettle = ndf.SettlementCurrency; if (provider.ValuationDate.isAfter(ndf.PaymentDate)) { return(CurrencyAmount.zero(ccySettle)); } Currency ccyOther = ndf.NonDeliverableCurrency; CurrencyAmount notionalSettle = ndf.SettlementCurrencyNotional; double agreedRate = ndf.AgreedFxRate.fxRate(ccySettle, ccyOther); double forwardRate = provider.fxIndexRates(ndf.Index).rate(ndf.Observation, ccySettle); double dfSettle = provider.discountFactor(ccySettle, ndf.PaymentDate); return(notionalSettle.multipliedBy(dfSettle * (1d - agreedRate / forwardRate))); }
//------------------------------------------------------------------------- public virtual MultiCurrencyAmount currencyExposure(FxResetNotionalExchange @event, RatesProvider provider) { LocalDate fixingDate = @event.Observation.FixingDate; FxIndexRates rates = provider.fxIndexRates(@event.Observation.Index); double df = provider.discountFactor(@event.Currency, @event.PaymentDate); if (!fixingDate.isAfter(provider.ValuationDate) && rates.Fixings.get(fixingDate).HasValue) { double fxRate = rates.rate(@event.Observation, @event.ReferenceCurrency); return(MultiCurrencyAmount.of(CurrencyAmount.of(@event.Currency, @event.Notional * df * fxRate))); } LocalDate maturityDate = @event.Observation.MaturityDate; double fxRateSpotSensitivity = rates.FxForwardRates.rateFxSpotSensitivity(@event.ReferenceCurrency, maturityDate); return(MultiCurrencyAmount.of(CurrencyAmount.of(@event.ReferenceCurrency, @event.Notional * df * fxRateSpotSensitivity))); }
/// <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))); }
//------------------------------------------------------------------------- // sensitivity to the spread for a payment period with FLAT compounding type private double pvbpCompoundedFlat(RatePaymentPeriod paymentPeriod, RatesProvider provider) { int nbCmp = paymentPeriod.AccrualPeriods.size(); double[] rate = paymentPeriod.AccrualPeriods.Select(ap => rawRate(ap, provider)).ToArray(); double df = provider.discountFactor(paymentPeriod.Currency, paymentPeriod.PaymentDate); double rBar = 1.0; double[] cpaAccumulatedBar = new double[nbCmp + 1]; cpaAccumulatedBar[nbCmp] = paymentPeriod.Notional * df * rBar; double spreadBar = 0.0d; for (int j = nbCmp - 1; j >= 0; j--) { cpaAccumulatedBar[j] = (1.0d + paymentPeriod.AccrualPeriods.get(j).YearFraction *rate[j] * paymentPeriod.AccrualPeriods.get(j).Gearing) * cpaAccumulatedBar[j + 1]; spreadBar += paymentPeriod.AccrualPeriods.get(j).YearFraction *cpaAccumulatedBar[j + 1]; } return(spreadBar); }
//------------------------------------------------------------------------- public virtual MultiCurrencyAmount currencyExposure(RatePaymentPeriod period, RatesProvider provider) { double df = provider.discountFactor(period.Currency, period.PaymentDate); if (period.FxReset.Present) { FxReset fxReset = period.FxReset.get(); LocalDate fixingDate = fxReset.Observation.FixingDate; FxIndexRates rates = provider.fxIndexRates(fxReset.Observation.Index); if (!fixingDate.isAfter(provider.ValuationDate) && rates.Fixings.get(fixingDate).HasValue) { double fxRate = rates.rate(fxReset.Observation, fxReset.ReferenceCurrency); return(MultiCurrencyAmount.of(period.Currency, accrualWithNotional(period, period.Notional * fxRate * df, provider))); } double fxRateSpotSensitivity = rates.FxForwardRates.rateFxSpotSensitivity(fxReset.ReferenceCurrency, fxReset.Observation.MaturityDate); return(MultiCurrencyAmount.of(fxReset.ReferenceCurrency, accrualWithNotional(period, period.Notional * fxRateSpotSensitivity * df, provider))); } return(MultiCurrencyAmount.of(period.Currency, accrualWithNotional(period, period.Notional * df, provider))); }
/// <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()); }
//------------------------------------------------------------------------- public virtual void explainPresentValue(RatePaymentPeriod paymentPeriod, RatesProvider provider, ExplainMapBuilder builder) { Currency currency = paymentPeriod.Currency; LocalDate paymentDate = paymentPeriod.PaymentDate; double fxRate = this.fxRate(paymentPeriod, provider); double notional = paymentPeriod.Notional * fxRate; builder.put(ExplainKey.ENTRY_TYPE, "RatePaymentPeriod"); builder.put(ExplainKey.PAYMENT_DATE, paymentDate); builder.put(ExplainKey.PAYMENT_CURRENCY, currency); builder.put(ExplainKey.NOTIONAL, CurrencyAmount.of(currency, notional)); builder.put(ExplainKey.TRADE_NOTIONAL, paymentPeriod.NotionalAmount); if (paymentDate.isBefore(provider.ValuationDate)) { builder.put(ExplainKey.COMPLETED, true); builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.zero(currency)); builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.zero(currency)); } else { paymentPeriod.FxReset.ifPresent(fxReset => { builder.addListEntry(ExplainKey.OBSERVATIONS, child => { child.put(ExplainKey.ENTRY_TYPE, "FxObservation"); child.put(ExplainKey.INDEX, fxReset.Observation.Index); child.put(ExplainKey.FIXING_DATE, fxReset.Observation.FixingDate); child.put(ExplainKey.INDEX_VALUE, fxRate); }); }); foreach (RateAccrualPeriod accrualPeriod in paymentPeriod.AccrualPeriods) { builder.addListEntry(ExplainKey.ACCRUAL_PERIODS, child => explainPresentValue(accrualPeriod, paymentPeriod.DayCount, currency, notional, provider, child)); } builder.put(ExplainKey.COMPOUNDING, paymentPeriod.CompoundingMethod); builder.put(ExplainKey.DISCOUNT_FACTOR, provider.discountFactor(currency, paymentDate)); builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.of(currency, forecastValue(paymentPeriod, provider))); builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.of(currency, presentValue(paymentPeriod, provider))); } }
// 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); }
//------------------------------------------------------------------------- public virtual void explainPresentValue(NotionalExchange @event, RatesProvider provider, ExplainMapBuilder builder) { Currency currency = @event.Currency; LocalDate paymentDate = @event.PaymentDate; builder.put(ExplainKey.ENTRY_TYPE, "NotionalExchange"); builder.put(ExplainKey.PAYMENT_DATE, paymentDate); builder.put(ExplainKey.PAYMENT_CURRENCY, currency); builder.put(ExplainKey.TRADE_NOTIONAL, @event.PaymentAmount); if (paymentDate.isBefore(provider.ValuationDate)) { builder.put(ExplainKey.COMPLETED, true); builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.zero(currency)); builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.zero(currency)); } else { builder.put(ExplainKey.DISCOUNT_FACTOR, provider.discountFactor(currency, paymentDate)); builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.of(currency, forecastValue(@event, provider))); builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.of(currency, presentValue(@event, provider))); } }