Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/bonds cashflow #2565

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -193,4 +200,25 @@ CurrencyAmount currentCash(
return tradePricer.currentCash(trade, discountingProvider.getValuationDate());
}

//-------------------------------------------------------------------------
// calculates cashflows for all scenarios
ScenarioArray<CashFlows> 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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
* <li>{@linkplain Measures#PV01_MARKET_QUOTE_BUCKETED PV01 market quote bucketed}
* <li>{@linkplain Measures#CURRENCY_EXPOSURE Currency exposure}
* <li>{@linkplain Measures#CURRENT_CASH Current cash}
* <li>{@linkplain Measures#CASH_FLOWS Cash flows}
* <li>{@linkplain Measures#RESOLVED_TARGET Resolved trade}
* </ul>
*
Expand Down Expand Up @@ -82,6 +83,7 @@ public class FixedCouponBondTradeCalculationFunction<T extends SecuritizedProduc
.put(Measures.PV01_MARKET_QUOTE_BUCKETED, FixedCouponBondMeasureCalculations.DEFAULT::pv01MarketQuoteBucketed)
.put(Measures.CURRENCY_EXPOSURE, FixedCouponBondMeasureCalculations.DEFAULT::currencyExposure)
.put(Measures.CURRENT_CASH, FixedCouponBondMeasureCalculations.DEFAULT::currentCash)
.put(Measures.CASH_FLOWS, FixedCouponBondMeasureCalculations.DEFAULT::cashFlows)
.put(Measures.RESOLVED_TARGET, (rt, smd) -> rt)
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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.
* <p>
* 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<CashFlow> cashFlows(ResolvedFixedCouponBond bond, DiscountFactors discountFactor) {
ImmutableList<FixedCouponBondPaymentPeriod> bondPayments = bond.getPeriodicPayments();
List<CashFlow> 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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@
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;
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.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;
Expand Down Expand Up @@ -449,4 +455,28 @@ public LocalDate settlementDate(ResolvedFixedCouponBondTrade trade, LocalDate va
.orElse(valuationDate);
}

//-------------------------------------------------------------------------
/**
* Calculates the future cash flow of the fixed coupon trade.
* <p>
* 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<CashFlow> listCashFlow = productPricer.cashFlows(trade.getProduct(), discountFactors);
double quantity = trade.getQuantity();

List<CashFlow> newListCashFlow = new ArrayList<>();

for (CashFlow tempCashflow : listCashFlow) {
newListCashFlow.add(CashFlow.ofForecastValue(tempCashflow.getPaymentDate(),
tempCashflow.getForecastValue().multipliedBy(quantity), tempCashflow.getDiscountFactor()));
}

return CashFlows.of(newListCashFlow);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -167,6 +168,16 @@ public void test_presentValue() {
assertThat(computed.getAmount()).isCloseTo(expected.getAmount(), offset(NOTIONAL * TOL));
}

@Test
public void test_cashFlows() {
List<CashFlow> 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);
Expand Down