/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.interestrate.bond.provider;
import static com.opengamma.financial.convention.yield.SimpleYieldConvention.INDEX_LINKED_FLOAT;
import static com.opengamma.financial.convention.yield.SimpleYieldConvention.UK_IL_BOND;
import static com.opengamma.financial.convention.yield.SimpleYieldConvention.US_IL_REAL;
import org.apache.commons.lang.Validate;
import com.opengamma.analytics.financial.instrument.inflation.CouponInflationGearing;
import com.opengamma.analytics.financial.interestrate.bond.definition.BondCapitalIndexedSecurity;
import com.opengamma.analytics.financial.interestrate.inflation.derivative.CouponInflationZeroCouponInterpolationGearing;
import com.opengamma.analytics.financial.interestrate.inflation.derivative.CouponInflationZeroCouponMonthlyGearing;
import com.opengamma.analytics.financial.interestrate.payments.derivative.Coupon;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponFixed;
import com.opengamma.analytics.financial.provider.calculator.inflation.NetAmountInflationCalculator;
import com.opengamma.analytics.financial.provider.calculator.inflation.PresentValueCurveSensitivityDiscountingInflationCalculator;
import com.opengamma.analytics.financial.provider.calculator.inflation.PresentValueDiscountingInflationCalculator;
import com.opengamma.analytics.financial.provider.description.inflation.InflationProviderDecorated;
import com.opengamma.analytics.financial.provider.description.inflation.InflationProviderInterface;
import com.opengamma.analytics.financial.provider.sensitivity.inflation.MultipleCurrencyInflationSensitivity;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.rootfinding.BracketRoot;
import com.opengamma.analytics.math.rootfinding.BrentSingleRootFinder;
import com.opengamma.analytics.math.rootfinding.RealSingleRootFinder;
import com.opengamma.financial.convention.yield.YieldConvention;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.MultipleCurrencyAmount;
/**
* TODO : remove when inflation curves with issuer are integrated
*/
public class BondCapitalIndexedSecurityDiscountingMethodWithoutIssuer {
/**
* The unique instance of the class.
*/
private static final BondCapitalIndexedSecurityDiscountingMethodWithoutIssuer INSTANCE = new BondCapitalIndexedSecurityDiscountingMethodWithoutIssuer();
/**
* Return the class instance.
* @return The instance.
*/
public static BondCapitalIndexedSecurityDiscountingMethodWithoutIssuer getInstance() {
return INSTANCE;
}
/**
* The present value inflation calculator (for the different parts of the bond transaction).
*/
private static final PresentValueDiscountingInflationCalculator PVIC = PresentValueDiscountingInflationCalculator.getInstance();
private static final NetAmountInflationCalculator NAIC = NetAmountInflationCalculator.getInstance();
private static final PresentValueCurveSensitivityDiscountingInflationCalculator PVCSIC = PresentValueCurveSensitivityDiscountingInflationCalculator.getInstance();
//TODO: REVIEW: Method depends on Calculator; Calculator would depend on Method (code duplicated to avoid circularity).
/**
* The root bracket used for yield finding.
*/
private static final BracketRoot BRACKETER = new BracketRoot();
/**
* The root finder used for yield finding.
*/
private static final RealSingleRootFinder ROOT_FINDER = new BrentSingleRootFinder();
/**
* Computes the present value of a capital indexed bound by index estimation and discounting. The value is the value of the nominal and the coupons but not the settlement.
* @param bond The bond.
* @param provider The provider.
* @return The present value.
*/
public MultipleCurrencyAmount presentValue(final BondCapitalIndexedSecurity<?> bond, final InflationProviderInterface provider) {
ArgumentChecker.notNull(bond, "Bond");
final MultipleCurrencyAmount pvNominal = bond.getNominal().accept(PVIC, provider);
final MultipleCurrencyAmount pvCoupon = bond.getCoupon().accept(PVIC, provider);
return pvNominal.plus(pvCoupon);
}
/**
* Computes the security present value from a quoted clean real price. The real accrued are added to the clean real price,
* the result is multiplied by the inflation index ratio and then discounted from settlement time to 0 with the discounting curve.
* @param bondCapitalIndexedSecurity The bond security.
* @param market The market.
* @param cleanPriceReal The clean price.
* @return The present value.
*/
public MultipleCurrencyAmount presentValueFromCleanPriceReal(final BondCapitalIndexedSecurity<?> bondCapitalIndexedSecurity, final InflationProviderInterface market, final double cleanPriceReal) {
Validate.notNull(bondCapitalIndexedSecurity, "Coupon");
Validate.notNull(market, "Market");
final double notional = bondCapitalIndexedSecurity.getCoupon().getNthPayment(0).getNotional();
final double dirtyPriceReal = cleanPriceReal + bondCapitalIndexedSecurity.getAccruedInterest() / notional;
final MultipleCurrencyAmount pv = bondCapitalIndexedSecurity.getSettlement().accept(PVIC, market.getInflationProvider());
return pv.multipliedBy(dirtyPriceReal);
}
/**
* Calculates the accrued interest for a fixed-coupon bond using the clean price. The accrued interest is defined
* as dirty price - clean price.
* @param bond The bond, not null
* @param cleanPrice The clean price
* @return The accrued interest
*/
public double accruedInterestFromCleanRealPrice(final BondCapitalIndexedSecurity<?> bond, final double cleanPrice) {
ArgumentChecker.notNull(bond, "bond");
return dirtyRealPriceFromCleanRealPrice(bond, cleanPrice) - cleanPrice;
}
/**
* Calculates the accrued interest for a fixed-coupon bond using the clean price. The accrued interest is defined
* as dirty price - clean price.
* @param bond The bond, not null
* @param yield The yield
* @return The accrued interest
*/
public double accruedInterestFromCleanYield(final BondCapitalIndexedSecurity<?> bond, final double yield) {
ArgumentChecker.notNull(bond, "bond");
return dirtyPriceFromRealYield(bond, yield) - cleanPriceFromYield(bond, yield);
}
/**
* Computes the clean real price of a bond security from a dirty real price.
* @param bond The bond security.
* @param dirtyPrice The dirty price.
* @return The clean price.
*/
public double cleanRealPriceFromDirtyRealPrice(final BondCapitalIndexedSecurity<?> bond, final double dirtyPrice) {
final double notional = bond.getCoupon().getNthPayment(0).getNotional();
return dirtyPrice - bond.getAccruedInterest() / notional;
}
/**
* Computes the clean nominal price of a bond security from a dirty real price.
* @param bond The bond security.
* @param dirtyPrice The dirty price.
* @return The clean price.
*/
public double cleanNominalPriceFromDirtyNominalPrice(final BondCapitalIndexedSecurity<?> bond, final double dirtyPrice) {
final double notional = bond.getCoupon().getNthPayment(0).getNotional();
final double rpibase = bond.getIndexStartValue();
final double rpiLast = bond.getLastIndexKnownFixing();
final double indexRatio = rpiLast / rpibase;
return dirtyPrice - bond.getAccruedInterest() / notional * indexRatio;
}
/**
* Computes the clean price of a bond security from curves.
* @param bond The bond security.
* @param issuerMulticurves The issuer and multi-curves provider.
* @return The clean price.
*/
public double cleanRealPriceFromCurves(final BondCapitalIndexedSecurity<?> bond, final InflationProviderInterface issuerMulticurves) {
final double dirtyPrice = dirtyRealPriceFromCurves(bond, issuerMulticurves);
return cleanRealPriceFromDirtyRealPrice(bond, dirtyPrice);
}
/**
* Compute the dirty price of a bond security from curves.
* @param bond The bond security.
* @param issuerMulticurves The issuer and multi-curves provider.
* @return The dirty price.
*/
public double dirtyRealPriceFromCurves(final BondCapitalIndexedSecurity<?> bond, final InflationProviderInterface issuerMulticurves) {
ArgumentChecker.notNull(bond, "Bond");
ArgumentChecker.notNull(issuerMulticurves, "Issuer and multi-curves provider");
final MultipleCurrencyAmount pv = presentValue(bond, issuerMulticurves);
final double df = issuerMulticurves.getMulticurveProvider().getDiscountFactor(bond.getCurrency(), bond.getSettlementTime());
final double notional = bond.getCoupon().getNthPayment(0).getNotional();
return pv.getAmount(bond.getCurrency()) / df / notional;
}
/**
* Computes the dirty real price of a bond security from the clean real price.
* @param bond The bond security.
* @param cleanPrice The clean price.
* @return The clean price.
*/
public double dirtyRealPriceFromCleanRealPrice(final BondCapitalIndexedSecurity<?> bond, final double cleanPrice) {
final double notional = bond.getCoupon().getNthPayment(0).getNotional();
return cleanPrice + bond.getAccruedInterest() / notional;
}
/**
* The net amount paid at settlement date for a given clean real price.
* @param bond The bond.
* @param market The market.
* @param cleanPriceReal The clean real price.
* @return The net amount.
*/
public MultipleCurrencyAmount netAmount(final BondCapitalIndexedSecurity<Coupon> bond, final InflationProviderInterface market, final double cleanPriceReal) {
final double notional = bond.getCoupon().getNthPayment(0).getNotional();
final double netAmountRealByUnit = cleanPriceReal + bond.getAccruedInterest() / notional;
final MultipleCurrencyAmount netAmount = bond.getSettlement().accept(NAIC, market.getInflationProvider());
return netAmount.multipliedBy(netAmountRealByUnit);
}
/**
* Computes the dirty (real or nominal depending of the convention) price from the conventional real yield.
* @param bond The bond security.
* @param yield The bond yield.
* @return The dirty price.
*/
public double dirtyPriceFromRealYield(final BondCapitalIndexedSecurity<?> bond, final double yield) {
Validate.isTrue(bond.getNominal().getNumberOfPayments() == 1, "Yield: more than one nominal repayment.");
final int nbCoupon = bond.getCoupon().getNumberOfPayments();
final YieldConvention yieldConvention = bond.getYieldConvention();
if (yieldConvention.equals(US_IL_REAL)) {
// Coupon period rate to next coupon and simple rate from next coupon to settlement.
double pvAtFirstCoupon;
if (Math.abs(yield) > 1.0E-8) {
final double factorOnPeriod = 1 + yield / bond.getCouponPerYear();
final double vn = Math.pow(factorOnPeriod, 1 - nbCoupon);
pvAtFirstCoupon = ((CouponInflationGearing) bond.getCoupon().getNthPayment(0)).getFactor() / yield * (factorOnPeriod - vn) + vn;
} else {
pvAtFirstCoupon = ((CouponInflationGearing) bond.getCoupon().getNthPayment(0)).getFactor() / bond.getCouponPerYear() * nbCoupon + 1;
}
return pvAtFirstCoupon / (1 + bond.getAccrualFactorToNextCoupon() * yield / bond.getCouponPerYear());
}
if (yieldConvention.equals(INDEX_LINKED_FLOAT)) {
double realRate = 0.0;
if (bond.getCoupon().getNthPayment(1) instanceof CouponInflationGearing) {
realRate = ((CouponInflationGearing) bond.getCoupon().getNthPayment(1)).getFactor() / bond.getCouponPerYear();
} else if (bond.getCoupon().getNthPayment(1) instanceof CouponFixed) {
realRate = ((CouponFixed) bond.getCoupon().getNthPayment(1)).getFixedRate();
}
double firstYearFraction = 0.0;
double firstCouponEndFixingTime = 0.0;
if (bond.getCoupon().getNthPayment(0) instanceof CouponInflationZeroCouponInterpolationGearing) {
firstYearFraction = ((CouponInflationZeroCouponInterpolationGearing) bond.getCoupon().getNthPayment(0)).getPaymentYearFraction();
firstCouponEndFixingTime = ((CouponInflationZeroCouponInterpolationGearing) bond.getCoupon().getNthPayment(0)).getReferenceEndTime()[1];
} else if (bond.getCoupon().getNthPayment(0) instanceof CouponInflationZeroCouponMonthlyGearing) {
firstYearFraction = ((CouponInflationZeroCouponMonthlyGearing) bond.getCoupon().getNthPayment(0)).getPaymentYearFraction();
firstCouponEndFixingTime = ((CouponInflationZeroCouponMonthlyGearing) bond.getCoupon().getNthPayment(0)).getReferenceEndTime();
}
final double firstCashFlow = firstYearFraction * realRate;
final double v = 1 / (1 + yield / bond.getCouponPerYear());
final double rpibase = bond.getIndexStartValue();
final double rpiLast = bond.getLastIndexKnownFixing();
final int nbMonth = (int) ((firstCouponEndFixingTime - bond.getLastKnownFixingTime()) * 12);
final double u = Math.pow(1 / (1 + .03), .5);
final double a = rpiLast / rpibase * Math.pow(u, 2 * nbMonth / 12);
if (bond.getCoupon().getNumberOfPayments() == 1) {
return Math.pow(u * v, bond.getAccrualFactorToNextCoupon()) * (firstCashFlow + 1) * a / u;
} else {
double secondYearFraction = 0.0;
if (bond.getCoupon().getNthPayment(1) instanceof CouponInflationZeroCouponInterpolationGearing) {
secondYearFraction = ((CouponInflationZeroCouponInterpolationGearing) bond.getCoupon().getNthPayment(1)).getPaymentYearFraction();
} else if (bond.getCoupon().getNthPayment(1) instanceof CouponInflationZeroCouponMonthlyGearing) {
secondYearFraction = ((CouponInflationZeroCouponMonthlyGearing) bond.getCoupon().getNthPayment(1)).getPaymentYearFraction();
}
final double secondCashFlow = secondYearFraction * realRate;
final double vn = Math.pow(v, nbCoupon - 1);
final double pvAtFirstCoupon = firstCashFlow + secondCashFlow * u * v + a * realRate * v * v * (1 - vn / v) / (1 - v) + a * vn;
return pvAtFirstCoupon * Math.pow(u * v, bond.getAccrualFactorToNextCoupon());
}
}
if (yieldConvention.equals(UK_IL_BOND)) {
double firstYearFraction = 0.0;
final double realRate = ((CouponInflationGearing) bond.getCoupon().getNthPayment(1)).getFactor() / bond.getCouponPerYear();
if (bond.getCoupon().getNthPayment(0) instanceof CouponInflationZeroCouponInterpolationGearing) {
firstYearFraction = ((CouponInflationZeroCouponInterpolationGearing) bond.getCoupon().getNthPayment(0)).getPaymentYearFraction();
} else if (bond.getCoupon().getNthPayment(0) instanceof CouponInflationZeroCouponMonthlyGearing) {
firstYearFraction = ((CouponInflationZeroCouponMonthlyGearing) bond.getCoupon().getNthPayment(0)).getPaymentYearFraction();
}
final double firstCashFlow = firstYearFraction * realRate;
final double v = 1 / (1 + yield / bond.getCouponPerYear());
if (bond.getCoupon().getNumberOfPayments() == 1) {
return Math.pow(v, bond.getAccrualFactorToNextCoupon()) * (firstCashFlow + 1);
} else {
double secondYearFraction = 0.0;
if (bond.getCoupon().getNthPayment(1) instanceof CouponInflationZeroCouponInterpolationGearing) {
secondYearFraction = ((CouponInflationZeroCouponInterpolationGearing) bond.getCoupon().getNthPayment(1)).getPaymentYearFraction();
} else if (bond.getCoupon().getNthPayment(1) instanceof CouponInflationZeroCouponMonthlyGearing) {
secondYearFraction = ((CouponInflationZeroCouponMonthlyGearing) bond.getCoupon().getNthPayment(1)).getPaymentYearFraction();
}
final double secondCashFlow = secondYearFraction * realRate;
final double vn = Math.pow(v, nbCoupon - 1);
final double pvAtFirstCoupon = firstCashFlow + secondCashFlow * v + realRate * v * v * (1 - vn / v) / (1 - v) + vn;
return pvAtFirstCoupon * Math.pow(v, bond.getAccrualFactorToNextCoupon());
}
}
throw new UnsupportedOperationException("The convention " + bond.getYieldConvention().getName() + " is not supported.");
}
/**
* Computes the clean price (real or nominal depending on the convention) from the conventional real yield.
* @param bond The bond security.
* @param yield The bond yield.
* @return The clean price.
*/
public double cleanPriceFromYield(final BondCapitalIndexedSecurity<?> bond, final double yield) {
Validate.isTrue(bond.getNominal().getNumberOfPayments() == 1, "Yield: more than one nominal repayment.");
final double dirtyPrice = dirtyPriceFromRealYield(bond, yield);
return cleanRealPriceFromDirtyRealPrice(bond, dirtyPrice);
}
/**
* Compute the conventional yield from the dirty price.
* @param bond The bond security.
* @param dirtyPrice The bond dirty price.
* @return The yield.
*/
public double yieldRealFromDirtyRealPrice(final BondCapitalIndexedSecurity<?> bond, final double dirtyPrice) {
/**
* Inner function used to find the yield.
*/
final Function1D<Double, Double> priceResidual = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double y) {
return dirtyPriceFromRealYield(bond, y) - dirtyPrice;
}
};
final double[] range = BRACKETER.getBracketedPoints(priceResidual, -0.05, 0.10);
final double yield = ROOT_FINDER.getRoot(priceResidual, range[0], range[1]);
return yield;
}
/**
* Computes the present value sensitivity of a capital indexed bound by index estimation and discounting. The sensitivity is the sensitivity of the nominal and the coupons but not the settlement.
* @param bond The bond.
* @param provider The provider.
* @return The present value.
*/
public MultipleCurrencyInflationSensitivity presentValueCurveSensitivity(final BondCapitalIndexedSecurity<?> bond, final InflationProviderInterface provider) {
ArgumentChecker.notNull(bond, "Bond");
final MultipleCurrencyInflationSensitivity sensitivityNominal = bond.getNominal().accept(PVCSIC, provider);
final MultipleCurrencyInflationSensitivity sensitivityCoupon = bond.getCoupon().accept(PVCSIC, provider);
return sensitivityNominal.plus(sensitivityCoupon);
}
/**
* Computes the bill yield from the curves. The yield is in the bill yield convention.
* @param bond The bond.
* @param provider The inflation and multi-curves provider.
* @return The yield.
*/
public double yieldRealFromCurves(final BondCapitalIndexedSecurity<?> bond, final InflationProviderInterface provider) {
ArgumentChecker.notNull(bond, "Bond");
ArgumentChecker.notNull(provider, "inflation and multi-curves provider");
final double dirtyPrice = dirtyRealPriceFromCurves(bond, provider);
final double yield = yieldRealFromDirtyRealPrice(bond, dirtyPrice);
return yield;
}
/**
* Calculates the modified duration from a standard yield.
* @param bond The bond
* @param yield The yield
* @return The modified duration
*/
private double modifiedDurationFromYieldStandard(final BondCapitalIndexedSecurity<?> bond, final double yield) {
ArgumentChecker.isTrue(bond.getNominal().getNumberOfPayments() == 1, "Yield: more than one nominal repayment.");
final int nbCoupon = bond.getCoupon().getNumberOfPayments();
final double nominal = bond.getNominal().getNthPayment(0).getNotional();
final double factorOnPeriod = 1 + yield / bond.getCouponPerYear();
double mdAtFirstCoupon = 0;
double pvAtFirstCoupon = 0;
for (int loopcpn = 0; loopcpn < nbCoupon; loopcpn++) {
mdAtFirstCoupon += bond.getCoupon().getNthPayment(loopcpn).getNotional() / Math.pow(factorOnPeriod, loopcpn + 1) * (loopcpn + bond.getAccrualFactorToNextCoupon()) / bond.getCouponPerYear();
pvAtFirstCoupon += bond.getCoupon().getNthPayment(loopcpn).getNotional() / Math.pow(factorOnPeriod, loopcpn);
}
mdAtFirstCoupon += nominal / Math.pow(factorOnPeriod, nbCoupon) * (nbCoupon - 1 + bond.getAccrualFactorToNextCoupon()) / bond.getCouponPerYear();
pvAtFirstCoupon += nominal / Math.pow(factorOnPeriod, nbCoupon - 1);
final double pv = pvAtFirstCoupon * Math.pow(factorOnPeriod, -bond.getAccrualFactorToNextCoupon());
final double md = mdAtFirstCoupon * Math.pow(factorOnPeriod, -bond.getAccrualFactorToNextCoupon()) / pv;
return md;
}
/**
* Computes the modified duration of a bond from the curves.
* @param bond The bond security.
* @param issuerMulticurves The issuer and multi-curves provider.
* @return The modified duration.
*/
public double modifiedDurationFromCurves(final BondCapitalIndexedSecurity<?> bond, final InflationProviderInterface issuerMulticurves) {
final double yield = yieldRealFromCurves(bond, issuerMulticurves);
return modifiedDurationFromYieldStandard(bond, yield);
}
/**
* Compute the conventional yield from the clean price.
* @param bond The bond security.
* @param cleanPrice The bond clean price.
* @return The yield.
*/
public double yieldRealFromCleanRealPrice(final BondCapitalIndexedSecurity<?> bond, final double cleanPrice) {
final double dirtyPrice = dirtyRealPriceFromCleanRealPrice(bond, cleanPrice);
final double yield = yieldRealFromDirtyRealPrice(bond, dirtyPrice);
return yield;
}
/**
* Computes the modified duration of a bond from the clean price.
* @param bond The bond security.
* @param cleanPrice The bond clean price.
* @return The modified duration.
*/
public double modifiedDurationFromCleanPrice(final BondCapitalIndexedSecurity<?> bond, final double cleanPrice) {
final double yield = yieldRealFromCleanRealPrice(bond, cleanPrice);
return modifiedDurationFromYieldStandard(bond, yield);
}
/**
* Calculates the convexity from a standard yield.
* @param bond The bond
* @param yield The yield
* @return The convexity
*/
private double convexityFromYieldStandard(final BondCapitalIndexedSecurity<?> bond, final double yield) {
ArgumentChecker.isTrue(bond.getNominal().getNumberOfPayments() == 1, "Yield: more than one nominal repayment.");
final int nbCoupon = bond.getCoupon().getNumberOfPayments();
final double nominal = bond.getNominal().getNthPayment(bond.getNominal().getNumberOfPayments() - 1).getNotional();
final double factorOnPeriod = 1 + yield / bond.getCouponPerYear();
double cvAtFirstCoupon = 0;
double pvAtFirstCoupon = 0;
for (int loopcpn = 0; loopcpn < nbCoupon; loopcpn++) {
cvAtFirstCoupon += bond.getCoupon().getNthPayment(loopcpn).getNotional() / Math.pow(factorOnPeriod, loopcpn + 2) * (loopcpn + bond.getAccrualFactorToNextCoupon())
* (loopcpn + bond.getAccrualFactorToNextCoupon() + 1) / (bond.getCouponPerYear() * bond.getCouponPerYear());
pvAtFirstCoupon += bond.getCoupon().getNthPayment(loopcpn).getNotional() / Math.pow(factorOnPeriod, loopcpn);
}
cvAtFirstCoupon += nominal / Math.pow(factorOnPeriod, nbCoupon + 1) * (nbCoupon - 1 + bond.getAccrualFactorToNextCoupon()) * (nbCoupon + bond.getAccrualFactorToNextCoupon())
/ (bond.getCouponPerYear() * bond.getCouponPerYear());
pvAtFirstCoupon += nominal / Math.pow(factorOnPeriod, nbCoupon - 1);
final double pv = pvAtFirstCoupon * Math.pow(factorOnPeriod, -bond.getAccrualFactorToNextCoupon());
final double cv = cvAtFirstCoupon * Math.pow(factorOnPeriod, -bond.getAccrualFactorToNextCoupon()) / pv;
return cv;
}
/**
* Computes the convexity of a bond from the curves.
* @param bond The bond security.
* @param issuerMulticurves The issuer and multi-curves provider.
* @return The convexity.
*/
public double convexityFromCurves(final BondCapitalIndexedSecurity<?> bond, final InflationProviderInterface issuerMulticurves) {
final double yield = yieldRealFromCurves(bond, issuerMulticurves);
return convexityFromYieldFiniteDifference(bond, yield);
}
/**
* Calculates the modified duration from a standard yield.
* @param bond The bond
* @param yield The yield
* @return The modified duration
*/
public double modifiedDurationFromYieldFiniteDifference(final BondCapitalIndexedSecurity<?> bond, final double yield) {
ArgumentChecker.isTrue(bond.getNominal().getNumberOfPayments() == 1, "Yield: more than one nominal repayment.");
final double price = cleanPriceFromYield(bond, yield);
final double priceplus = cleanPriceFromYield(bond, yield + 10e-6);
final double priceminus = cleanPriceFromYield(bond, yield - 10e-6);
return -(priceplus - priceminus) / (2 * price * 10e-6);
}
/**
* Calculates the modified duration from a standard yield.
* @param bond The bond
* @param yield The yield
* @return The modified duration
*/
public double convexityFromYieldFiniteDifference(final BondCapitalIndexedSecurity<?> bond, final double yield) {
ArgumentChecker.isTrue(bond.getNominal().getNumberOfPayments() == 1, "Yield: more than one nominal repayment.");
ArgumentChecker.isTrue(bond.getNominal().getNumberOfPayments() == 1, "Yield: more than one nominal repayment.");
final double price = cleanPriceFromYield(bond, yield);
final double priceplus = cleanPriceFromYield(bond, yield + 10e-6);
final double priceminus = cleanPriceFromYield(bond, yield - 10e-6);
return (priceplus - 2 * price + priceminus) / (price * 10e-6 * 10e-6);
}
/**
* Computes the present value of a bond security from z-spread. The z-spread is a parallel shift applied to the discounting curve associated to the bond (Issuer Entity).
* The parallel shift is done in the curve convention.
* @param bond The bond security.
* @param issuerMulticurves The issuer and multi-curves provider.
* @param zSpread The z-spread.
* @return The present value.
*/
public MultipleCurrencyAmount presentValueFromZSpread(final BondCapitalIndexedSecurity<?> bond, final InflationProviderInterface issuerMulticurves, final double zSpread) {
final InflationProviderInterface issuerShifted = new InflationProviderDecorated(issuerMulticurves, zSpread);
return presentValue(bond, issuerShifted);
}
/**
* Computes the present value of a bond security from z-spread. The z-spread is a parallel shift applied to the discounting curve associated to the bond (Issuer Entity).
* The parallel shift is done in the curve convention.
* @param bond The bond security.
* @param issuerMulticurves The issuer and multi-curves provider.
* @param zSpread The z-spread.
* @return The present value.
*/
public double cleanPriceFromZSpread(final BondCapitalIndexedSecurity<?> bond, final InflationProviderInterface issuerMulticurves, final double zSpread) {
final InflationProviderInterface issuerShifted = new InflationProviderDecorated(issuerMulticurves, zSpread);
return cleanRealPriceFromCurves(bond, issuerShifted);
}
/**
* Computes a bond z-spread from the curves and a present value.
* The z-spread is a parallel shift applied to the discounting curve associated to the bond (Issuer Entity) to match the present value.
* @param bond The bond.
* @param issuerMulticurves The issuer and multi-curves provider.
* @param cleanRealPrice The target clean real price.
* @return The z-spread.
*/
public double zSpreadFromCurvesAndCleanRealPriceDirect(final BondCapitalIndexedSecurity<?> bond, final InflationProviderInterface issuerMulticurves, final double cleanRealPrice) {
ArgumentChecker.notNull(bond, "Bond");
ArgumentChecker.notNull(issuerMulticurves, "Issuer and multi-curves provider");
final Function1D<Double, Double> residual = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double z) {
return cleanPriceFromZSpread(bond, issuerMulticurves, z) - cleanRealPrice;
}
};
final double[] range = BRACKETER.getBracketedPoints(residual, -0.5, 0.5); // Starting range is [-1%, 1%]
return ROOT_FINDER.getRoot(residual, range[0], range[1]);
}
/**
* Computes a bond z-spread from the curves and a present value.
* The z-spread is a parallel shift applied to the discounting curve associated to the bond (Issuer Entity) to match the present value.
* @param bond The bond.
* @param issuerMulticurves The issuer and multi-curves provider.
* @param pv The target present value.
* @return The z-spread.
*/
public double zSpreadFromCurvesAndPV(final BondCapitalIndexedSecurity<?> bond, final InflationProviderInterface issuerMulticurves, final MultipleCurrencyAmount pv) {
ArgumentChecker.notNull(bond, "Bond");
ArgumentChecker.notNull(issuerMulticurves, "Issuer and multi-curves provider");
final Currency ccy = bond.getCurrency();
final Function1D<Double, Double> residual = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double z) {
return presentValueFromZSpread(bond, issuerMulticurves, z).getAmount(ccy) - pv.getAmount(ccy);
}
};
final double[] range = BRACKETER.getBracketedPoints(residual, -0.5, 0.5); // Starting range is [-1%, 1%]
return ROOT_FINDER.getRoot(residual, range[0], range[1]);
}
/**
* Computes a bond z-spread from the curves and a clean price.
* The z-spread is a parallel shift applied to the discounting curve associated to the bond (Issuer Entity) to match the CleanPrice present value.
* @param bond The bond.
* @param issuerMulticurves The issuer and multi-curves provider.
* @param cleanPrice The target clean price.
* @return The z-spread.
*/
public double zSpreadFromCurvesAndCleanPrice(final BondCapitalIndexedSecurity<Coupon> bond, final InflationProviderInterface issuerMulticurves, final double cleanPrice) {
return zSpreadFromCurvesAndPV(bond, issuerMulticurves, presentValueFromCleanPriceReal(bond, issuerMulticurves, cleanPrice));
}
}