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"); } } }
private double accrualWithNotional(RatePaymentPeriod period, double notional, RatesProvider provider) { // handle simple case and more complex compounding for whole payment period if (period.AccrualPeriods.size() == 1) { RateAccrualPeriod accrualPeriod = period.AccrualPeriods.get(0); return(unitNotionalAccrual(accrualPeriod, accrualPeriod.Spread, provider) * notional); } return(accrueCompounded(period, notional, 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); }
// explain PV for an accrual period, ignoring compounding private void explainPresentValue(RateAccrualPeriod accrualPeriod, DayCount dayCount, Currency currency, double notional, RatesProvider provider, ExplainMapBuilder builder) { double rawRate = rateComputationFn.explainRate(accrualPeriod.RateComputation, accrualPeriod.StartDate, accrualPeriod.EndDate, provider, builder); double payOffRate = rawRate * accrualPeriod.Gearing + accrualPeriod.Spread; double ua = unitNotionalAccrual(accrualPeriod, accrualPeriod.Spread, provider); // Note that the forecast value is not published since this is potentially misleading when // compounding is being applied, and when it isn't then it's the same as the forecast // value of the payment period. builder.put(ExplainKey.ENTRY_TYPE, "AccrualPeriod"); builder.put(ExplainKey.START_DATE, accrualPeriod.StartDate); builder.put(ExplainKey.UNADJUSTED_START_DATE, accrualPeriod.UnadjustedStartDate); builder.put(ExplainKey.END_DATE, accrualPeriod.EndDate); builder.put(ExplainKey.UNADJUSTED_END_DATE, accrualPeriod.UnadjustedEndDate); builder.put(ExplainKey.ACCRUAL_YEAR_FRACTION, accrualPeriod.YearFraction); builder.put(ExplainKey.ACCRUAL_DAYS, dayCount.days(accrualPeriod.StartDate, accrualPeriod.EndDate)); builder.put(ExplainKey.DAYS, (int)DAYS.between(accrualPeriod.StartDate, accrualPeriod.EndDate)); builder.put(ExplainKey.GEARING, accrualPeriod.Gearing); builder.put(ExplainKey.SPREAD, accrualPeriod.Spread); builder.put(ExplainKey.PAY_OFF_RATE, accrualPeriod.NegativeRateMethod.adjust(payOffRate)); builder.put(ExplainKey.UNIT_AMOUNT, ua); }
public virtual void currency_exposure_USD() { 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), USD)).notional(notional).paymentDate(endDate).dayCount(DayCounts.ONE_ONE).currency(GBP).build(); // 1_000_000 USD paid in GBP at maturity PointSensitivityBuilder pts = PERIOD_PRICER.presentValueSensitivity(fixedFx, PROVIDER); MultiCurrencyAmount ceComputed = PERIOD_PRICER.currencyExposure(fixedFx, PROVIDER); double dfUsd = PROVIDER.discountFactor(USD, endDate); double ceUsdExpected = notional * yearFraction * rate * dfUsd; assertEquals(ceComputed.getAmount(USD).Amount, ceUsdExpected, 1.0E-6); MultiCurrencyAmount ceWithoutPvComputed = PROVIDER.currencyExposure(pts.build().convertedTo(USD, PROVIDER)); CurrencyAmount pvComputed = CurrencyAmount.of(GBP, PERIOD_PRICER.presentValue(fixedFx, PROVIDER)); MultiCurrencyAmount ceComputed2 = ceWithoutPvComputed.plus(pvComputed); assertEquals(ceComputed2.getAmount(USD).Amount, ceUsdExpected, TOLERANCE); assertEquals(ceComputed2.getAmount(GBP).Amount, 0.0, TOLERANCE); }
// computes the sensitivity of the accrual period to the rate observations (not to discount factors) private PointSensitivityBuilder unitNotionalSensitivityAccrual(RateAccrualPeriod period, Currency ccy, RatesProvider provider) { PointSensitivityBuilder sensi = rateComputationFn.rateSensitivity(period.RateComputation, period.StartDate, period.EndDate, provider); return(sensi.multipliedBy(period.Gearing * period.YearFraction)); }
// finds the raw rate for the accrual period // the raw rate is the rate before gearing, spread and negative checks are applied private double rawRate(RateAccrualPeriod accrualPeriod, RatesProvider provider) { return(rateComputationFn.rate(accrualPeriod.RateComputation, accrualPeriod.StartDate, accrualPeriod.EndDate, provider)); }
// calculate the accrual for a unit notional from the raw rate private double unitNotionalAccrualRaw(RateAccrualPeriod accrualPeriod, double rawRate, double spread) { double treatedRate = rawRate * accrualPeriod.Gearing + spread; return(accrualPeriod.NegativeRateMethod.adjust(treatedRate * accrualPeriod.YearFraction)); }
// calculate the accrual for a unit notional private double unitNotionalAccrual(RateAccrualPeriod accrualPeriod, double spread, RatesProvider provider) { double rawRate = this.rawRate(accrualPeriod, provider); return(unitNotionalAccrualRaw(accrualPeriod, rawRate, spread)); }