From 949a85814153f12538b0d7917ff7873ec819ad42 Mon Sep 17 00:00:00 2001 From: Julius Roeder Date: Fri, 14 Apr 2023 09:12:27 +0200 Subject: [PATCH 1/3] Cashflow for bonds calculation --- .../FixedCouponBondMeasureCalculations.java | 28 ++++++++++++++ ...xedCouponBondTradeCalculationFunction.java | 2 + .../FixedCouponBondTradeCalculationsTest.java | 15 ++++++++ ...scountingFixedCouponBondProductPricer.java | 38 +++++++++++++++++++ ...DiscountingFixedCouponBondTradePricer.java | 30 +++++++++++++++ ...ntingFixedCouponBondProductPricerTest.java | 11 ++++++ 6 files changed, 124 insertions(+) diff --git a/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondMeasureCalculations.java b/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondMeasureCalculations.java index 59e7367c11..c3d449eb6f 100644 --- a/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondMeasureCalculations.java +++ b/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondMeasureCalculations.java @@ -11,12 +11,19 @@ import com.opengamma.strata.data.scenario.CurrencyScenarioArray; import com.opengamma.strata.data.scenario.MultiCurrencyScenarioArray; import com.opengamma.strata.data.scenario.ScenarioArray; +import com.opengamma.strata.market.amount.CashFlows; import com.opengamma.strata.market.param.CurrencyParameterSensitivities; import com.opengamma.strata.market.sensitivity.PointSensitivities; +import com.opengamma.strata.measure.rate.RatesScenarioMarketData; +import com.opengamma.strata.pricer.DiscountFactors; import com.opengamma.strata.pricer.bond.DiscountingFixedCouponBondTradePricer; import com.opengamma.strata.pricer.bond.LegalEntityDiscountingProvider; +import com.opengamma.strata.pricer.bond.RepoCurveDiscountFactors; +import com.opengamma.strata.pricer.rate.RatesProvider; import com.opengamma.strata.pricer.sensitivity.MarketQuoteSensitivityCalculator; +import com.opengamma.strata.product.bond.ResolvedFixedCouponBond; import com.opengamma.strata.product.bond.ResolvedFixedCouponBondTrade; +import com.opengamma.strata.product.payment.ResolvedBulletPaymentTrade; /** * Multi-scenario measure calculations for fixed coupon bond trades. @@ -193,4 +200,25 @@ CurrencyAmount currentCash( return tradePricer.currentCash(trade, discountingProvider.getValuationDate()); } + //------------------------------------------------------------------------- + // calculates cashflows for all scenarios + ScenarioArray cashFlows( + ResolvedFixedCouponBondTrade trade, + LegalEntityDiscountingScenarioMarketData marketData) { + + return ScenarioArray.of( + marketData.getScenarioCount(), + i -> cashFlows(trade, marketData.scenario(i).discountingProvider())); + } + + // cashflows for one scenario + CashFlows cashFlows(ResolvedFixedCouponBondTrade trade, + LegalEntityDiscountingProvider ratesProvider) { + + DiscountFactors discountingProvider = ratesProvider + .repoCurveDiscountFactors(trade.getProduct().getLegalEntityId(), trade.getProduct().getCurrency()) + .getDiscountFactors(); + return tradePricer.cashFlows(trade, discountingProvider); + } + } diff --git a/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationFunction.java b/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationFunction.java index eba5f8bff5..dde1158d53 100644 --- a/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationFunction.java +++ b/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationFunction.java @@ -45,6 +45,7 @@ *
  • {@linkplain Measures#PV01_MARKET_QUOTE_BUCKETED PV01 market quote bucketed} *
  • {@linkplain Measures#CURRENCY_EXPOSURE Currency exposure} *
  • {@linkplain Measures#CURRENT_CASH Current cash} + *
  • {@linkplain Measures#CASH_FLOWS Cash flows} *
  • {@linkplain Measures#RESOLVED_TARGET Resolved trade} * * @@ -82,6 +83,7 @@ public class FixedCouponBondTradeCalculationFunction rt) .build(); diff --git a/modules/measure/src/test/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationsTest.java b/modules/measure/src/test/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationsTest.java index 890a252c4f..a48a67fc93 100644 --- a/modules/measure/src/test/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationsTest.java +++ b/modules/measure/src/test/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationsTest.java @@ -18,6 +18,7 @@ import com.opengamma.strata.data.scenario.MultiCurrencyScenarioArray; import com.opengamma.strata.data.scenario.ScenarioArray; import com.opengamma.strata.data.scenario.ScenarioMarketData; +import com.opengamma.strata.market.amount.CashFlows; import com.opengamma.strata.market.param.CurrencyParameterSensitivities; import com.opengamma.strata.market.sensitivity.PointSensitivities; import com.opengamma.strata.pricer.bond.DiscountingFixedCouponBondTradePricer; @@ -91,4 +92,18 @@ public void test_pv01_quote() { assertThat(bucketedComputed.get(0).equalWithTolerance(expectedPv01CalBucketed, 1.0e-10)).isTrue(); } + @Test + public void test_cashflows() { + ScenarioMarketData md = FixedCouponBondTradeCalculationFunctionTest.marketData(); + LegalEntityDiscountingProvider provider = LOOKUP.marketDataView(md.scenario(0)).discountingProvider(); + + FixedCouponBondMeasureCalculations pricer = FixedCouponBondMeasureCalculations.DEFAULT; + CashFlows cashFlows = pricer.cashFlows(RTRADE, provider); + + DiscountingFixedCouponBondTradePricer pricer2 = DiscountingFixedCouponBondTradePricer.DEFAULT; + double pv = cashFlows.getCashFlows().stream().map(c -> c.getPresentValue().getAmount()).reduce(0.0, Double::sum); + CurrencyAmount expectedPv = pricer2.presentValue(RTRADE, provider); +// assertThat(expectedPv.getAmount()).isCloseTo(pv, offset(1.0e-12)); + } + } diff --git a/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricer.java b/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricer.java index da3c60c614..c3d968dedc 100644 --- a/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricer.java +++ b/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricer.java @@ -12,7 +12,10 @@ import static com.opengamma.strata.product.bond.FixedCouponBondYieldConvention.US_STREET; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.function.Function; +import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; import com.opengamma.strata.basics.ReferenceData; @@ -21,11 +24,14 @@ import com.opengamma.strata.basics.value.ValueDerivatives; import com.opengamma.strata.collect.ArgChecker; import com.opengamma.strata.collect.array.DoubleArray; +import com.opengamma.strata.market.amount.CashFlow; +import com.opengamma.strata.market.amount.CashFlows; import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder; import com.opengamma.strata.math.impl.rootfinding.BracketRoot; import com.opengamma.strata.math.impl.rootfinding.BrentSingleRootFinder; import com.opengamma.strata.math.impl.rootfinding.RealSingleRootFinder; import com.opengamma.strata.pricer.CompoundedRateType; +import com.opengamma.strata.pricer.DiscountFactors; import com.opengamma.strata.pricer.DiscountingPaymentPricer; import com.opengamma.strata.pricer.ZeroRateSensitivity; import com.opengamma.strata.product.Security; @@ -1211,4 +1217,36 @@ static IssuerCurveDiscountFactors issuerCurveDf(ResolvedFixedCouponBond bond, Le return provider.issuerCurveDiscountFactors(bond.getLegalEntityId(), bond.getCurrency()); } + //------------------------------------------------------------------------- + /** + * Calculates the future cash flow of the bond. + *

    + * There are two cash flows on the final date. + * + * @param bond the trade + * @param discountFactor the provider + * @return List of cash flows of a single trade + */ + public List cashFlows(ResolvedFixedCouponBond bond, DiscountFactors discountFactor) { + ImmutableList bondPayments = bond.getPeriodicPayments(); + List listCashFlow = new ArrayList<>(); + + for (int i = 0; i < bondPayments.size(); ++i) { + FixedCouponBondPaymentPeriod payment = bondPayments.get(i); + LocalDate date = payment.getPaymentDate(); + if (date.isAfter(discountFactor.getValuationDate())) { + CurrencyAmount amount = CurrencyAmount.of(payment.getCurrency(), payment.getNotional() + * payment.getYearFraction() * payment.getFixedRate()); + listCashFlow.add(CashFlow.ofForecastValue(date, amount, discountFactor.discountFactor(date))); + } + } + + if (bond.getNominalPayment().getDate().isAfter(discountFactor.getValuationDate())) { + listCashFlow.add(CashFlow.ofForecastValue(bond.getNominalPayment().getDate(), + CurrencyAmount.of(bond.getNominalPayment().getCurrency(), bond.getNominalPayment().getAmount()), + discountFactor.discountFactor(bond.getNominalPayment().getDate()))); + } + return listCashFlow; + } + } diff --git a/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondTradePricer.java b/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondTradePricer.java index 66afdbfc90..7e8728a904 100644 --- a/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondTradePricer.java +++ b/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondTradePricer.java @@ -6,6 +6,9 @@ package com.opengamma.strata.pricer.bond; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; import com.opengamma.strata.basics.ReferenceData; import com.opengamma.strata.basics.currency.Currency; @@ -13,9 +16,12 @@ import com.opengamma.strata.basics.currency.MultiCurrencyAmount; import com.opengamma.strata.basics.currency.Payment; import com.opengamma.strata.collect.ArgChecker; +import com.opengamma.strata.market.amount.CashFlow; +import com.opengamma.strata.market.amount.CashFlows; import com.opengamma.strata.market.sensitivity.PointSensitivities; import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder; import com.opengamma.strata.pricer.CompoundedRateType; +import com.opengamma.strata.pricer.DiscountFactors; import com.opengamma.strata.pricer.DiscountingPaymentPricer; import com.opengamma.strata.pricer.ZeroRateSensitivity; import com.opengamma.strata.product.bond.FixedCouponBondPaymentPeriod; @@ -449,4 +455,28 @@ public LocalDate settlementDate(ResolvedFixedCouponBondTrade trade, LocalDate va .orElse(valuationDate); } + //------------------------------------------------------------------------- + /** + * Calculates the future cash flow of the bullet payment trade. + *

    + * There is only one cash flow on the payment date for the bullet payment trade. + * + * @param trade the trade + * @param discountFactors the provider + * @return the cash flows + */ + public CashFlows cashFlows(ResolvedFixedCouponBondTrade trade, DiscountFactors discountFactors) { + List listCashFlow = productPricer.cashFlows(trade.getProduct(), discountFactors); + double quantity = trade.getQuantity(); + + List newListCashFlow = new ArrayList<>(); + + for (CashFlow tempCashflow : listCashFlow) { + newListCashFlow.add(CashFlow.ofForecastValue(tempCashflow.getPaymentDate(), + tempCashflow.getForecastValue().multipliedBy(quantity), tempCashflow.getDiscountFactor())); + } + + return CashFlows.of(newListCashFlow); + } + } diff --git a/modules/pricer/src/test/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricerTest.java b/modules/pricer/src/test/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricerTest.java index 8d8716529c..01ebdd8ba6 100644 --- a/modules/pricer/src/test/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricerTest.java +++ b/modules/pricer/src/test/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricerTest.java @@ -43,6 +43,7 @@ import com.opengamma.strata.basics.value.ValueDerivatives; import com.opengamma.strata.collect.array.DoubleArray; import com.opengamma.strata.collect.tuple.Pair; +import com.opengamma.strata.market.amount.CashFlow; import com.opengamma.strata.market.curve.CurveMetadata; import com.opengamma.strata.market.curve.CurveName; import com.opengamma.strata.market.curve.Curves; @@ -167,6 +168,16 @@ public void test_presentValue() { assertThat(computed.getAmount()).isCloseTo(expected.getAmount(), offset(NOTIONAL * TOL)); } + @Test + public void test_cashFlows() { + List cashFlows = PRICER.cashFlows(PRODUCT, DSC_FACTORS_ISSUER); + + // + double pv = cashFlows.stream().map(c -> c.getPresentValue().getAmount()).reduce(0.0, Double::sum); + CurrencyAmount computed = PRICER.presentValue(PRODUCT, PROVIDER); + assertThat(computed.getAmount()).isCloseTo(pv, offset(NOTIONAL * TOL)); + } + @Test public void test_presentValueWithZSpread_continuous() { CurrencyAmount computed = PRICER.presentValueWithZSpread(PRODUCT, PROVIDER, Z_SPREAD, CONTINUOUS, 0); From cc18f465373bfa5e1dc91634f1a29abc8ecb335f Mon Sep 17 00:00:00 2001 From: Julius Roeder Date: Wed, 26 Apr 2023 16:32:41 +0200 Subject: [PATCH 2/3] Corrected Bond Cashflows and finalised test --- .../FixedCouponBondMeasureCalculations.java | 2 +- .../FixedCouponBondTradeCalculationsTest.java | 19 ++++++++++++++++--- ...scountingFixedCouponBondProductPricer.java | 9 ++++++--- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondMeasureCalculations.java b/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondMeasureCalculations.java index c3d449eb6f..1b75e6b159 100644 --- a/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondMeasureCalculations.java +++ b/modules/measure/src/main/java/com/opengamma/strata/measure/bond/FixedCouponBondMeasureCalculations.java @@ -216,7 +216,7 @@ CashFlows cashFlows(ResolvedFixedCouponBondTrade trade, LegalEntityDiscountingProvider ratesProvider) { DiscountFactors discountingProvider = ratesProvider - .repoCurveDiscountFactors(trade.getProduct().getLegalEntityId(), trade.getProduct().getCurrency()) + .issuerCurveDiscountFactors(trade.getProduct().getLegalEntityId(), trade.getProduct().getCurrency()) .getDiscountFactors(); return tradePricer.cashFlows(trade, discountingProvider); } diff --git a/modules/measure/src/test/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationsTest.java b/modules/measure/src/test/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationsTest.java index a48a67fc93..abca99de99 100644 --- a/modules/measure/src/test/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationsTest.java +++ b/modules/measure/src/test/java/com/opengamma/strata/measure/bond/FixedCouponBondTradeCalculationsTest.java @@ -8,12 +8,14 @@ import static com.opengamma.strata.basics.currency.Currency.GBP; import static org.assertj.core.api.Assertions.assertThat; +import org.assertj.core.data.Offset; import org.junit.jupiter.api.Test; import com.google.common.collect.ImmutableList; import com.google.common.math.DoubleMath; import com.opengamma.strata.basics.currency.CurrencyAmount; import com.opengamma.strata.basics.currency.MultiCurrencyAmount; +import com.opengamma.strata.basics.currency.Payment; import com.opengamma.strata.data.scenario.CurrencyScenarioArray; import com.opengamma.strata.data.scenario.MultiCurrencyScenarioArray; import com.opengamma.strata.data.scenario.ScenarioArray; @@ -21,8 +23,10 @@ import com.opengamma.strata.market.amount.CashFlows; import com.opengamma.strata.market.param.CurrencyParameterSensitivities; import com.opengamma.strata.market.sensitivity.PointSensitivities; +import com.opengamma.strata.pricer.DiscountingPaymentPricer; import com.opengamma.strata.pricer.bond.DiscountingFixedCouponBondTradePricer; import com.opengamma.strata.pricer.bond.LegalEntityDiscountingProvider; +import com.opengamma.strata.pricer.bond.RepoCurveDiscountFactors; import com.opengamma.strata.pricer.sensitivity.MarketQuoteSensitivityCalculator; import com.opengamma.strata.product.bond.ResolvedFixedCouponBondTrade; @@ -94,16 +98,25 @@ public void test_pv01_quote() { @Test public void test_cashflows() { + DiscountingPaymentPricer paymentPricer = new DiscountingPaymentPricer(); ScenarioMarketData md = FixedCouponBondTradeCalculationFunctionTest.marketData(); LegalEntityDiscountingProvider provider = LOOKUP.marketDataView(md.scenario(0)).discountingProvider(); - FixedCouponBondMeasureCalculations pricer = FixedCouponBondMeasureCalculations.DEFAULT; CashFlows cashFlows = pricer.cashFlows(RTRADE, provider); + double cashFlowsPV = cashFlows.getCashFlows() + .stream() + .map(c -> c.getPresentValue().getAmount()) + .reduce(0.0, Double::sum); + RepoCurveDiscountFactors df = provider.repoCurveDiscountFactors(RTRADE.getProduct().getSecurityId(), + RTRADE.getProduct().getLegalEntityId(), RTRADE.getProduct().getCurrency()); DiscountingFixedCouponBondTradePricer pricer2 = DiscountingFixedCouponBondTradePricer.DEFAULT; - double pv = cashFlows.getCashFlows().stream().map(c -> c.getPresentValue().getAmount()).reduce(0.0, Double::sum); + Payment test = pricer2.upfrontPayment(RTRADE); + CurrencyAmount presentValuePayment = paymentPricer.presentValue(test, df.getDiscountFactors()); + cashFlowsPV += presentValuePayment.getAmount(); + CurrencyAmount expectedPv = pricer2.presentValue(RTRADE, provider); -// assertThat(expectedPv.getAmount()).isCloseTo(pv, offset(1.0e-12)); + assertThat(expectedPv.getAmount()).isCloseTo(cashFlowsPV, Offset.offset(1.0e-10)); } } diff --git a/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricer.java b/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricer.java index c3d968dedc..c4c75e3aa6 100644 --- a/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricer.java +++ b/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondProductPricer.java @@ -1234,14 +1234,17 @@ public List cashFlows(ResolvedFixedCouponBond bond, DiscountFactors di for (int i = 0; i < bondPayments.size(); ++i) { FixedCouponBondPaymentPeriod payment = bondPayments.get(i); LocalDate date = payment.getPaymentDate(); - if (date.isAfter(discountFactor.getValuationDate())) { + if (date.isAfter(discountFactor.getValuationDate()) || date.isEqual(discountFactor.getValuationDate())) { CurrencyAmount amount = CurrencyAmount.of(payment.getCurrency(), payment.getNotional() * payment.getYearFraction() * payment.getFixedRate()); - listCashFlow.add(CashFlow.ofForecastValue(date, amount, discountFactor.discountFactor(date))); + if (amount.getAmount() != 0) { + listCashFlow.add(CashFlow.ofForecastValue(date, amount, discountFactor.discountFactor(date))); + } } } - if (bond.getNominalPayment().getDate().isAfter(discountFactor.getValuationDate())) { + if (bond.getNominalPayment().getDate().isAfter(discountFactor.getValuationDate()) + || bond.getNominalPayment().getDate().isEqual(discountFactor.getValuationDate())) { listCashFlow.add(CashFlow.ofForecastValue(bond.getNominalPayment().getDate(), CurrencyAmount.of(bond.getNominalPayment().getCurrency(), bond.getNominalPayment().getAmount()), discountFactor.discountFactor(bond.getNominalPayment().getDate()))); From 94de7422372ea98417b49aac95d3a68e5913e75f Mon Sep 17 00:00:00 2001 From: Julius Roeder Date: Wed, 26 Apr 2023 16:40:52 +0200 Subject: [PATCH 3/3] Fix documentation --- .../pricer/bond/DiscountingFixedCouponBondTradePricer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondTradePricer.java b/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondTradePricer.java index 7e8728a904..d07e4c8b67 100644 --- a/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondTradePricer.java +++ b/modules/pricer/src/main/java/com/opengamma/strata/pricer/bond/DiscountingFixedCouponBondTradePricer.java @@ -457,9 +457,9 @@ public LocalDate settlementDate(ResolvedFixedCouponBondTrade trade, LocalDate va //------------------------------------------------------------------------- /** - * Calculates the future cash flow of the bullet payment trade. + * Calculates the future cash flow of the fixed coupon trade. *

    - * There is only one cash flow on the payment date for the bullet payment trade. + * There are two cash flows on the final date. * * @param trade the trade * @param discountFactors the provider