public void test_builder_twoAccrualPeriods() { RatePaymentPeriod test = RatePaymentPeriod.builder() .paymentDate(DATE_2014_10_01) .accrualPeriods(RAP1, RAP2) .dayCount(ACT_365F) .currency(GBP) .notional(1000d) .compoundingMethod(CompoundingMethod.STRAIGHT) .build(); assertEquals(test.getStartDate(), DATE_2014_03_30); assertEquals(test.getEndDate(), DATE_2014_09_30); assertEquals(test.getPaymentDate(), DATE_2014_10_01); assertEquals(test.getAccrualPeriods(), ImmutableList.of(RAP1, RAP2)); assertEquals(test.getCurrency(), GBP); assertEquals(test.getFxReset(), Optional.empty()); assertEquals(test.getNotional(), 1000d, 0d); assertEquals(test.getCompoundingMethod(), CompoundingMethod.STRAIGHT); assertEquals(test.isCompoundingApplicable(), true); }
/** * Restricted copy constructor. * @param beanToCopy the bean to copy from, not null */ private Builder(RatePaymentPeriod beanToCopy) { this.paymentDate = beanToCopy.getPaymentDate(); this.accrualPeriods = beanToCopy.getAccrualPeriods(); this.dayCount = beanToCopy.getDayCount(); this.currency = beanToCopy.getCurrency(); this.fxReset = beanToCopy.fxReset; this.notional = beanToCopy.getNotional(); this.compoundingMethod = beanToCopy.getCompoundingMethod(); }
@Override public double presentValue(RatePaymentPeriod period, RatesProvider provider) { // forecastValue * discountFactor double df = provider.discountFactor(period.getCurrency(), period.getPaymentDate()); return forecastValue(period, provider) * df; }
private double pvbpCompoundedFlat(RatePaymentPeriod paymentPeriod, RatesProvider provider) { int nbCmp = paymentPeriod.getAccrualPeriods().size(); double[] rate = paymentPeriod.getAccrualPeriods().stream() .mapToDouble(ap -> rawRate(ap, provider)) .toArray(); double df = provider.discountFactor(paymentPeriod.getCurrency(), paymentPeriod.getPaymentDate()); double rBar = 1.0; double[] cpaAccumulatedBar = new double[nbCmp + 1]; cpaAccumulatedBar[nbCmp] = paymentPeriod.getNotional() * df * rBar; double spreadBar = 0.0d; for (int j = nbCmp - 1; j >= 0; j--) { cpaAccumulatedBar[j] = (1.0d + paymentPeriod.getAccrualPeriods().get(j).getYearFraction() * rate[j] * paymentPeriod.getAccrualPeriods().get(j).getGearing()) * cpaAccumulatedBar[j + 1]; spreadBar += paymentPeriod.getAccrualPeriods().get(j).getYearFraction() * cpaAccumulatedBar[j + 1]; } return spreadBar; }
@Override public double pvbp(RatePaymentPeriod paymentPeriod, RatesProvider provider) { ArgChecker.isTrue(!paymentPeriod.getFxReset().isPresent(), "FX reset is not supported"); int accPeriodCount = paymentPeriod.getAccrualPeriods().size(); ArgChecker.isTrue(accPeriodCount == 1 || paymentPeriod.getCompoundingMethod().equals(CompoundingMethod.FLAT), "Only one accrued period or Flat compounding supported"); // no compounding if (accPeriodCount == 1) { RateAccrualPeriod accrualPeriod = paymentPeriod.getAccrualPeriods().get(0); double df = provider.discountFactor(paymentPeriod.getCurrency(), paymentPeriod.getPaymentDate()); return df * accrualPeriod.getYearFraction() * paymentPeriod.getNotional(); } else { // Flat compounding switch (paymentPeriod.getCompoundingMethod()) { case FLAT: return pvbpCompoundedFlat(paymentPeriod, provider); default: throw new UnsupportedOperationException("PVBP not implemented yet for non FLAT compounding"); } } }
public void test_pvbp_onePeriod() { RatesProvider mockProv = mock(RatesProvider.class); double df = 0.99d; when(mockProv.discountFactor(USD, FIXED_RATE_PAYMENT_PERIOD_PAY_USD.getPaymentDate())) .thenReturn(df); double expected = df * FIXED_RATE_PAYMENT_PERIOD_PAY_USD.getNotional() * FIXED_RATE_PAYMENT_PERIOD_PAY_USD.getAccrualPeriods().get(0).getYearFraction(); DiscountingSwapLegPricer test = DiscountingSwapLegPricer.DEFAULT; assertEquals(test.pvbp(FIXED_SWAP_LEG_PAY_USD, mockProv), expected, TOLERANCE); }
@Override public void explainPresentValue(RatePaymentPeriod paymentPeriod, RatesProvider provider, ExplainMapBuilder builder) { Currency currency = paymentPeriod.getCurrency(); LocalDate paymentDate = paymentPeriod.getPaymentDate(); double notional = paymentPeriod.getNotional() * 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.getNotionalAmount()); if (paymentDate.isBefore(provider.getValuationDate())) { builder.put(ExplainKey.COMPLETED, Boolean.TRUE); builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.zero(currency)); } else { paymentPeriod.getFxReset().ifPresent(fxReset -> { builder.addListEntry(ExplainKey.OBSERVATIONS, child -> { child.put(ExplainKey.ENTRY_TYPE, "FxObservation"); }); }); for (RateAccrualPeriod accrualPeriod : paymentPeriod.getAccrualPeriods()) { builder.addListEntry( ExplainKey.ACCRUAL_PERIODS, child -> explainPresentValue(accrualPeriod, paymentPeriod.getDayCount(), currency, notional, provider, child)); builder.put(ExplainKey.COMPOUNDING, paymentPeriod.getCompoundingMethod()); builder.put(ExplainKey.DISCOUNT_FACTOR, provider.discountFactor(currency, paymentDate)); builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.of(currency, forecastValue(paymentPeriod, provider)));
/** * Computes cash flow equivalent and sensitivity of fixed leg. * <p> * The return type is a map of {@code NotionalExchange} and {@code PointSensitivityBuilder}. * * @param fixedLeg the fixed leg * @param ratesProvider the rates provider * @return the cash flow equivalent and sensitivity */ public static ImmutableMap<Payment, PointSensitivityBuilder> cashFlowEquivalentAndSensitivityFixedLeg( ResolvedSwapLeg fixedLeg, RatesProvider ratesProvider) { ArgChecker.isTrue(fixedLeg.getType().equals(SwapLegType.FIXED), "Leg type should be FIXED"); ArgChecker.isTrue(fixedLeg.getPaymentEvents().isEmpty(), "PaymentEvent should be empty"); Map<Payment, PointSensitivityBuilder> res = new HashMap<Payment, PointSensitivityBuilder>(); for (SwapPaymentPeriod paymentPeriod : fixedLeg.getPaymentPeriods()) { ArgChecker.isTrue(paymentPeriod instanceof RatePaymentPeriod, "rate payment should be RatePaymentPeriod"); RatePaymentPeriod ratePaymentPeriod = (RatePaymentPeriod) paymentPeriod; ArgChecker.isTrue(ratePaymentPeriod.getAccrualPeriods().size() == 1, "rate payment should not be compounding"); RateAccrualPeriod rateAccrualPeriod = ratePaymentPeriod.getAccrualPeriods().get(0); double factor = rateAccrualPeriod.getYearFraction() * ((FixedRateComputation) rateAccrualPeriod.getRateComputation()).getRate(); CurrencyAmount notional = ratePaymentPeriod.getNotionalAmount().multipliedBy(factor); LocalDate paymentDate = ratePaymentPeriod.getPaymentDate(); Payment pay = Payment.of(notional, paymentDate); res.put(pay, PointSensitivityBuilder.none()); } return ImmutableMap.copyOf(res); }
private double accrualWithNotional(RatePaymentPeriod period, double notional, RatesProvider provider) { // handle simple case and more complex compounding for whole payment period if (period.getAccrualPeriods().size() == 1) { RateAccrualPeriod accrualPeriod = period.getAccrualPeriods().get(0); return unitNotionalAccrual(accrualPeriod, accrualPeriod.getSpread(), provider) * notional; } return accrueCompounded(period, notional, provider); }
@Override public double accruedInterest(RatePaymentPeriod period, RatesProvider provider) { LocalDate valDate = provider.getValuationDate(); if (valDate.compareTo(period.getStartDate()) <= 0 || valDate.compareTo(period.getEndDate()) > 0) { return 0d; } ImmutableList.Builder<RateAccrualPeriod> truncated = ImmutableList.builder(); for (RateAccrualPeriod rap : period.getAccrualPeriods()) { if (valDate.compareTo(rap.getEndDate()) > 0) { truncated.add(rap); } else { truncated.add(rap.toBuilder() .endDate(provider.getValuationDate()) .unadjustedEndDate(provider.getValuationDate()) .yearFraction(period.getDayCount().yearFraction(rap.getStartDate(), provider.getValuationDate())) .build()); break; } } RatePaymentPeriod adjustedPaymentPeriod = period.toBuilder().accrualPeriods(truncated.build()).build(); return forecastValue(adjustedPaymentPeriod, provider); }
/** * Computes the conventional cash annuity from a swap leg. * <p> * The computation is relevant only for standard swaps with constant notional and regular payments. * The swap leg must be a fixed leg. However, this is not checked internally. * * @param fixedLeg the fixed leg of the swap * @param yield the yield * @return the cash annuity */ public double annuityCash(ResolvedSwapLeg fixedLeg, double yield) { int nbFixedPeriod = fixedLeg.getPaymentPeriods().size(); SwapPaymentPeriod paymentPeriod = fixedLeg.getPaymentPeriods().get(0); ArgChecker.isTrue(paymentPeriod instanceof RatePaymentPeriod, "payment period should be RatePaymentPeriod"); RatePaymentPeriod ratePaymentPeriod = (RatePaymentPeriod) paymentPeriod; int nbFixedPaymentYear = (int) Math.round(1d / ratePaymentPeriod.getDayCount().yearFraction(ratePaymentPeriod.getStartDate(), ratePaymentPeriod.getEndDate())); double notional = Math.abs(ratePaymentPeriod.getNotional()); double annuityCash = notional * annuityCash(nbFixedPaymentYear, nbFixedPeriod, yield); return annuityCash; }
@Override public MultiCurrencyAmount currencyExposure(RatePaymentPeriod period, RatesProvider provider) { double df = provider.discountFactor(period.getCurrency(), period.getPaymentDate()); if (period.getFxReset().isPresent()) { FxReset fxReset = period.getFxReset().get(); LocalDate fixingDate = fxReset.getObservation().getFixingDate(); FxIndexRates rates = provider.fxIndexRates(fxReset.getObservation().getIndex()); if (!fixingDate.isAfter(provider.getValuationDate()) && rates.getFixings().get(fixingDate).isPresent()) { double fxRate = rates.rate(fxReset.getObservation(), fxReset.getReferenceCurrency()); return MultiCurrencyAmount.of(period.getCurrency(), accrualWithNotional(period, period.getNotional() * fxRate * df, provider)); } double fxRateSpotSensitivity = rates.getFxForwardRates() .rateFxSpotSensitivity(fxReset.getReferenceCurrency(), fxReset.getObservation().getMaturityDate()); return MultiCurrencyAmount.of(fxReset.getReferenceCurrency(), accrualWithNotional(period, period.getNotional() * fxRateSpotSensitivity * df, provider)); } return MultiCurrencyAmount.of(period.getCurrency(), accrualWithNotional(period, period.getNotional() * df, provider)); }
private PointSensitivityBuilder unitNotionalSensitivityNoCompounding(RatePaymentPeriod period, RatesProvider provider) { Currency ccy = period.getCurrency(); PointSensitivityBuilder sensi = PointSensitivityBuilder.none(); for (RateAccrualPeriod accrualPeriod : period.getAccrualPeriods()) { sensi = sensi.combinedWith(unitNotionalSensitivityAccrual(accrualPeriod, ccy, provider)); } return sensi; }
private List<ZeroRateSensitivity> dscSensitivityFD(RatesProvider provider, RatePaymentPeriod payment, RateComputationFn<RateComputation> obsFunc, double eps) { LocalDate valuationDate = provider.getValuationDate(); LocalDate paymentDate = payment.getPaymentDate(); double discountFactor = provider.discountFactor(payment.getCurrency(), paymentDate); double paymentTime = DAY_COUNT.relativeYearFraction(valuationDate, paymentDate); Currency currency = payment.getCurrency(); when(provDw.discountFactor(currency, paymentDate)).thenReturn(discountFactor * Math.exp(eps * paymentTime)); ImmutableList<RateAccrualPeriod> periods = payment.getAccrualPeriods(); for (int i = 0; i < periods.size(); ++i) { RateComputation observation = periods.get(i).getRateComputation();
@Test(dataProvider = "compoundingRatePaymentPeriod") public void test_forecastValueSensitivity_ibor_compounding(RatePaymentPeriod period) { RatesProvider mockProv = mock(RatesProvider.class); RateComputationFn<RateComputation> obsFunc = mock(RateComputationFn.class); when(mockProv.getValuationDate()).thenReturn(VAL_DATE); DiscountingRatePaymentPeriodPricer pricer = new DiscountingRatePaymentPeriodPricer(obsFunc); LocalDate[] dates = new LocalDate[] {CPN_DATE_1, CPN_DATE_2, CPN_DATE_3, CPN_DATE_4}; double[] rates = new double[] {RATE_1, RATE_2, RATE_3}; for (int i = 0; i < 3; ++i) { IborRateComputation rateObs = (IborRateComputation) period.getAccrualPeriods().get(i).getRateComputation(); IborRateSensitivity iborSense = IborRateSensitivity.of(rateObs.getObservation(), 1.0d); when(obsFunc.rateSensitivity(rateObs, dates[i], dates[i + 1], mockProv)).thenReturn(iborSense); when(obsFunc.rate(rateObs, dates[i], dates[i + 1], mockProv)).thenReturn(rates[i]); } PointSensitivities senseComputed = pricer.forecastValueSensitivity(period, mockProv).build(); List<IborRateSensitivity> senseExpectedList = futureFwdSensitivityFD(mockProv, period, obsFunc, EPS_FD); PointSensitivities senseExpected = PointSensitivities.of(senseExpectedList); assertTrue(senseComputed.equalWithTolerance(senseExpected, EPS_FD * period.getNotional())); }
@Override public PointSensitivityBuilder forecastValueSensitivity(RatePaymentPeriod period, RatesProvider provider) { // historic payments have zero sensi if (period.getPaymentDate().isBefore(provider.getValuationDate())) { return PointSensitivityBuilder.none(); } PointSensitivityBuilder sensiFx = fxRateSensitivity(period, provider); double accrual = accrualWithNotional(period, period.getNotional(), provider); sensiFx = sensiFx.multipliedBy(accrual); PointSensitivityBuilder sensiAccrual = PointSensitivityBuilder.none(); if (period.isCompoundingApplicable()) { sensiAccrual = accrueCompoundedSensitivity(period, provider); } else { sensiAccrual = unitNotionalSensitivityNoCompounding(period, provider); } double notional = period.getNotional() * fxRate(period, provider); sensiAccrual = sensiAccrual.multipliedBy(notional); return sensiFx.combinedWith(sensiAccrual); }
@Override public double forecastValue(RatePaymentPeriod period, RatesProvider provider) { // notional * fxRate // fxRate is 1 if no FX conversion double notional = period.getNotional() * fxRate(period, provider); return accrualWithNotional(period, notional, provider); }
public void test_findNotional() { ResolvedSwapLeg test = ResolvedSwapLeg.builder() .type(IBOR) .payReceive(RECEIVE) .paymentPeriods(RPP1, RPP2) .build(); // Date is before the start date assertEquals(test.findNotional(RPP1.getStartDate().minusMonths(1)), Optional.of(RPP1.getNotionalAmount())); // Date is on the start date assertEquals(test.findNotional(RPP1.getStartDate()), Optional.of(RPP1.getNotionalAmount())); // Date is after the start date assertEquals(test.findNotional(RPP1.getStartDate().plusDays(1)), Optional.of(RPP1.getNotionalAmount())); // Date is before the end date assertEquals(test.findNotional(RPP2.getEndDate().minusDays(1)), Optional.of(RPP2.getNotionalAmount())); // Date is on the end date assertEquals(test.findNotional(RPP2.getEndDate()), Optional.of(RPP2.getNotionalAmount())); // Date is after the end date assertEquals(test.findNotional(RPP2.getEndDate().plusMonths(1)), Optional.of(RPP2.getNotionalAmount())); }
if (ratePaymentPeriod.getCompoundingMethod() == CompoundingMethod.NONE) { return Triple.of(false, 0, 0.0d); // Should be compounded ImmutableList<RateAccrualPeriod> accrualPeriods = ratePaymentPeriod.getAccrualPeriods(); int nbAccrualPeriods = accrualPeriods.size(); double fixedRate = 0;
public void test_findPaymentPeriod() { ResolvedSwapLeg test = ResolvedSwapLeg.builder() .type(IBOR) .payReceive(RECEIVE) .paymentPeriods(RPP1, RPP2) .build(); assertEquals(test.findPaymentPeriod(RPP1.getStartDate()), Optional.empty()); assertEquals(test.findPaymentPeriod(RPP1.getStartDate().plusDays(1)), Optional.of(RPP1)); assertEquals(test.findPaymentPeriod(RPP1.getEndDate()), Optional.of(RPP1)); assertEquals(test.findPaymentPeriod(RPP2.getStartDate()), Optional.of(RPP1)); assertEquals(test.findPaymentPeriod(RPP2.getStartDate().plusDays(1)), Optional.of(RPP2)); assertEquals(test.findPaymentPeriod(RPP2.getEndDate()), Optional.of(RPP2)); assertEquals(test.findPaymentPeriod(RPP2.getEndDate().plusDays(1)), Optional.empty()); }