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..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 @@ -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 + .issuerCurveDiscountFactors(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..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,20 +8,25 @@ 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; 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.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; @@ -91,4 +96,27 @@ public void test_pv01_quote() { assertThat(bucketedComputed.get(0).equalWithTolerance(expectedPv01CalBucketed, 1.0e-10)).isTrue(); } + @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; + 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(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 da3c60c614..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 @@ -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,39 @@ 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()) || date.isEqual(discountFactor.getValuationDate())) { + CurrencyAmount amount = CurrencyAmount.of(payment.getCurrency(), payment.getNotional() + * payment.getYearFraction() * payment.getFixedRate()); + if (amount.getAmount() != 0) { + listCashFlow.add(CashFlow.ofForecastValue(date, amount, discountFactor.discountFactor(date))); + } + } + } + + 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()))); + } + 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..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 @@ -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 fixed coupon trade. + *

    + * There are two cash flows on the final date. + * + * @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);