/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.equity.trs.method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.NotImplementedException;
import com.opengamma.analytics.financial.equity.EquityTrsDataBundle;
import com.opengamma.analytics.financial.equity.trs.definition.EquityTotalReturnSwap;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponFixed;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponIbor;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponIborSpread;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponON;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponONSpread;
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.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;
import com.opengamma.util.tuple.DoublesPair;
/**
* Class with pricing methods related to equity TRS valued by discounting.
* The equity TRS has a termination option at each valuation date. Only the cash flows up to the next unfixed payment are taken into account.
* The asset and funding leg are suppose to have the same fixing and payment dates. Currently only Ibor coupons are supported.
* Reference: Equity's total return swap, OpenGamma documentation 22, version 1.0, May 2014.
*/
public final class EquityTotalReturnSwapDiscountingMethod {
/**
* The unique instance of the class.
*/
private static final EquityTotalReturnSwapDiscountingMethod INSTANCE = new EquityTotalReturnSwapDiscountingMethod();
/**
* Return the class instance.
* @return The instance.
*/
public static EquityTotalReturnSwapDiscountingMethod getInstance() {
return INSTANCE;
}
/**
* Constructor
*/
private EquityTotalReturnSwapDiscountingMethod() {
}
/** The present value and present value curve sensitivity calculators used for bonds calculation */
private static final PresentValueDiscountingCalculator PVDC = PresentValueDiscountingCalculator.getInstance();
private static final PresentValueCurveSensitivityDiscountingCalculator PVCSDC = PresentValueCurveSensitivityDiscountingCalculator.getInstance();
/**
* Computes the present value of a equity TRS.
* The present value of the equity leg is converted into the currency of the TRS notional currency.
* @param trs The equity total return swap.
* @param equityMulticurves The multi-curves provider with equity price.
* @return The present value.
*/
public MultipleCurrencyAmount presentValue(final EquityTotalReturnSwap trs, final EquityTrsDataBundle equityMulticurves) {
ArgumentChecker.notNull(trs, "equity TRS");
ArgumentChecker.notNull(equityMulticurves, "multi-curve provider with equity price");
ArgumentChecker.isTrue(trs.getDividendPercentage() == 1.0, "equity TRS dividend ration should be 1.0");
MultipleCurrencyAmount equityPv = MultipleCurrencyAmount.of(trs.getEquity().getCurrency(),
equityMulticurves.getSpotEquity() * trs.getEquity().getNumberOfShares());
// First coupon ON or Ibor: Payment up to the fist payment taken into account OR Only one payment left
if ((trs.getFundingLeg().getNumberOfPayments() == 1) ||
(trs.getFundingLeg().getNthPayment(0) instanceof CouponIbor) || (trs.getFundingLeg().getNthPayment(0) instanceof CouponIborSpread)
|| (trs.getFundingLeg().getNthPayment(0) instanceof CouponON) || (trs.getFundingLeg().getNthPayment(0) instanceof CouponONSpread)) {
MultipleCurrencyAmount previousFixingPv = MultipleCurrencyAmount.of(trs.getNotionalCurrency(),
-trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), trs.getFundingLeg().getNthPayment(0).getPaymentTime()));
final MultipleCurrencyAmount fundingLegPV = trs.getFundingLeg().getNthPayment(0).accept(PVDC, equityMulticurves.getCurves());
return previousFixingPv.plus(equityMulticurves.getCurves().getFxRates().convert(equityPv, trs.getNotionalCurrency())).plus(fundingLegPV);
}
// Second coupons fixed or Ibor: Payment up to the end of the second period taken into account.
if ((trs.getFundingLeg().getNthPayment(1) instanceof CouponFixed)
|| (trs.getFundingLeg().getNthPayment(1) instanceof CouponON)
|| (trs.getFundingLeg().getNthPayment(1) instanceof CouponONSpread)) {
MultipleCurrencyAmount previousFixingPv = MultipleCurrencyAmount.of(trs.getNotionalCurrency(),
-trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), trs.getFundingLeg().getNthPayment(1).getPaymentTime()));
final MultipleCurrencyAmount fundingLegPv0 = trs.getFundingLeg().getNthPayment(0).accept(PVDC, equityMulticurves.getCurves());
final MultipleCurrencyAmount fundingLegPv1 = trs.getFundingLeg().getNthPayment(1).accept(PVDC, equityMulticurves.getCurves());
return previousFixingPv.plus(equityMulticurves.getCurves().getFxRates().convert(equityPv, trs.getNotionalCurrency())).plus(fundingLegPv0).plus(fundingLegPv1);
}
// First coupon is fixed (and the second one is not fixed or ON): in an already fixed Libor coupon: Payment up to the fist payment taken into account
if (trs.getFundingLeg().getNthPayment(0) instanceof CouponFixed) {
MultipleCurrencyAmount previousFixingPv = MultipleCurrencyAmount.of(trs.getNotionalCurrency(),
-trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), trs.getFundingLeg().getNthPayment(0).getPaymentTime()));
final MultipleCurrencyAmount fundingLegPV = trs.getFundingLeg().getNthPayment(0).accept(PVDC, equityMulticurves.getCurves());
return previousFixingPv.plus(equityMulticurves.getCurves().getFxRates().convert(equityPv, trs.getNotionalCurrency())).plus(fundingLegPV);
}
// Other cases not covered by the pricing method.
throw new NotImplementedException("Pricing of equity TRS not implemented for those types of coupons");
}
/**
* Computes the present value of the asset leg of a equity TRS.
* The present value of the equity leg is converted into the currency of the TRS notional currency.
* @param trs The equity total return swap.
* @param equityMulticurves The multi-curves provider with equity price.
* @return The present value.
*/
public MultipleCurrencyAmount presentValueAssetLeg(final EquityTotalReturnSwap trs, final EquityTrsDataBundle equityMulticurves) {
ArgumentChecker.notNull(trs, "equity TRS");
ArgumentChecker.notNull(equityMulticurves, "multi-curve provider with equity price");
MultipleCurrencyAmount equityPv = MultipleCurrencyAmount.of(trs.getEquity().getCurrency(),
equityMulticurves.getSpotEquity() * trs.getEquity().getNumberOfShares());
// First coupon ON or Ibor: Payment up to the fist payment taken into account OR Only one payment left
if ((trs.getFundingLeg().getNumberOfPayments() == 1) ||
(trs.getFundingLeg().getNthPayment(0) instanceof CouponIbor) || (trs.getFundingLeg().getNthPayment(0) instanceof CouponIborSpread)
|| (trs.getFundingLeg().getNthPayment(0) instanceof CouponON) || (trs.getFundingLeg().getNthPayment(0) instanceof CouponONSpread)) {
MultipleCurrencyAmount previousFixingPv = MultipleCurrencyAmount.of(trs.getNotionalCurrency(),
-trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), trs.getFundingLeg().getNthPayment(0).getPaymentTime()));
return previousFixingPv.plus(equityMulticurves.getCurves().getFxRates().convert(equityPv, trs.getNotionalCurrency()));
}
// Second coupons fixed or Ibor: Payment up to the end of the second period taken into account.
if ((trs.getFundingLeg().getNthPayment(1) instanceof CouponFixed)
|| (trs.getFundingLeg().getNthPayment(1) instanceof CouponON)
|| (trs.getFundingLeg().getNthPayment(1) instanceof CouponONSpread)) {
MultipleCurrencyAmount previousFixingPv = MultipleCurrencyAmount.of(trs.getNotionalCurrency(),
-trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), trs.getFundingLeg().getNthPayment(1).getPaymentTime()));
return previousFixingPv.plus(equityMulticurves.getCurves().getFxRates().convert(equityPv, trs.getNotionalCurrency()));
}
// First coupon is fixed (and the second one is not fixed or ON): in an already fixed Libor coupon: Payment up to the fist payment taken into account
if (trs.getFundingLeg().getNthPayment(0) instanceof CouponFixed) {
MultipleCurrencyAmount previousFixingPv = MultipleCurrencyAmount.of(trs.getNotionalCurrency(),
-trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), trs.getFundingLeg().getNthPayment(0).getPaymentTime()));
return previousFixingPv.plus(equityMulticurves.getCurves().getFxRates().convert(equityPv, trs.getNotionalCurrency()));
}
// Other cases not covered by the pricing method.
throw new NotImplementedException("Pricing of equity TRS not implemented for those types of coupons");
}
/**
* Computes the present value of the funding leg of a equity TRS.
* @param trs The equity total return swap.
* @param equityMulticurves The multi-curves provider with equity price.
* @return The present value.
*/
public MultipleCurrencyAmount presentValueFundingLeg(final EquityTotalReturnSwap trs, final EquityTrsDataBundle equityMulticurves) {
ArgumentChecker.notNull(trs, "equity TRS");
ArgumentChecker.notNull(equityMulticurves, "multi-curve provider with equity price");
// First coupon ON or Ibor: Payment up to the fist payment taken into account OR Only one payment left
if ((trs.getFundingLeg().getNumberOfPayments() == 1) ||
(trs.getFundingLeg().getNthPayment(0) instanceof CouponIbor) || (trs.getFundingLeg().getNthPayment(0) instanceof CouponIborSpread)
|| (trs.getFundingLeg().getNthPayment(0) instanceof CouponON) || (trs.getFundingLeg().getNthPayment(0) instanceof CouponONSpread)) {
return trs.getFundingLeg().getNthPayment(0).accept(PVDC, equityMulticurves.getCurves());
}
// Second coupons fixed or Ibor: Payment up to the end of the second period taken into account.
if ((trs.getFundingLeg().getNthPayment(1) instanceof CouponFixed)
|| (trs.getFundingLeg().getNthPayment(1) instanceof CouponON)
|| (trs.getFundingLeg().getNthPayment(1) instanceof CouponONSpread)) {
final MultipleCurrencyAmount fundingLegPv0 = trs.getFundingLeg().getNthPayment(0).accept(PVDC, equityMulticurves.getCurves());
final MultipleCurrencyAmount fundingLegPv1 = trs.getFundingLeg().getNthPayment(1).accept(PVDC, equityMulticurves.getCurves());
return fundingLegPv0.plus(fundingLegPv1);
}
// First coupon is fixed (and the second one is not fixed or ON): in an already fixed Libor coupon: Payment up to the fist payment taken into account
if (trs.getFundingLeg().getNthPayment(0) instanceof CouponFixed) {
return trs.getFundingLeg().getNthPayment(0).accept(PVDC, equityMulticurves.getCurves());
}
// Other cases not covered by the pricing method.
throw new NotImplementedException("Pricing of equity TRS not implemented for those types of coupons");
}
/**
* Computes the currency exposure of a equity TRS.
* The currency of the equity part is in the equity currency (not converted to the notional currency like for PV);
* the currency of the already fixed equity part is in the notional currency; the funding leg part is in the funding leg currency.
* @param trs The equity total return swap.
* @param equityMulticurves The multi-curves provider with equity price.
* @return The present value.
*/
public MultipleCurrencyAmount currencyExposure(final EquityTotalReturnSwap trs, final EquityTrsDataBundle equityMulticurves) {
ArgumentChecker.notNull(trs, "equity TRS");
ArgumentChecker.notNull(equityMulticurves, "multi-curve provider with equity price");
MultipleCurrencyAmount equityPv = MultipleCurrencyAmount.of(trs.getEquity().getCurrency(),
equityMulticurves.getSpotEquity() * trs.getEquity().getNumberOfShares());
// First coupon ON or Ibor: Payment up to the fist payment taken into account OR Only one payment left
if ((trs.getFundingLeg().getNumberOfPayments() == 1) ||
(trs.getFundingLeg().getNthPayment(0) instanceof CouponIbor) || (trs.getFundingLeg().getNthPayment(0) instanceof CouponIborSpread)
|| (trs.getFundingLeg().getNthPayment(0) instanceof CouponON) || (trs.getFundingLeg().getNthPayment(0) instanceof CouponONSpread)) {
MultipleCurrencyAmount previousFixingPv = MultipleCurrencyAmount.of(trs.getNotionalCurrency(),
-trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), trs.getFundingLeg().getNthPayment(0).getPaymentTime()));
final MultipleCurrencyAmount fundingLegPV = trs.getFundingLeg().getNthPayment(0).accept(PVDC, equityMulticurves.getCurves());
return previousFixingPv.plus(equityPv).plus(fundingLegPV);
}
// Second coupons fixed or Ibor: Payment up to the end of the second period taken into account.
if ((trs.getFundingLeg().getNthPayment(1) instanceof CouponFixed)
|| (trs.getFundingLeg().getNthPayment(1) instanceof CouponON)
|| (trs.getFundingLeg().getNthPayment(1) instanceof CouponONSpread)) {
MultipleCurrencyAmount previousFixingPv = MultipleCurrencyAmount.of(trs.getNotionalCurrency(),
-trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), trs.getFundingLeg().getNthPayment(1).getPaymentTime()));
final MultipleCurrencyAmount fundingLegPv0 = trs.getFundingLeg().getNthPayment(0).accept(PVDC, equityMulticurves.getCurves());
final MultipleCurrencyAmount fundingLegPv1 = trs.getFundingLeg().getNthPayment(1).accept(PVDC, equityMulticurves.getCurves());
return previousFixingPv.plus(equityPv).plus(fundingLegPv0).plus(fundingLegPv1);
}
// First coupon is fixed (and the second one is not fixed or ON): in an already fixed Libor coupon: Payment up to the fist payment taken into account
if (trs.getFundingLeg().getNthPayment(0) instanceof CouponFixed) {
MultipleCurrencyAmount previousFixingPv = MultipleCurrencyAmount.of(trs.getNotionalCurrency(),
-trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), trs.getFundingLeg().getNthPayment(0).getPaymentTime()));
final MultipleCurrencyAmount fundingLegPV = trs.getFundingLeg().getNthPayment(0).accept(PVDC, equityMulticurves.getCurves());
return previousFixingPv.plus(equityPv).plus(fundingLegPV);
}
// Other cases not covered by the pricing method.
throw new NotImplementedException("Pricing of equity TRS not implemented for those types of coupons");
}
/**
* Computes the exposure to the asset (equity) underlying the TRS. The exposure is reported in the equity currency.
* @param trs The equity total return swap.
* @param equityMulticurves The multi-curves provider with equity price.
* @return The present value.
*/
public MultipleCurrencyAmount assetExposure(final EquityTotalReturnSwap trs, final EquityTrsDataBundle equityMulticurves) {
ArgumentChecker.notNull(trs, "equity TRS");
ArgumentChecker.notNull(equityMulticurves, "multi-curve provider with equity price");
ArgumentChecker.isTrue(trs.getDividendPercentage() == 1.0, "equity TRS dividend ration should be 1.0");
MultipleCurrencyAmount equityPV = MultipleCurrencyAmount.of(trs.getEquity().getCurrency(),
equityMulticurves.getSpotEquity() * trs.getEquity().getNumberOfShares());
return equityPV;
}
/**
* Computes the present value curve sensitivity of a equity TRS.
* The sensitivity to the (issuer) curves used in the bond valuation and the sensitivity to the curves used in the funding leg valuation are computed.
* @param trs The equity total return swap.
* @param equityMulticurves The multi-curves provider with equity price.
* @return The present value.
*/
public MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity(final EquityTotalReturnSwap trs, final EquityTrsDataBundle equityMulticurves) {
ArgumentChecker.notNull(trs, "equity TRS");
ArgumentChecker.notNull(equityMulticurves, "multi-curve provider with equity price");
MulticurveProviderInterface multicurve = equityMulticurves.getCurves();
Currency notionalCurrency = trs.getNotionalCurrency();
// First coupon ON or Ibor: Payment up to the fist payment taken into account OR Only one payment left
if ((trs.getFundingLeg().getNumberOfPayments() == 1) ||
(trs.getFundingLeg().getNthPayment(0) instanceof CouponIbor) || (trs.getFundingLeg().getNthPayment(0) instanceof CouponIborSpread)
|| (trs.getFundingLeg().getNthPayment(0) instanceof CouponON) || (trs.getFundingLeg().getNthPayment(0) instanceof CouponONSpread)) {
final double time = trs.getFundingLeg().getNthPayment(0).getPaymentTime();
final Map<String, List<DoublesPair>> mapDsc = new HashMap<>();
final DoublesPair s = DoublesPair.of(time, time * trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), time));
final List<DoublesPair> list = new ArrayList<>();
list.add(s);
mapDsc.put(multicurve.getName(notionalCurrency), list);
MultipleCurrencyMulticurveSensitivity equityLegPvcs = new MultipleCurrencyMulticurveSensitivity();
equityLegPvcs = equityLegPvcs.plus(notionalCurrency, MulticurveSensitivity.ofYieldDiscounting(mapDsc));
MultipleCurrencyMulticurveSensitivity fundingLegPvcs = trs.getFundingLeg().getNthPayment(0).accept(PVCSDC, equityMulticurves.getCurves());
return equityLegPvcs.plus(fundingLegPvcs);
}
// Second coupons fixed or Ibor: Payment up to the end of the second period taken into account.
if ((trs.getFundingLeg().getNthPayment(1) instanceof CouponFixed)
|| (trs.getFundingLeg().getNthPayment(1) instanceof CouponON)
|| (trs.getFundingLeg().getNthPayment(1) instanceof CouponONSpread)) {
final double time = trs.getFundingLeg().getNthPayment(1).getPaymentTime();
final Map<String, List<DoublesPair>> mapDsc = new HashMap<>();
final DoublesPair s = DoublesPair.of(time, time * trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), time));
final List<DoublesPair> list = new ArrayList<>();
list.add(s);
mapDsc.put(multicurve.getName(notionalCurrency), list);
MultipleCurrencyMulticurveSensitivity equityLegPvcs = new MultipleCurrencyMulticurveSensitivity();
equityLegPvcs = equityLegPvcs.plus(notionalCurrency, MulticurveSensitivity.ofYieldDiscounting(mapDsc));
MultipleCurrencyMulticurveSensitivity fundingLegPvcs0 = trs.getFundingLeg().getNthPayment(0).accept(PVCSDC, equityMulticurves.getCurves());
MultipleCurrencyMulticurveSensitivity fundingLegPvcs1 = trs.getFundingLeg().getNthPayment(1).accept(PVCSDC, equityMulticurves.getCurves());
return equityLegPvcs.plus(fundingLegPvcs0).plus(fundingLegPvcs1);
}
// First coupon is fixed (and the second one is not fixed or ON): in an already fixed Libor coupon: Payment up to the fist payment taken into account
if (trs.getFundingLeg().getNthPayment(0) instanceof CouponFixed) {
final double time = trs.getFundingLeg().getNthPayment(0).getPaymentTime();
final Map<String, List<DoublesPair>> mapDsc = new HashMap<>();
final DoublesPair s = DoublesPair.of(time, time * trs.getNotionalAmount() * equityMulticurves.getCurves().getDiscountFactor(trs.getNotionalCurrency(), time));
final List<DoublesPair> list = new ArrayList<>();
list.add(s);
mapDsc.put(multicurve.getName(notionalCurrency), list);
MultipleCurrencyMulticurveSensitivity equityLegPvcs = new MultipleCurrencyMulticurveSensitivity();
equityLegPvcs = equityLegPvcs.plus(notionalCurrency, MulticurveSensitivity.ofYieldDiscounting(mapDsc));
MultipleCurrencyMulticurveSensitivity fundingLegPvcs = trs.getFundingLeg().getNthPayment(0).accept(PVCSDC, equityMulticurves.getCurves());
return equityLegPvcs.plus(fundingLegPvcs);
}
// Other cases not covered by the pricing method.
throw new NotImplementedException("Pricing of equity TRS not implemented for those types of coupons");
}
}