/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.interestrate.bond.provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.opengamma.analytics.financial.interestrate.bond.definition.BondFixedSecurity;
import com.opengamma.analytics.financial.interestrate.bond.definition.BondFixedTransaction;
import com.opengamma.analytics.financial.interestrate.bond.definition.BondIborTransaction;
import com.opengamma.analytics.financial.interestrate.bond.definition.BondSecurity;
import com.opengamma.analytics.financial.interestrate.bond.definition.BondTransaction;
import com.opengamma.analytics.financial.interestrate.payments.derivative.Coupon;
import com.opengamma.analytics.financial.interestrate.payments.derivative.Payment;
import com.opengamma.analytics.financial.interestrate.payments.derivative.PaymentFixed;
import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueCurveSensitivityDiscountingCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueDiscountingCalculator;
import com.opengamma.analytics.financial.provider.description.interestrate.IssuerProviderInterface;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscountingDecoratedIssuer;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderInterface;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MulticurveSensitivity;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.MultipleCurrencyAmount;
/**
* Class with methods related to bond transaction valued by discounting.
*/
public final class BondTransactionDiscountingMethod {
/** The logger */
private static final Logger LOGGER = LoggerFactory.getLogger(BondTransactionDiscountingMethod.class);
/**
* The unique instance of the class.
*/
private static final BondTransactionDiscountingMethod INSTANCE = new BondTransactionDiscountingMethod();
/**
* Return the class instance.
* @return The instance.
*/
public static BondTransactionDiscountingMethod getInstance() {
return INSTANCE;
}
/**
* Constructor
*/
private BondTransactionDiscountingMethod() {
}
/** The present value calculator (for the different parts of the bond transaction). */
private static final PresentValueDiscountingCalculator PVDC = PresentValueDiscountingCalculator.getInstance();
/** The present value calculator (for the different parts of the bond transaction). */
private static final PresentValueCurveSensitivityDiscountingCalculator PVCSDC =
PresentValueCurveSensitivityDiscountingCalculator.getInstance();
/** Bond security calculation method **/
private static final BondSecurityDiscountingMethod METHOD_BOND_SECURITY = BondSecurityDiscountingMethod.getInstance();
/**
* Compute the present value of a fixed coupon bond transaction.
* @param bond The bond transaction.
* @param issuerMulticurves The issuer and multi-curves provider.
* @return The present value.
*/
public MultipleCurrencyAmount presentValue(final BondFixedTransaction bond,
final IssuerProviderInterface issuerMulticurves) {
ArgumentChecker.notNull(bond, "Bond");
final Currency ccy = bond.getBondTransaction().getCurrency();
final MulticurveProviderInterface multicurvesDecorated =
new MulticurveProviderDiscountingDecoratedIssuer(issuerMulticurves, ccy, bond.getBondTransaction().getIssuerEntity());
final MultipleCurrencyAmount pvNominal = bond.getBondTransaction().getNominal().accept(PVDC, multicurvesDecorated);
final MultipleCurrencyAmount pvCoupon = bond.getBondTransaction().getCoupon().accept(PVDC, multicurvesDecorated);
final double settlementAmount = -(bond.getTransactionPrice() *
bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional() +
bond.getBondTransaction().getAccruedInterest()) * bond.getQuantity();
final PaymentFixed settlement = new PaymentFixed(bond.getBondTransaction().getCurrency(),
bond.getBondTransaction().getSettlementTime(), settlementAmount);
final MultipleCurrencyAmount pvSettlement = settlement.accept(PVDC, issuerMulticurves.getMulticurveProvider());
return pvNominal.plus(pvCoupon).multipliedBy(bond.getQuantity()).plus(pvSettlement);
}
/**
* Compute the present value of a Ibor coupon bond (FRN) transaction.
* @param bond The bond transaction.
* @param issuerMulticurves The issuer and multi-curves provider.
* @return The present value.
*/
public MultipleCurrencyAmount presentValue(final BondIborTransaction bond,
final IssuerProviderInterface issuerMulticurves) {
ArgumentChecker.notNull(bond, "Bond");
final Currency ccy = bond.getBondTransaction().getCurrency();
final MulticurveProviderInterface multicurvesDecorated = new MulticurveProviderDiscountingDecoratedIssuer(issuerMulticurves, ccy, bond.getBondTransaction().getIssuerEntity());
final MultipleCurrencyAmount pvNominal = bond.getBondTransaction().getNominal().accept(PVDC, multicurvesDecorated);
final MultipleCurrencyAmount pvCoupon = bond.getBondTransaction().getCoupon().accept(PVDC, multicurvesDecorated);
final double settlementAmount = bond.getTransactionPrice() * bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional(); //FIXME: add accrued.
LOGGER.error("The FRN settlement amount does not include the accrued interest.");
final PaymentFixed settlement = new PaymentFixed(bond.getBondTransaction().getCurrency(), bond.getBondTransaction().getSettlementTime(), settlementAmount);
final MultipleCurrencyAmount pvSettlement = settlement.accept(PVDC, issuerMulticurves.getMulticurveProvider());
return pvNominal.plus(pvCoupon).multipliedBy(bond.getQuantity()).plus(pvSettlement);
}
/**
* Compute the present value of a bond transaction from its clean price.
* @param bond The bond transaction.
* @param issuerMulticurves The issuer and multi-curves provider.
* @param cleanPrice The bond clean price.
* @return The present value.
*/
public MultipleCurrencyAmount presentValueFromCleanPrice(
final BondTransaction<? extends BondSecurity<? extends Payment, ? extends Coupon>> bond,
final IssuerProviderInterface issuerMulticurves, final double cleanPrice) {
ArgumentChecker.notNull(bond, "Bond");
ArgumentChecker.isTrue(bond instanceof BondFixedTransaction, "Present value from clean price only for fixed coupon bond");
final Currency ccy = bond.getBondTransaction().getCurrency();
final MulticurveProviderInterface multicurvesDecorated = new MulticurveProviderDiscountingDecoratedIssuer(issuerMulticurves, ccy, bond.getBondTransaction().getIssuerEntity());
final BondFixedTransaction bondFixed = (BondFixedTransaction) bond;
final double dfSettle = issuerMulticurves.getMulticurveProvider().getDiscountFactor(ccy, bondFixed.getBondStandard().getSettlementTime());
final MultipleCurrencyAmount pvPriceStandard = MultipleCurrencyAmount.of(ccy, (cleanPrice * bondFixed.getNotionalStandard() + bondFixed.getBondStandard().getAccruedInterest()) * dfSettle);
final MultipleCurrencyAmount pvNominalStandard = bond.getBondStandard().getNominal().accept(PVDC, multicurvesDecorated);
final MultipleCurrencyAmount pvCouponStandard = bond.getBondStandard().getCoupon().accept(PVDC, multicurvesDecorated);
final MultipleCurrencyAmount pvDiscountingStandard = pvNominalStandard.plus(pvCouponStandard);
final MultipleCurrencyAmount pvNominalTransaction = bond.getBondTransaction().getNominal().accept(PVDC, multicurvesDecorated);
final MultipleCurrencyAmount pvCouponTransaction = bond.getBondTransaction().getCoupon().accept(PVDC, multicurvesDecorated);
final MultipleCurrencyAmount pvDiscountingTransaction = pvNominalTransaction.plus(pvCouponTransaction);
double settlementAmount = -bond.getTransactionPrice() * bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional();
if (bond.getBondTransaction() instanceof BondFixedSecurity) {
settlementAmount -= ((BondFixedSecurity) bond.getBondTransaction()).getAccruedInterest();
}
settlementAmount *= bond.getQuantity();
final PaymentFixed settlement = new PaymentFixed(bond.getBondTransaction().getCurrency(), bond.getBondTransaction().getSettlementTime(), settlementAmount);
final MultipleCurrencyAmount pvSettlement = settlement.accept(PVDC, issuerMulticurves.getMulticurveProvider());
return (pvDiscountingTransaction.plus(pvDiscountingStandard.multipliedBy(-1d)).plus(pvPriceStandard)).multipliedBy(bond.getQuantity()).plus(pvSettlement);
}
/**
* Compute the present value of a bond transaction from its conventional yield.
* @param bond The bond transaction.
* @param issuerMulticurves The issuer and multi-curves provider.
* @param yield The bond conventional yield (in the bond convention).
* @return The present value.
*/
public MultipleCurrencyAmount presentValueFromYield(
final BondTransaction<? extends BondSecurity<? extends Payment, ? extends Coupon>> bond,
final IssuerProviderInterface issuerMulticurves, final double yield) {
ArgumentChecker.notNull(bond, "Bond");
ArgumentChecker.isTrue(bond instanceof BondFixedTransaction,
"Present value from clean price only for fixed coupon bond");
final Currency ccy = bond.getBondTransaction().getCurrency();
final MulticurveProviderInterface multicurvesDecorated = new MulticurveProviderDiscountingDecoratedIssuer(
issuerMulticurves, ccy, bond.getBondTransaction().getIssuerEntity());
final BondFixedTransaction bondFixed = (BondFixedTransaction) bond;
final double dfSettle = issuerMulticurves.getMulticurveProvider().
getDiscountFactor(ccy, bondFixed.getBondStandard().getSettlementTime());
final double dirtyPrice = METHOD_BOND_SECURITY.dirtyPriceFromYield(bondFixed.getBondStandard(), yield);
final MultipleCurrencyAmount pvPriceStandard = MultipleCurrencyAmount.of(ccy, dirtyPrice * dfSettle);
final MultipleCurrencyAmount pvNominalStandard = bond.getBondStandard().getNominal().accept(PVDC, multicurvesDecorated);
final MultipleCurrencyAmount pvCouponStandard = bond.getBondStandard().getCoupon().accept(PVDC, multicurvesDecorated);
final MultipleCurrencyAmount pvDiscountingStandard = pvNominalStandard.plus(pvCouponStandard);
final MultipleCurrencyAmount pvNominalTransaction = bond.getBondTransaction().getNominal().accept(PVDC, multicurvesDecorated);
final MultipleCurrencyAmount pvCouponTransaction = bond.getBondTransaction().getCoupon().accept(PVDC, multicurvesDecorated);
final MultipleCurrencyAmount pvDiscountingTransaction = pvNominalTransaction.plus(pvCouponTransaction);
double settlementAmount = -bond.getTransactionPrice() * bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional();
if (bond.getBondTransaction() instanceof BondFixedSecurity) {
settlementAmount -= ((BondFixedSecurity) bond.getBondTransaction()).getAccruedInterest();
}
settlementAmount *= bond.getQuantity();
final PaymentFixed settlement = new PaymentFixed(bond.getBondTransaction().getCurrency(),
bond.getBondTransaction().getSettlementTime(), settlementAmount);
final MultipleCurrencyAmount pvSettlement = settlement.accept(PVDC, issuerMulticurves.getMulticurveProvider());
return (pvDiscountingTransaction.plus(pvDiscountingStandard.multipliedBy(-1d)).plus(pvPriceStandard)).
multipliedBy(bond.getQuantity()).plus(pvSettlement);
}
/**
* Compute the present value sensitivity of a bond transaction.
* @param bond The bond transaction.
* @param issuerMulticurves The issuer and multi-curves provider.
* @return The present value sensitivity.
*/
public MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity(final BondFixedTransaction bond,
final IssuerProviderInterface issuerMulticurves) {
ArgumentChecker.notNull(bond, "Bond");
final Currency ccy = bond.getBondTransaction().getCurrency();
final MulticurveProviderInterface multicurvesDecorated = new MulticurveProviderDiscountingDecoratedIssuer(
issuerMulticurves, ccy, bond.getBondTransaction().getIssuerEntity());
final MultipleCurrencyMulticurveSensitivity pvcsNominal =
bond.getBondTransaction().getNominal().accept(PVCSDC, multicurvesDecorated);
final MultipleCurrencyMulticurveSensitivity pvcsCoupon =
bond.getBondTransaction().getCoupon().accept(PVCSDC, multicurvesDecorated);
final double settlementAmount = -(bond.getTransactionPrice() *
bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional() +
bond.getBondTransaction().getAccruedInterest()) * bond.getQuantity();
final PaymentFixed settlement = new PaymentFixed(bond.getBondTransaction().getCurrency(),
bond.getBondTransaction().getSettlementTime(), settlementAmount);
final MultipleCurrencyMulticurveSensitivity pvcsSettlement =
settlement.accept(PVCSDC, issuerMulticurves.getMulticurveProvider());
return pvcsNominal.plus(pvcsCoupon).multipliedBy(bond.getQuantity()).plus(pvcsSettlement);
}
public MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity(final BondIborTransaction bond,
final IssuerProviderInterface issuerMulticurves) {
ArgumentChecker.notNull(bond, "Bond");
final Currency ccy = bond.getBondTransaction().getCurrency();
final MulticurveProviderInterface multicurvesDecorated =
new MulticurveProviderDiscountingDecoratedIssuer(issuerMulticurves, ccy, bond.getBondTransaction().getIssuerEntity());
final MultipleCurrencyMulticurveSensitivity pvcsNominal =
bond.getBondTransaction().getNominal().accept(PVCSDC, multicurvesDecorated);
final MultipleCurrencyMulticurveSensitivity pvcsCoupon =
bond.getBondTransaction().getCoupon().accept(PVCSDC, multicurvesDecorated);
final double settlementAmount = bond.getTransactionPrice() *
bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional(); //FIXME: add accrued.
LOGGER.error("The FRN settlement amount does not include the accrued interests.");
final PaymentFixed settlement = new PaymentFixed(bond.getBondTransaction().getCurrency(),
bond.getBondTransaction().getSettlementTime(), settlementAmount);
final MultipleCurrencyMulticurveSensitivity pvcsSettlement =
settlement.accept(PVCSDC, issuerMulticurves.getMulticurveProvider());
return pvcsNominal.plus(pvcsCoupon).multipliedBy(bond.getQuantity()).plus(pvcsSettlement);
}
/**
* The par spread with respect to the trade price for which the present value of the bond transaction is 0.
* If that spread was added to the transaction price, the new transaction would have a present value of 0.
* @param bond The bond transaction.
* @param issuerMulticurves The issuer and multi-curves provider.
* @return The spread.
*/
public double parSpread(final BondFixedTransaction bond, final IssuerProviderInterface issuerMulticurves) {
ArgumentChecker.notNull(bond, "Bond");
ArgumentChecker.notNull(issuerMulticurves, "Issuer and multi-curves provider");
final Currency ccy = bond.getBondTransaction().getCurrency();
final PaymentFixed nominalAtSettlement = new PaymentFixed(bond.getBondTransaction().getCurrency(),
bond.getBondTransaction().getSettlementTime(), bond.getBondTransaction().getCoupon()
.getNthPayment(0).getNotional() * bond.getQuantity());
final double pvNominalAtSettlement =
nominalAtSettlement.accept(PVDC, issuerMulticurves.getMulticurveProvider()).getAmount(ccy);
return presentValue(bond, issuerMulticurves).getAmount(ccy) / pvNominalAtSettlement;
}
public double parSpread(final BondIborTransaction bond, final IssuerProviderInterface issuerMulticurves) {
ArgumentChecker.notNull(bond, "Bond");
ArgumentChecker.notNull(issuerMulticurves, "Issuer and multi-curves provider");
final Currency ccy = bond.getBondTransaction().getCurrency();
final PaymentFixed nominalAtSettlement = new PaymentFixed(bond.getBondTransaction().getCurrency(),
bond.getBondTransaction().getSettlementTime(), bond.getBondTransaction().getCoupon()
.getNthPayment(0).getNotional() * bond.getQuantity());
final double pvNominalAtSettlement = nominalAtSettlement.accept(PVDC,
issuerMulticurves.getMulticurveProvider()).getAmount(ccy);
return -presentValue(bond, issuerMulticurves).getAmount(ccy) / pvNominalAtSettlement;
}
/**
* The par spread with respect to the trade yield for which the present value of the bond transaction is 0.
* If that spread was added to the transaction yield, the new transaction would have a present value of 0.
* @param bond The bond transaction.
* @param issuerMulticurves The issuer and multi-curves provider.
* @return The spread.
*/
public double parSpreadYield(final BondFixedTransaction bond, final IssuerProviderInterface issuerMulticurves) {
final double parSpreadPrice = parSpread(bond, issuerMulticurves);
final double yieldAtTrade = METHOD_BOND_SECURITY.yieldFromCleanPrice(bond.getBondStandard(),
bond.getTransactionPrice());
final double yieldAtPar = METHOD_BOND_SECURITY.yieldFromCleanPrice(bond.getBondStandard(),
bond.getTransactionPrice() + parSpreadPrice);
return yieldAtPar - yieldAtTrade;
}
/**
* The par spread with respect to price curve sensitivity.
* @param bond The bond transaction.
* @param issuerMulticurves The issuer and multi-curves provider.
* @return The curve sensitivity.
*/
public MulticurveSensitivity parSpreadCurveSensitivity(final BondFixedTransaction bond, final IssuerProviderInterface issuerMulticurves) {
ArgumentChecker.notNull(bond, "Bond");
ArgumentChecker.notNull(issuerMulticurves, "Issuer and multi-curves provider");
final Currency ccy = bond.getBondTransaction().getCurrency();
final PaymentFixed nominalAtSettlement = new PaymentFixed(bond.getBondTransaction().getCurrency(),
bond.getBondTransaction().getSettlementTime(), bond.getBondTransaction().getCoupon()
.getNthPayment(0).getNotional() * bond.getQuantity());
final double pvNominalAtSettlement = nominalAtSettlement.accept(PVDC,
issuerMulticurves.getMulticurveProvider()).getAmount(ccy);
final MulticurveSensitivity pvcsNominalAtSettlement = nominalAtSettlement.accept(PVCSDC,
issuerMulticurves.getMulticurveProvider()).getSensitivity(ccy);
final MulticurveSensitivity pvcsBond = presentValueCurveSensitivity(bond, issuerMulticurves).getSensitivity(ccy);
final double pvBond = presentValue(bond, issuerMulticurves).getAmount(ccy);
return pvcsBond.multipliedBy(1.0 / pvNominalAtSettlement).plus(pvcsNominalAtSettlement.
multipliedBy(pvBond / (pvNominalAtSettlement * pvNominalAtSettlement)));
}
public MulticurveSensitivity parSpreadCurveSensitivity(final BondIborTransaction bond,
final IssuerProviderInterface issuerMulticurves) {
ArgumentChecker.notNull(bond, "Bond");
ArgumentChecker.notNull(issuerMulticurves, "Issuer and multi-curves provider");
final Currency ccy = bond.getBondTransaction().getCurrency();
final PaymentFixed nominalAtSettlement = new PaymentFixed(bond.getBondTransaction().getCurrency(),
bond.getBondTransaction().getSettlementTime(),
bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional() * bond.getQuantity());
final double pvNominalAtSettlement =
nominalAtSettlement.accept(PVDC, issuerMulticurves.getMulticurveProvider()).getAmount(ccy);
final MulticurveSensitivity pvcsNominalAtSettlement =
nominalAtSettlement.accept(PVCSDC, issuerMulticurves.getMulticurveProvider()).getSensitivity(ccy);
final MulticurveSensitivity pvcsBond = presentValueCurveSensitivity(bond, issuerMulticurves).getSensitivity(ccy);
final double pvBond = presentValue(bond, issuerMulticurves).getAmount(ccy);
return pvcsBond.multipliedBy(-1 / pvNominalAtSettlement).plus(pvcsNominalAtSettlement.
multipliedBy(pvBond / (pvNominalAtSettlement * pvNominalAtSettlement)));
}
/**
* The par spread with respect to yield curve sensitivity.
* @param bond The bond transaction.
* @param issuerMulticurves The issuer and multi-curves provider.
* @return The curve sensitivity.
*/
public MulticurveSensitivity parSpreadYieldCurveSensitivity(final BondFixedTransaction bond,
final IssuerProviderInterface issuerMulticurves) {
final double parSpreadPrice = parSpread(bond, issuerMulticurves);
//Backward sweep
final double parSpreadYieldBar = 1.0d;
final double yieldAtParBar = parSpreadYieldBar;
final double dirtyPriceAtPar = bond.getTransactionPrice() + parSpreadPrice + bond.getBondStandard().getAccruedInterest();
final double modifiedDurationAtPar = METHOD_BOND_SECURITY.
modifiedDurationFromCleanPrice(bond.getBondStandard(), bond.getTransactionPrice() + parSpreadPrice);
final double dYielddCleanAtPar = -1.0d / (modifiedDurationAtPar * dirtyPriceAtPar);
final double parSpreadPriceBar = dYielddCleanAtPar * yieldAtParBar;
final MulticurveSensitivity parSpreadPriceCurveSensitivity = parSpreadCurveSensitivity(bond, issuerMulticurves);
return parSpreadPriceCurveSensitivity.multipliedBy(parSpreadPriceBar);
}
}