com.opengamma.strata.pricer.bond.DiscountingFixedCouponBondProductPricer Maven / Gradle / Ivy
/*
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.bond;
import static com.opengamma.strata.math.MathUtils.pow2;
import static com.opengamma.strata.product.bond.FixedCouponBondYieldConvention.DE_BONDS;
import static com.opengamma.strata.product.bond.FixedCouponBondYieldConvention.GB_BUMP_DMO;
import static com.opengamma.strata.product.bond.FixedCouponBondYieldConvention.JP_SIMPLE;
import static com.opengamma.strata.product.bond.FixedCouponBondYieldConvention.US_STREET;
import java.time.LocalDate;
import java.util.function.Function;
import com.google.common.collect.ImmutableList;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.currency.CurrencyAmount;
import com.opengamma.strata.basics.currency.Payment;
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.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.DiscountingPaymentPricer;
import com.opengamma.strata.pricer.ZeroRateSensitivity;
import com.opengamma.strata.product.Security;
import com.opengamma.strata.product.bond.FixedCouponBondPaymentPeriod;
import com.opengamma.strata.product.bond.FixedCouponBondYieldConvention;
import com.opengamma.strata.product.bond.ResolvedFixedCouponBond;
/**
* Pricer for fixed coupon bond products.
*
* This function provides the ability to price a {@link ResolvedFixedCouponBond}.
*
*
Price
* Strata uses decimal prices for bonds in the trade model, pricers and market data.
* For example, a price of 99.32% is represented in Strata by 0.9932.
*/
public class DiscountingFixedCouponBondProductPricer {
/**
* Default implementation.
*/
public static final DiscountingFixedCouponBondProductPricer DEFAULT = new DiscountingFixedCouponBondProductPricer(
DiscountingFixedCouponBondPaymentPeriodPricer.DEFAULT,
DiscountingPaymentPricer.DEFAULT);
/**
* The root finder.
*/
private static final RealSingleRootFinder ROOT_FINDER = new BrentSingleRootFinder();
/**
* Brackets a root.
*/
private static final BracketRoot ROOT_BRACKETER = new BracketRoot();
/**
* Pricer for {@link Payment}.
*/
private final DiscountingPaymentPricer nominalPricer;
/**
* Pricer for {@link FixedCouponBondPaymentPeriod}.
*/
private final DiscountingFixedCouponBondPaymentPeriodPricer periodPricer;
/**
* Creates an instance.
*
* @param periodPricer the pricer for {@link FixedCouponBondPaymentPeriod}
* @param nominalPricer the pricer for {@link Payment}
*/
public DiscountingFixedCouponBondProductPricer(
DiscountingFixedCouponBondPaymentPeriodPricer periodPricer,
DiscountingPaymentPricer nominalPricer) {
this.nominalPricer = ArgChecker.notNull(nominalPricer, "nominalPricer");
this.periodPricer = ArgChecker.notNull(periodPricer, "periodPricer");
}
//-------------------------------------------------------------------------
/**
* Calculates the present value of the fixed coupon bond product.
*
* The present value of the product is the value on the valuation date.
* The result is expressed using the payment currency of the bond.
*
* Coupon payments of the product are considered based on the valuation date.
*
* @param bond the product
* @param provider the discounting provider
* @return the present value of the fixed coupon bond product
*/
public CurrencyAmount presentValue(ResolvedFixedCouponBond bond, LegalEntityDiscountingProvider provider) {
return presentValue(bond, provider, provider.getValuationDate());
}
// calculate the present value
CurrencyAmount presentValue(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
LocalDate referenceDate) {
IssuerCurveDiscountFactors issuerDf = issuerCurveDf(bond, provider);
CurrencyAmount pvNominal = nominalPricer.presentValue(bond.getNominalPayment(), issuerDf.getDiscountFactors());
CurrencyAmount pvCoupon = presentValueCoupon(bond, issuerDf, referenceDate);
return pvNominal.plus(pvCoupon);
}
/**
* Calculates the present value of the fixed coupon bond product with z-spread.
*
* The present value of the product is the value on the valuation date.
* The result is expressed using the payment currency of the bond.
*
* The z-spread is a parallel shift applied to continuously compounded rates or
* periodic compounded rates of the issuer discounting curve.
*
* @param bond the product
* @param provider the discounting provider
* @param zSpread the z-spread
* @param compoundedRateType the compounded rate type
* @param periodsPerYear the number of periods per year
* @return the present value of the fixed coupon bond product
*/
public CurrencyAmount presentValueWithZSpread(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear) {
return presentValueWithZSpread(
bond, provider, zSpread, compoundedRateType, periodsPerYear, provider.getValuationDate());
}
// calculate the present value
CurrencyAmount presentValueWithZSpread(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear,
LocalDate referenceDate) {
IssuerCurveDiscountFactors issuerDf = issuerCurveDf(bond, provider);
CurrencyAmount pvNominal = nominalPricer.presentValueWithSpread(
bond.getNominalPayment(), issuerDf.getDiscountFactors(), zSpread, compoundedRateType, periodsPerYear);
CurrencyAmount pvCoupon = presentValueCouponFromZSpread(
bond, issuerDf, zSpread, compoundedRateType, periodsPerYear, referenceDate);
return pvNominal.plus(pvCoupon);
}
//-------------------------------------------------------------------------
/**
* Calculates the dirty price of the fixed coupon bond.
*
* The fixed coupon bond is represented as {@link Security} where standard ID of the bond is stored.
*
* Strata uses decimal prices for bonds. For example, a price of 99.32% is represented in Strata by 0.9932.
*
* @param bond the product
* @param provider the discounting provider
* @param refData the reference data used to calculate the settlement date
* @return the dirty price of the fixed coupon bond security
*/
public double dirtyPriceFromCurves(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
ReferenceData refData) {
LocalDate settlementDate = bond.getSettlementDateOffset().adjust(provider.getValuationDate(), refData);
return dirtyPriceFromCurves(bond, provider, settlementDate);
}
/**
* Calculates the dirty price of the fixed coupon bond under the specified settlement date.
*
* The fixed coupon bond is represented as {@link Security} where standard ID of the bond is stored.
*
* @param bond the product
* @param provider the discounting provider
* @param settlementDate the settlement date
* @return the dirty price of the fixed coupon bond security
*/
public double dirtyPriceFromCurves(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
LocalDate settlementDate) {
CurrencyAmount pv = presentValue(bond, provider, settlementDate);
RepoCurveDiscountFactors repoDf = repoCurveDf(bond, provider);
double df = repoDf.discountFactor(settlementDate);
double notional = bond.getNotional();
return pv.getAmount() / df / notional;
}
/**
* Calculates the dirty price of the fixed coupon bond with z-spread.
*
* The z-spread is a parallel shift applied to continuously compounded rates or periodic
* compounded rates of the discounting curve.
*
* The fixed coupon bond is represented as {@link Security} where standard ID of the bond is stored.
*
* @param bond the product
* @param provider the discounting provider
* @param refData the reference data used to calculate the settlement date
* @param zSpread the z-spread
* @param compoundedRateType the compounded rate type
* @param periodsPerYear the number of periods per year
* @return the dirty price of the fixed coupon bond security
*/
public double dirtyPriceFromCurvesWithZSpread(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
ReferenceData refData,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear) {
LocalDate settlementDate = bond.getSettlementDateOffset().adjust(provider.getValuationDate(), refData);
return dirtyPriceFromCurvesWithZSpread(bond, provider, zSpread, compoundedRateType, periodsPerYear, settlementDate);
}
/**
* Calculates the dirty price of the fixed coupon bond under the specified settlement date with z-spread.
*
* The z-spread is a parallel shift applied to continuously compounded rates or periodic
* compounded rates of the discounting curve.
*
* The fixed coupon bond is represented as {@link Security} where standard ID of the bond is stored.
*
* @param bond the product
* @param provider the discounting provider
* @param zSpread the z-spread
* @param compoundedRateType the compounded rate type
* @param periodsPerYear the number of periods per year
* @param settlementDate the settlement date
* @return the dirty price of the fixed coupon bond security
*/
public double dirtyPriceFromCurvesWithZSpread(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear,
LocalDate settlementDate) {
CurrencyAmount pv = presentValueWithZSpread(bond, provider, zSpread, compoundedRateType, periodsPerYear, settlementDate);
RepoCurveDiscountFactors repoDf = repoCurveDf(bond, provider);
double df = repoDf.discountFactor(settlementDate);
double notional = bond.getNotional();
return pv.getAmount() / df / notional;
}
//-------------------------------------------------------------------------
/**
* Calculates the dirty price of the fixed coupon bond from its settlement date and clean price.
*
* @param bond the product
* @param settlementDate the settlement date
* @param cleanPrice the clean price
* @return the present value of the fixed coupon bond product
*/
public double dirtyPriceFromCleanPrice(ResolvedFixedCouponBond bond, LocalDate settlementDate, double cleanPrice) {
double notional = bond.getNotional();
double accruedInterest = accruedInterest(bond, settlementDate);
return cleanPrice + accruedInterest / notional;
}
/**
* Calculates the clean price of the fixed coupon bond from its settlement date and dirty price.
*
* Strata uses decimal prices for bonds. For example, a price of 99.32% is represented in Strata by 0.9932.
*
* @param bond the product
* @param settlementDate the settlement date
* @param dirtyPrice the dirty price
* @return the present value of the fixed coupon bond product
*/
public double cleanPriceFromDirtyPrice(ResolvedFixedCouponBond bond, LocalDate settlementDate, double dirtyPrice) {
double notional = bond.getNotional();
double accruedInterest = accruedInterest(bond, settlementDate);
return dirtyPrice - accruedInterest / notional;
}
//-------------------------------------------------------------------------
/**
* Calculates the z-spread of the fixed coupon bond from curves and dirty price.
*
* The z-spread is a parallel shift applied to continuously compounded rates or periodic
* compounded rates of the discounting curve associated to the bond (Issuer Entity)
* to match the dirty price.
*
* @param bond the product
* @param provider the discounting provider
* @param refData the reference data used to calculate the settlement date
* @param dirtyPrice the dirtyPrice
* @param compoundedRateType the compounded rate type
* @param periodsPerYear the number of periods per year
* @return the z-spread of the fixed coupon bond security
*/
public double zSpreadFromCurvesAndDirtyPrice(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
ReferenceData refData,
double dirtyPrice,
CompoundedRateType compoundedRateType,
int periodsPerYear) {
final Function residual = new Function() {
@Override
public Double apply(final Double z) {
return dirtyPriceFromCurvesWithZSpread(
bond, provider, refData, z, compoundedRateType, periodsPerYear) - dirtyPrice;
}
};
double[] range = ROOT_BRACKETER.getBracketedPoints(residual, -0.01, 0.01); // Starting range is [-1%, 1%]
return ROOT_FINDER.getRoot(residual, range[0], range[1]);
}
//-------------------------------------------------------------------------
/**
* Calculates the present value sensitivity of the fixed coupon bond product.
*
* The present value sensitivity of the product is the sensitivity of the present value to
* the underlying curves.
*
* @param bond the product
* @param provider the discounting provider
* @return the present value curve sensitivity of the product
*/
public PointSensitivityBuilder presentValueSensitivity(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider) {
return presentValueSensitivity(bond, provider, provider.getValuationDate());
}
// calculate the present value sensitivity
PointSensitivityBuilder presentValueSensitivity(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
LocalDate referenceDate) {
IssuerCurveDiscountFactors issuerDf = issuerCurveDf(bond, provider);
PointSensitivityBuilder pvNominal = presentValueSensitivityNominal(bond, issuerDf);
PointSensitivityBuilder pvCoupon = presentValueSensitivityCoupon(bond, issuerDf, referenceDate);
return pvNominal.combinedWith(pvCoupon);
}
/**
* Calculates the present value sensitivity of the fixed coupon bond with z-spread.
*
* The present value sensitivity of the product is the sensitivity of the present value to
* the underlying curves.
*
* The z-spread is a parallel shift applied to continuously compounded rates or
* periodic compounded rates of the issuer discounting curve.
*
* @param bond the product
* @param provider the discounting provider
* @param zSpread the z-spread
* @param compoundedRateType the compounded rate type
* @param periodsPerYear the number of periods per year
* @return the present value curve sensitivity of the product
*/
public PointSensitivityBuilder presentValueSensitivityWithZSpread(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear) {
return presentValueSensitivityWithZSpread(
bond, provider, zSpread, compoundedRateType, periodsPerYear, provider.getValuationDate());
}
// calculate the present value sensitivity
PointSensitivityBuilder presentValueSensitivityWithZSpread(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear,
LocalDate referenceDate) {
IssuerCurveDiscountFactors issuerDf = issuerCurveDf(bond, provider);
PointSensitivityBuilder pvNominal = presentValueSensitivityNominalFromZSpread(
bond, issuerDf, zSpread, compoundedRateType, periodsPerYear);
PointSensitivityBuilder pvCoupon = presentValueSensitivityCouponFromZSpread(
bond, issuerDf, zSpread, compoundedRateType, periodsPerYear, referenceDate);
return pvNominal.combinedWith(pvCoupon);
}
//-------------------------------------------------------------------------
/**
* Calculates the dirty price sensitivity of the fixed coupon bond product.
*
* The dirty price sensitivity of the security is the sensitivity of the present value to
* the underlying curves.
*
* @param bond the product
* @param provider the discounting provider
* @param refData the reference data used to calculate the settlement date
* @return the dirty price value curve sensitivity of the security
*/
public PointSensitivityBuilder dirtyPriceSensitivity(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
ReferenceData refData) {
LocalDate settlementDate = bond.getSettlementDateOffset().adjust(provider.getValuationDate(), refData);
return dirtyPriceSensitivity(bond, provider, settlementDate);
}
/**
* Calculates the dirty price sensitivity of the fixed coupon bond product under the specified settlement date.
*
* The dirty price sensitivity of the security is the sensitivity of the present value to
* the underlying curves.
*
* @param bond the product
* @param provider the discounting provider
* @param settlementDate the settlement date
* @return the dirty price value curve sensitivity of the security
*/
public PointSensitivityBuilder dirtyPriceSensitivity(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
LocalDate settlementDate) {
double notional = bond.getNotional();
CurrencyAmount pv = presentValue(bond, provider, settlementDate);
RepoCurveDiscountFactors repoDf = repoCurveDf(bond, provider);
double df = repoDf.discountFactor(settlementDate);
// Backward sweep
double priceBar = 1.0;
double pvBar = 1.0d / df / notional * priceBar;
double dfBar = -pv.getAmount() / (df * df) / notional * priceBar;
RepoCurveZeroRateSensitivity dfDr = repoDf.zeroRatePointSensitivity(settlementDate);
PointSensitivityBuilder pvDr = presentValueSensitivity(bond, provider, settlementDate);
return pvDr.multipliedBy(pvBar).combinedWith(dfDr.multipliedBy(dfBar));
}
/**
* Calculates the dirty price sensitivity of the fixed coupon bond with z-spread.
*
* The dirty price sensitivity of the security is the sensitivity of the present value to
* the underlying curves.
*
* The z-spread is a parallel shift applied to continuously compounded rates or periodic
* compounded rates of the discounting curve.
*
* @param bond the product
* @param provider the discounting provider
* @param refData the reference data used to calculate the settlement date
* @param zSpread the z-spread
* @param compoundedRateType the compounded rate type
* @param periodsPerYear the number of periods per year
* @return the dirty price curve sensitivity of the security
*/
public PointSensitivityBuilder dirtyPriceSensitivityWithZspread(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
ReferenceData refData,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear) {
LocalDate settlementDate = bond.getSettlementDateOffset().adjust(provider.getValuationDate(), refData);
return dirtyPriceSensitivityWithZspread(bond, provider, zSpread, compoundedRateType, periodsPerYear, settlementDate);
}
// calculate the dirty price sensitivity
PointSensitivityBuilder dirtyPriceSensitivityWithZspread(
ResolvedFixedCouponBond bond,
LegalEntityDiscountingProvider provider,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear,
LocalDate referenceDate) {
RepoCurveDiscountFactors repoDf = repoCurveDf(bond, provider);
double df = repoDf.discountFactor(referenceDate);
CurrencyAmount pv = presentValueWithZSpread(bond, provider, zSpread, compoundedRateType, periodsPerYear);
double notional = bond.getNotional();
PointSensitivityBuilder pvSensi = presentValueSensitivityWithZSpread(
bond, provider, zSpread, compoundedRateType, periodsPerYear).multipliedBy(1d / df / notional);
RepoCurveZeroRateSensitivity dfSensi = repoDf.zeroRatePointSensitivity(referenceDate)
.multipliedBy(-pv.getAmount() / df / df / notional);
return pvSensi.combinedWith(dfSensi);
}
//-------------------------------------------------------------------------
/**
* Calculates the accrued interest of the fixed coupon bond with the specified settlement date.
*
* @param bond the product
* @param settlementDate the settlement date
* @return the accrued interest of the product
*/
public double accruedInterest(ResolvedFixedCouponBond bond, LocalDate settlementDate) {
double notional = bond.getNotional();
return accruedYearFraction(bond, settlementDate) * bond.getFixedRate() * notional;
}
/**
* Calculates the accrued year fraction of the fixed coupon bond with the specified settlement date.
*
* @param bond the product
* @param settlementDate the settlement date
* @return the accrued year fraction of the product
*/
public double accruedYearFraction(ResolvedFixedCouponBond bond, LocalDate settlementDate) {
if (bond.getUnadjustedStartDate().isAfter(settlementDate)) {
return 0d;
}
FixedCouponBondPaymentPeriod period = bond.findPeriod(settlementDate)
.orElseThrow(() -> new IllegalArgumentException("Date outside range of bond"));
LocalDate previousAccrualDate = period.getUnadjustedStartDate();
double accruedYearFraction = bond.yearFraction(previousAccrualDate, settlementDate);
double result = 0d;
if (settlementDate.isAfter(period.getDetachmentDate())) {
result = accruedYearFraction - period.getYearFraction();
} else {
result = accruedYearFraction;
}
return result;
}
//-------------------------------------------------------------------------
/**
* Calculates the dirty price of the fixed coupon bond from yield.
*
* The yield must be fractional.
* The dirty price is computed for {@link FixedCouponBondYieldConvention}, and the result is expressed in fraction.
*
* @param bond the product
* @param settlementDate the settlement date
* @param yield the yield
* @return the dirty price of the product
*/
public double dirtyPriceFromYield(ResolvedFixedCouponBond bond, LocalDate settlementDate, double yield) {
ImmutableList payments = bond.getPeriodicPayments();
int nCoupon = payments.size() - couponIndex(payments, settlementDate);
FixedCouponBondYieldConvention yieldConv = bond.getYieldConvention();
if (nCoupon == 1) {
if (yieldConv.equals(US_STREET) || yieldConv.equals(DE_BONDS)) {
FixedCouponBondPaymentPeriod payment = payments.get(payments.size() - 1);
return (1d + payment.getFixedRate() * payment.getYearFraction()) /
(1d + factorToNextCoupon(bond, settlementDate) * yield /
((double) bond.getFrequency().eventsPerYear()));
}
}
if ((yieldConv.equals(US_STREET)) || (yieldConv.equals(GB_BUMP_DMO)) || (yieldConv.equals(DE_BONDS))) {
return dirtyPriceFromYieldStandard(bond, settlementDate, yield);
}
if (yieldConv.equals(JP_SIMPLE)) {
LocalDate maturityDate = bond.getUnadjustedEndDate();
if (settlementDate.isAfter(maturityDate)) {
return 0d;
}
double maturity = bond.getDayCount().relativeYearFraction(settlementDate, maturityDate);
double cleanPrice = (1d + bond.getFixedRate() * maturity) / (1d + yield * maturity);
return dirtyPriceFromCleanPrice(bond, settlementDate, cleanPrice);
}
throw new UnsupportedOperationException("The convention " + yieldConv.name() + " is not supported.");
}
/**
* Calculates the dirty price of the fixed coupon bond from yield and its derivative wrt to the yield.
*
* The yield must be fractional.
* The dirty price is computed for {@link FixedCouponBondYieldConvention}, and the result is expressed in fraction.
*
* @param bond the product
* @param settlementDate the settlement date
* @param yield the yield
* @return the dirty price of the product and its derivative
*/
public ValueDerivatives dirtyPriceFromYieldAd(ResolvedFixedCouponBond bond, LocalDate settlementDate, double yield) {
ImmutableList payments = bond.getPeriodicPayments();
int nCoupon = payments.size() - couponIndex(payments, settlementDate);
FixedCouponBondYieldConvention yieldConv = bond.getYieldConvention();
if (nCoupon == 1) {
if (yieldConv.equals(US_STREET) || yieldConv.equals(DE_BONDS)) {
FixedCouponBondPaymentPeriod payment = payments.get(payments.size() - 1);
double df = (1d + factorToNextCoupon(bond, settlementDate) * yield /
((double) bond.getFrequency().eventsPerYear()));
double price = (1d + payment.getFixedRate() * payment.getYearFraction()) / df;
double yieldBar = -(1d + payment.getFixedRate() * payment.getYearFraction()) /
(df * df) * factorToNextCoupon(bond, settlementDate) / ((double) bond.getFrequency().eventsPerYear());
return ValueDerivatives.of(price, DoubleArray.of(yieldBar));
}
}
if ((yieldConv.equals(US_STREET)) || (yieldConv.equals(GB_BUMP_DMO)) || (yieldConv.equals(DE_BONDS))) {
return dirtyPriceFromYieldStandardAd(bond, settlementDate, yield);
}
if (yieldConv.equals(JP_SIMPLE)) {
LocalDate maturityDate = bond.getUnadjustedEndDate();
if (settlementDate.isAfter(maturityDate)) {
double price = 0d;
return ValueDerivatives.of(price, DoubleArray.of(0.0d));
}
double maturity = bond.getDayCount().relativeYearFraction(settlementDate, maturityDate);
double cleanPrice = (1d + bond.getFixedRate() * maturity) / (1d + yield * maturity);
double price = dirtyPriceFromCleanPrice(bond, settlementDate, cleanPrice);
double yieldBar = -(1d + bond.getFixedRate() * maturity) / ((1d + yield * maturity) * (1d + yield * maturity)) * maturity;
return ValueDerivatives.of(price, DoubleArray.of(yieldBar));
}
throw new UnsupportedOperationException("The convention " + yieldConv.name() + " is not supported.");
}
// Computes the dirty price from the yield in the standard conventions
private double dirtyPriceFromYieldStandard(
ResolvedFixedCouponBond bond,
LocalDate settlementDate,
double yield) {
int nbCoupon = bond.getPeriodicPayments().size();
double factorOnPeriod = 1 + yield / ((double) bond.getFrequency().eventsPerYear());
double fixedRate = bond.getFixedRate();
double pvAtFirstCoupon = 0;
int pow = 0;
for (int loopcpn = 0; loopcpn < nbCoupon; loopcpn++) {
FixedCouponBondPaymentPeriod period = bond.getPeriodicPayments().get(loopcpn);
if ((period.hasExCouponPeriod() && !settlementDate.isAfter(period.getDetachmentDate())) ||
(!period.hasExCouponPeriod() && period.getPaymentDate().isAfter(settlementDate))) {
pvAtFirstCoupon += fixedRate * period.getYearFraction() / Math.pow(factorOnPeriod, pow);
++pow;
}
}
pvAtFirstCoupon += 1d / Math.pow(factorOnPeriod, pow - 1);
return pvAtFirstCoupon * Math.pow(factorOnPeriod, -factorToNextCoupon(bond, settlementDate));
}
// Computes the dirty price from the yield in the standard conventions and its derivative wrt to the yield
private ValueDerivatives dirtyPriceFromYieldStandardAd(
ResolvedFixedCouponBond bond,
LocalDate settlementDate,
double yield) {
int nbCoupon = bond.getPeriodicPayments().size();
double factorOnPeriod = 1 + yield / ((double) bond.getFrequency().eventsPerYear());
double fixedRate = bond.getFixedRate();
double pvAtFirstCoupon = 0;
int pow = 0;
for (int loopcpn = 0; loopcpn < nbCoupon; loopcpn++) {
FixedCouponBondPaymentPeriod period = bond.getPeriodicPayments().get(loopcpn);
if ((period.hasExCouponPeriod() && !settlementDate.isAfter(period.getDetachmentDate())) ||
(!period.hasExCouponPeriod() && period.getPaymentDate().isAfter(settlementDate))) {
pvAtFirstCoupon += fixedRate * period.getYearFraction() * Math.pow(factorOnPeriod, -pow);
++pow;
}
}
pvAtFirstCoupon += Math.pow(factorOnPeriod, 1.0d - pow);
double factorNextCoupon = factorToNextCoupon(bond, settlementDate);
double priceAfter = Math.pow(factorOnPeriod, -factorNextCoupon);
double price = pvAtFirstCoupon * priceAfter;
// Backward sweep
double priceBar = 1.0d;
double priceAfterBar = pvAtFirstCoupon * priceBar;
double pvAtFirstCouponBar = priceAfter * priceBar;
double factorOnPeriodBar = (1.0d - pow) * Math.pow(factorOnPeriod, -pow) * pvAtFirstCouponBar;
int pow2 = 0;
for (int loopcpn = 0; loopcpn < nbCoupon; loopcpn++) {
FixedCouponBondPaymentPeriod period = bond.getPeriodicPayments().get(loopcpn);
if ((period.hasExCouponPeriod() && !settlementDate.isAfter(period.getDetachmentDate())) ||
(!period.hasExCouponPeriod() && period.getPaymentDate().isAfter(settlementDate))) {
pvAtFirstCoupon += fixedRate * period.getYearFraction() * Math.pow(factorOnPeriod, -pow2);
factorOnPeriodBar +=
fixedRate * period.getYearFraction() * -pow2 * Math.pow(factorOnPeriod, -pow2 - 1) * pvAtFirstCouponBar;
++pow2;
}
}
factorOnPeriodBar += -factorNextCoupon * Math.pow(factorOnPeriod, -factorNextCoupon - 1.0) * priceAfterBar;
double yieldBar = 1.0d / ((double) bond.getFrequency().eventsPerYear()) * factorOnPeriodBar;
return ValueDerivatives.of(price, DoubleArray.of(yieldBar));
}
/**
* Calculates the yield of the fixed coupon bond product from dirty price.
*
* The dirty price must be fractional.
* If the analytic formula is not available, the yield is computed by solving
* a root-finding problem with {@link #dirtyPriceFromYield(ResolvedFixedCouponBond, LocalDate, double)}.
* The result is also expressed in fraction.
*
* @param bond the product
* @param settlementDate the settlement date
* @param dirtyPrice the dirty price
* @return the yield of the product
*/
public double yieldFromDirtyPrice(ResolvedFixedCouponBond bond, LocalDate settlementDate, double dirtyPrice) {
if (bond.getYieldConvention().equals(JP_SIMPLE)) {
double cleanPrice = cleanPriceFromDirtyPrice(bond, settlementDate, dirtyPrice);
LocalDate maturityDate = bond.getUnadjustedEndDate();
double maturity = bond.getDayCount().relativeYearFraction(settlementDate, maturityDate);
return (bond.getFixedRate() + (1d - cleanPrice) / maturity) / cleanPrice;
}
final Function priceResidual = new Function() {
@Override
public Double apply(final Double y) {
return dirtyPriceFromYield(bond, settlementDate, y) - dirtyPrice;
}
};
double[] range = ROOT_BRACKETER.getBracketedPoints(priceResidual, 0.00, 0.20);
double yield = ROOT_FINDER.getRoot(priceResidual, range[0], range[1]);
return yield;
}
/**
* Calculates the yield of the fixed coupon bond product from dirty price and its derivative wrt the price.
*
* The dirty price must be fractional.
* If the analytic formula is not available, the yield is computed by solving
* a root-finding problem with {@link #dirtyPriceFromYield(ResolvedFixedCouponBond, LocalDate, double)}.
* The result is also expressed in fraction.
*
* @param bond the product
* @param settlementDate the settlement date
* @param dirtyPrice the dirty price
* @return the yield of the product
*/
public ValueDerivatives yieldFromDirtyPriceAd(ResolvedFixedCouponBond bond, LocalDate settlementDate, double dirtyPrice) {
if (bond.getYieldConvention().equals(JP_SIMPLE)) {
double cleanPrice = cleanPriceFromDirtyPrice(bond, settlementDate, dirtyPrice);
LocalDate maturityDate = bond.getUnadjustedEndDate();
double maturity = bond.getDayCount().relativeYearFraction(settlementDate, maturityDate);
double yield = (bond.getFixedRate() + (1d - cleanPrice) / maturity) / cleanPrice;
double priceBar =
(-1.0d / maturity * cleanPrice - (bond.getFixedRate() + (1d - cleanPrice) / maturity)) / (cleanPrice * cleanPrice);
return ValueDerivatives.of(yield, DoubleArray.of(priceBar));
}
final Function priceResidual = new Function() {
@Override
public Double apply(final Double y) {
return dirtyPriceFromYield(bond, settlementDate, y) - dirtyPrice;
}
};
double[] range = ROOT_BRACKETER.getBracketedPoints(priceResidual, 0.00, 0.20);
double yield = ROOT_FINDER.getRoot(priceResidual, range[0], range[1]);
ValueDerivatives priceDYield = dirtyPriceFromYieldAd(bond, settlementDate, yield);
return ValueDerivatives.of(yield, DoubleArray.of(1.0 / priceDYield.getDerivative(0)));
}
//-------------------------------------------------------------------------
/**
* Calculates the modified duration of the fixed coupon bond product from yield.
*
* The modified duration is defined as the minus of the first derivative of dirty price
* with respect to yield, divided by the dirty price.
*
* The input yield must be fractional. The dirty price and its derivative are
* computed for {@link FixedCouponBondYieldConvention}, and the result is expressed in fraction.
*
* @param bond the product
* @param settlementDate the settlement date
* @param yield the yield
* @return the modified duration of the product
*/
public double modifiedDurationFromYield(ResolvedFixedCouponBond bond, LocalDate settlementDate, double yield) {
ImmutableList payments = bond.getPeriodicPayments();
int nCoupon = payments.size() - couponIndex(payments, settlementDate);
FixedCouponBondYieldConvention yieldConv = bond.getYieldConvention();
if (nCoupon == 1) {
if (yieldConv.equals(US_STREET) || yieldConv.equals(DE_BONDS)) {
double couponPerYear = bond.getFrequency().eventsPerYear();
double factor = factorToNextCoupon(bond, settlementDate);
return factor / couponPerYear / (1d + factor * yield / couponPerYear);
}
}
if (yieldConv.equals(US_STREET) || yieldConv.equals(GB_BUMP_DMO) || yieldConv.equals(DE_BONDS)) {
return modifiedDurationFromYieldStandard(bond, settlementDate, yield);
}
if (yieldConv.equals(JP_SIMPLE)) {
LocalDate maturityDate = bond.getUnadjustedEndDate();
if (settlementDate.isAfter(maturityDate)) {
return 0d;
}
double maturity = bond.getDayCount().relativeYearFraction(settlementDate, maturityDate);
double num = 1d + bond.getFixedRate() * maturity;
double den = 1d + yield * maturity;
double dirtyPrice = dirtyPriceFromCleanPrice(bond, settlementDate, num / den);
return num * maturity / den / den / dirtyPrice;
}
throw new UnsupportedOperationException("The convention " + yieldConv.name() + " is not supported.");
}
/**
* Calculates the modified duration of the fixed coupon bond product from yield and its derivative wrt to the yield.
*
* The modified duration is defined as the minus of the first derivative of dirty price
* with respect to yield, divided by the dirty price.
*
* The input yield must be fractional. The dirty price and its derivative are
* computed for {@link FixedCouponBondYieldConvention}, and the result is expressed in fraction.
*
* @param bond the product
* @param settlementDate the settlement date
* @param yield the yield
* @return the modified duration of the product
*/
public ValueDerivatives modifiedDurationFromYieldAd(
ResolvedFixedCouponBond bond,
LocalDate settlementDate,
double yield) {
ImmutableList payments = bond.getPeriodicPayments();
int nCoupon = payments.size() - couponIndex(payments, settlementDate);
FixedCouponBondYieldConvention yieldConv = bond.getYieldConvention();
if (nCoupon == 1) {
if (yieldConv.equals(US_STREET) || yieldConv.equals(DE_BONDS)) {
double couponPerYear = bond.getFrequency().eventsPerYear();
double factor = factorToNextCoupon(bond, settlementDate);
double md = factor / couponPerYear / (1d + factor * yield / couponPerYear);
double yieldBar = -factor / couponPerYear /
((1d + factor * yield / couponPerYear) * (1d + factor * yield / couponPerYear)) * factor / couponPerYear;
return ValueDerivatives.of(md, DoubleArray.of(yieldBar));
}
}
if (yieldConv.equals(US_STREET) || yieldConv.equals(GB_BUMP_DMO) || yieldConv.equals(DE_BONDS)) {
return modifiedDurationFromYieldStandardAd(bond, settlementDate, yield);
}
if (yieldConv.equals(JP_SIMPLE)) {
LocalDate maturityDate = bond.getUnadjustedEndDate();
if (settlementDate.isAfter(maturityDate)) {
double md = 0d;
double yieldBar = 0.0;
return ValueDerivatives.of(md, DoubleArray.of(yieldBar));
}
double maturity = bond.getDayCount().relativeYearFraction(settlementDate, maturityDate);
double num = 1d + bond.getFixedRate() * maturity;
double den = 1d + yield * maturity;
double cleanPrice = num / den;
double dirtyPrice = dirtyPriceFromCleanPrice(bond, settlementDate, cleanPrice);
double md = num * maturity / (den * den) / dirtyPrice;
double mdBar = 1.0;
double denBar = -2.0d * num * maturity / (den * den * den) / dirtyPrice * mdBar;
double dirtyPriceBar = -md / dirtyPrice * mdBar;
double cleanPriceBar = dirtyPriceBar;
denBar += -cleanPrice / den * cleanPriceBar;
double yieldBar = maturity * denBar;
return ValueDerivatives.of(md, DoubleArray.of(yieldBar));
}
throw new UnsupportedOperationException("The convention " + yieldConv.name() + " is not supported.");
}
// Computes the modified duration with standard convention
private double modifiedDurationFromYieldStandard(
ResolvedFixedCouponBond bond,
LocalDate settlementDate,
double yield) {
int nbCoupon = bond.getPeriodicPayments().size();
double couponPerYear = bond.getFrequency().eventsPerYear();
double factorToNextCoupon = factorToNextCoupon(bond, settlementDate);
double factorOnPeriod = 1 + yield / couponPerYear;
double nominal = bond.getNotional();
double fixedRate = bond.getFixedRate();
double mdAtFirstCoupon = 0d;
double pvAtFirstCoupon = 0d;
int pow = 0;
for (int loopcpn = 0; loopcpn < nbCoupon; loopcpn++) {
FixedCouponBondPaymentPeriod period = bond.getPeriodicPayments().get(loopcpn);
if ((period.hasExCouponPeriod() && !settlementDate.isAfter(period.getDetachmentDate())) ||
(!period.hasExCouponPeriod() && period.getPaymentDate().isAfter(settlementDate))) {
mdAtFirstCoupon += period.getYearFraction() / Math.pow(factorOnPeriod, pow + 1) *
(pow + factorToNextCoupon) / couponPerYear;
pvAtFirstCoupon += period.getYearFraction() / Math.pow(factorOnPeriod, pow);
++pow;
}
}
mdAtFirstCoupon *= fixedRate * nominal;
pvAtFirstCoupon *= fixedRate * nominal;
mdAtFirstCoupon += nominal / Math.pow(factorOnPeriod, pow) * (pow - 1 + factorToNextCoupon) /
couponPerYear;
pvAtFirstCoupon += nominal / Math.pow(factorOnPeriod, pow - 1);
double md = mdAtFirstCoupon / pvAtFirstCoupon;
return md;
}
private ValueDerivatives modifiedDurationFromYieldStandardAd(
ResolvedFixedCouponBond bond,
LocalDate settlementDate,
double yield) {
int nbCoupon = bond.getPeriodicPayments().size();
double couponPerYear = bond.getFrequency().eventsPerYear();
double factorToNextCoupon = factorToNextCoupon(bond, settlementDate);
double factorOnPeriod = 1 + yield / couponPerYear;
double nominal = bond.getNotional();
double fixedRate = bond.getFixedRate();
double mdAtFirstCoupon = 0d;
double pvAtFirstCoupon = 0d;
int pow = 0;
for (int loopcpn = 0; loopcpn < nbCoupon; loopcpn++) {
FixedCouponBondPaymentPeriod period = bond.getPeriodicPayments().get(loopcpn);
if ((period.hasExCouponPeriod() && !settlementDate.isAfter(period.getDetachmentDate())) ||
(!period.hasExCouponPeriod() && period.getPaymentDate().isAfter(settlementDate))) {
mdAtFirstCoupon += period.getYearFraction() / Math.pow(factorOnPeriod, pow + 1) *
(pow + factorToNextCoupon) / couponPerYear;
pvAtFirstCoupon += period.getYearFraction() / Math.pow(factorOnPeriod, pow);
++pow;
}
}
mdAtFirstCoupon *= fixedRate * nominal;
pvAtFirstCoupon *= fixedRate * nominal;
mdAtFirstCoupon += nominal / Math.pow(factorOnPeriod, pow) * (pow - 1 + factorToNextCoupon) /
couponPerYear;
pvAtFirstCoupon += nominal * Math.pow(factorOnPeriod, 1.0d - pow);
double md = mdAtFirstCoupon / pvAtFirstCoupon;
// Backward sweep
double mdAtFirstCouponBar = 1.0d / pvAtFirstCoupon;
double pvAtFirstCouponBar = -mdAtFirstCoupon / (pvAtFirstCoupon * pvAtFirstCoupon);
double factorOnPeriodBar = nominal * (1.0d - pow) * Math.pow(factorOnPeriod, -pow) * pvAtFirstCouponBar;
factorOnPeriodBar += nominal * -pow * Math.pow(factorOnPeriod, -pow - 1.0d) * (pow - 1 + factorToNextCoupon) /
couponPerYear * mdAtFirstCouponBar;
int pow2 = 0;
for (int loopcpn = 0; loopcpn < nbCoupon; loopcpn++) {
FixedCouponBondPaymentPeriod period = bond.getPeriodicPayments().get(loopcpn);
if ((period.hasExCouponPeriod() && !settlementDate.isAfter(period.getDetachmentDate())) ||
(!period.hasExCouponPeriod() && period.getPaymentDate().isAfter(settlementDate))) {
factorOnPeriodBar += period.getYearFraction() * (-pow2 - 1.0d) * Math.pow(factorOnPeriod, -pow2 - 2.0d) *
(pow2 + factorToNextCoupon) / couponPerYear * fixedRate * nominal * mdAtFirstCouponBar;
factorOnPeriodBar += period.getYearFraction() * -pow2 * Math.pow(factorOnPeriod, -pow2 - 1.0d) *
fixedRate * nominal * pvAtFirstCouponBar;
++pow2;
}
}
double yieldBar = 1.0d / couponPerYear * factorOnPeriodBar;
return ValueDerivatives.of(md, DoubleArray.of(yieldBar));
}
/**
* Calculates the Macaulay duration of the fixed coupon bond product from yield.
*
* Macaulay defined an alternative way of weighting the future cash flows.
*
* The input yield must be fractional. The dirty price and its derivative are
* computed for {@link FixedCouponBondYieldConvention}, and the result is expressed in fraction.
*
* @param bond the product
* @param settlementDate the settlement date
* @param yield the yield
* @return the modified duration of the product
*/
public double macaulayDurationFromYield(ResolvedFixedCouponBond bond, LocalDate settlementDate, double yield) {
ImmutableList payments = bond.getPeriodicPayments();
int nCoupon = payments.size() - couponIndex(payments, settlementDate);
FixedCouponBondYieldConvention yieldConv = bond.getYieldConvention();
if ((yieldConv.equals(US_STREET)) && (nCoupon == 1)) {
return factorToNextCoupon(bond, settlementDate) /
bond.getFrequency().eventsPerYear();
}
if ((yieldConv.equals(US_STREET)) || (yieldConv.equals(GB_BUMP_DMO)) || (yieldConv.equals(DE_BONDS))) {
return modifiedDurationFromYield(bond, settlementDate, yield) *
(1d + yield / bond.getFrequency().eventsPerYear());
}
throw new UnsupportedOperationException("The convention " + yieldConv.name() + " is not supported.");
}
/**
* Calculates the convexity of the fixed coupon bond product from yield.
*
* The convexity is defined as the second derivative of dirty price with respect
* to yield, divided by the dirty price.
*
* The input yield must be fractional. The dirty price and its derivative are
* computed for {@link FixedCouponBondYieldConvention}, and the result is expressed in fraction.
*
* @param bond the product
* @param settlementDate the settlement date
* @param yield the yield
* @return the convexity of the product
*/
public double convexityFromYield(ResolvedFixedCouponBond bond, LocalDate settlementDate, double yield) {
ImmutableList payments = bond.getPeriodicPayments();
int nCoupon = payments.size() - couponIndex(payments, settlementDate);
FixedCouponBondYieldConvention yieldConv = bond.getYieldConvention();
if (nCoupon == 1) {
if (yieldConv.equals(US_STREET) || yieldConv.equals(DE_BONDS)) {
double couponPerYear = bond.getFrequency().eventsPerYear();
double factorToNextCoupon = factorToNextCoupon(bond, settlementDate);
double timeToPay = factorToNextCoupon / couponPerYear;
double disc = (1d + factorToNextCoupon * yield / couponPerYear);
return 2d * timeToPay * timeToPay / (disc * disc);
}
}
if (yieldConv.equals(US_STREET) || yieldConv.equals(GB_BUMP_DMO) || yieldConv.equals(DE_BONDS)) {
return convexityFromYieldStandard(bond, settlementDate, yield);
}
if (yieldConv.equals(JP_SIMPLE)) {
LocalDate maturityDate = bond.getUnadjustedEndDate();
if (settlementDate.isAfter(maturityDate)) {
return 0d;
}
double maturity = bond.getDayCount().relativeYearFraction(settlementDate, maturityDate);
double num = 1d + bond.getFixedRate() * maturity;
double den = 1d + yield * maturity;
double dirtyPrice = dirtyPriceFromCleanPrice(bond, settlementDate, num / den);
return 2d * num * pow2(maturity) * Math.pow(den, -3) / dirtyPrice;
}
throw new UnsupportedOperationException("The convention " + yieldConv.name() + " is not supported.");
}
// assumes notional and coupon rate are constant across the payments.
private double convexityFromYieldStandard(
ResolvedFixedCouponBond bond,
LocalDate settlementDate,
double yield) {
int nbCoupon = bond.getPeriodicPayments().size();
double couponPerYear = bond.getFrequency().eventsPerYear();
double factorToNextCoupon = factorToNextCoupon(bond, settlementDate);
double factorOnPeriod = 1 + yield / couponPerYear;
double nominal = bond.getNotional();
double fixedRate = bond.getFixedRate();
double cvAtFirstCoupon = 0;
double pvAtFirstCoupon = 0;
int pow = 0;
for (int loopcpn = 0; loopcpn < nbCoupon; loopcpn++) {
FixedCouponBondPaymentPeriod period = bond.getPeriodicPayments().get(loopcpn);
if ((period.hasExCouponPeriod() && !settlementDate.isAfter(period.getDetachmentDate())) ||
(!period.hasExCouponPeriod() && period.getPaymentDate().isAfter(settlementDate))) {
cvAtFirstCoupon += period.getYearFraction() / Math.pow(factorOnPeriod, pow + 2) *
(pow + factorToNextCoupon) * (pow + factorToNextCoupon + 1);
pvAtFirstCoupon += period.getYearFraction() / Math.pow(factorOnPeriod, pow);
++pow;
}
}
cvAtFirstCoupon *= fixedRate * nominal / (couponPerYear * couponPerYear);
pvAtFirstCoupon *= fixedRate * nominal;
cvAtFirstCoupon += nominal / Math.pow(factorOnPeriod, pow + 1) * (pow - 1 + factorToNextCoupon) *
(pow + factorToNextCoupon) / (couponPerYear * couponPerYear);
pvAtFirstCoupon += nominal / Math.pow(factorOnPeriod, pow - 1);
final double pv = pvAtFirstCoupon * Math.pow(factorOnPeriod, -factorToNextCoupon);
final double cv = cvAtFirstCoupon * Math.pow(factorOnPeriod, -factorToNextCoupon) / pv;
return cv;
}
//-------------------------------------------------------------------------
// Accrual factor to the next coupon
private double factorToNextCoupon(ResolvedFixedCouponBond bond, LocalDate settlementDate) {
if (bond.getPeriodicPayments().get(0).getStartDate().isAfter(settlementDate)) {
return 0d;
}
int couponIndex = couponIndex(bond.getPeriodicPayments(), settlementDate);
double factorSpot = accruedYearFraction(bond, settlementDate);
double factorPeriod = bond.getPeriodicPayments().get(couponIndex).getYearFraction();
return (factorPeriod - factorSpot) * ((double) bond.getFrequency().eventsPerYear());
}
private int couponIndex(ImmutableList list, LocalDate date) {
int nbCoupon = list.size();
int couponIndex = 0;
for (int loopcpn = 0; loopcpn < nbCoupon; ++loopcpn) {
if (list.get(loopcpn).getEndDate().isAfter(date)) {
couponIndex = loopcpn;
break;
}
}
return couponIndex;
}
//-------------------------------------------------------------------------
private CurrencyAmount presentValueCoupon(
ResolvedFixedCouponBond bond,
IssuerCurveDiscountFactors discountFactors,
LocalDate referenceDate) {
double total = 0d;
for (FixedCouponBondPaymentPeriod period : bond.getPeriodicPayments()) {
if (period.getDetachmentDate().isAfter(referenceDate)) {
total += periodPricer.presentValue(period, discountFactors);
}
}
return CurrencyAmount.of(bond.getCurrency(), total);
}
private CurrencyAmount presentValueCouponFromZSpread(
ResolvedFixedCouponBond bond,
IssuerCurveDiscountFactors discountFactors,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear,
LocalDate referenceDate) {
double total = 0d;
for (FixedCouponBondPaymentPeriod period : bond.getPeriodicPayments()) {
if (!period.getDetachmentDate().isBefore(referenceDate)) {
total += periodPricer.presentValueWithSpread(period, discountFactors, zSpread, compoundedRateType, periodsPerYear);
}
}
return CurrencyAmount.of(bond.getCurrency(), total);
}
//-------------------------------------------------------------------------
private PointSensitivityBuilder presentValueSensitivityCoupon(
ResolvedFixedCouponBond bond,
IssuerCurveDiscountFactors discountFactors,
LocalDate referenceDate) {
PointSensitivityBuilder builder = PointSensitivityBuilder.none();
for (FixedCouponBondPaymentPeriod period : bond.getPeriodicPayments()) {
if (period.getDetachmentDate().isAfter(referenceDate)) {
builder = builder.combinedWith(periodPricer.presentValueSensitivity(period, discountFactors));
}
}
return builder;
}
private PointSensitivityBuilder presentValueSensitivityCouponFromZSpread(
ResolvedFixedCouponBond bond,
IssuerCurveDiscountFactors discountFactors,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear,
LocalDate referenceDate) {
PointSensitivityBuilder builder = PointSensitivityBuilder.none();
for (FixedCouponBondPaymentPeriod period : bond.getPeriodicPayments()) {
if (period.getDetachmentDate().isAfter(referenceDate)) {
builder = builder.combinedWith(periodPricer.presentValueSensitivityWithSpread(
period, discountFactors, zSpread, compoundedRateType, periodsPerYear));
}
}
return builder;
}
private PointSensitivityBuilder presentValueSensitivityNominal(
ResolvedFixedCouponBond bond,
IssuerCurveDiscountFactors discountFactors) {
Payment nominal = bond.getNominalPayment();
PointSensitivityBuilder pt = nominalPricer.presentValueSensitivity(nominal, discountFactors.getDiscountFactors());
if (pt instanceof ZeroRateSensitivity) {
return IssuerCurveZeroRateSensitivity.of((ZeroRateSensitivity) pt, discountFactors.getLegalEntityGroup());
}
return pt; // NoPointSensitivity
}
private PointSensitivityBuilder presentValueSensitivityNominalFromZSpread(
ResolvedFixedCouponBond bond,
IssuerCurveDiscountFactors discountFactors,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear) {
Payment nominal = bond.getNominalPayment();
PointSensitivityBuilder pt = nominalPricer.presentValueSensitivityWithSpread(
nominal, discountFactors.getDiscountFactors(), zSpread, compoundedRateType, periodsPerYear);
if (pt instanceof ZeroRateSensitivity) {
return IssuerCurveZeroRateSensitivity.of((ZeroRateSensitivity) pt, discountFactors.getLegalEntityGroup());
}
return pt; // NoPointSensitivity
}
//-------------------------------------------------------------------------
// compute pv of coupon payment(s) s.t. referenceDate1 < coupon <= referenceDate2
double presentValueCoupon(
ResolvedFixedCouponBond bond,
IssuerCurveDiscountFactors discountFactors,
LocalDate referenceDate1,
LocalDate referenceDate2) {
double pvDiff = 0d;
for (FixedCouponBondPaymentPeriod period : bond.getPeriodicPayments()) {
if (period.getDetachmentDate().isAfter(referenceDate1) && !period.getDetachmentDate().isAfter(referenceDate2)) {
pvDiff += periodPricer.presentValue(period, discountFactors);
}
}
return pvDiff;
}
// compute pv of coupon payment(s) s.t. referenceDate1 < coupon <= referenceDate2
double presentValueCouponWithZSpread(
ResolvedFixedCouponBond expanded,
IssuerCurveDiscountFactors discountFactors,
LocalDate referenceDate1,
LocalDate referenceDate2,
double zSpread,
CompoundedRateType compoundedRateType,
int periodsPerYear) {
double pvDiff = 0d;
for (FixedCouponBondPaymentPeriod period : expanded.getPeriodicPayments()) {
if (period.getDetachmentDate().isAfter(referenceDate1) && !period.getDetachmentDate().isAfter(referenceDate2)) {
pvDiff += periodPricer.presentValueWithSpread(period, discountFactors, zSpread, compoundedRateType, periodsPerYear);
}
}
return pvDiff;
}
//-------------------------------------------------------------------------
// extracts the repo curve discount factors for the bond
static RepoCurveDiscountFactors repoCurveDf(ResolvedFixedCouponBond bond, LegalEntityDiscountingProvider provider) {
return provider.repoCurveDiscountFactors(bond.getSecurityId(), bond.getLegalEntityId(), bond.getCurrency());
}
// extracts the issuer curve discount factors for the bond
static IssuerCurveDiscountFactors issuerCurveDf(ResolvedFixedCouponBond bond, LegalEntityDiscountingProvider provider) {
return provider.issuerCurveDiscountFactors(bond.getLegalEntityId(), bond.getCurrency());
}
}