/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.interestrate.swap.provider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.opengamma.analytics.financial.interestrate.annuity.derivative.AnnuityCouponFixed;
import com.opengamma.analytics.financial.interestrate.annuity.provider.AnnuityDiscountingMethod;
import com.opengamma.analytics.financial.interestrate.payments.derivative.Payment;
import com.opengamma.analytics.financial.interestrate.swap.derivative.SwapFixedCoupon;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderInterface;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MulticurveSensitivity;
import com.opengamma.financial.convention.calendar.Calendar;
import com.opengamma.financial.convention.daycount.DayCount;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.tuple.DoublesPair;
/**
* Class to compute the quantities related to swaps (annuity, PVBP, coupon equivalent).
*/
public class SwapFixedCouponDiscountingMethod {
/**
* The method unique instance.
*/
private static final SwapFixedCouponDiscountingMethod INSTANCE = new SwapFixedCouponDiscountingMethod();
/**
* Return the unique instance of the class.
* @return The instance.
*/
public static SwapFixedCouponDiscountingMethod getInstance() {
return INSTANCE;
}
/**
* Private constructor.
*/
protected SwapFixedCouponDiscountingMethod() {
}
/**
* The methods.
*/
protected static final AnnuityDiscountingMethod METHOD_ANNUITY = AnnuityDiscountingMethod.getInstance();
/**
* Computes the conventional cash annuity of a swap. The computation is relevant only for standard swaps with constant notional and regular payments.
* @param fixedCouponSwap The underlying swap.
* @param forward The swap forward rate.
* @return The cash annuity.
*/
public double getAnnuityCash(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final double forward) {
final int nbFixedPeriod = fixedCouponSwap.getFixedLeg().getPayments().length;
final int nbFixedPaymentYear = (int) Math.round(1.0 / fixedCouponSwap.getFixedLeg().getNthPayment(0).getPaymentYearFraction());
final double notional = Math.abs(fixedCouponSwap.getFixedLeg().getNthPayment(0).getNotional());
final double annuityCash = 1.0 / forward * (1.0 - 1.0 / Math.pow(1 + forward / nbFixedPaymentYear, nbFixedPeriod)) * notional;
return annuityCash;
}
/**
* Computes the derivative of cash annuity with respect to the forward. The computation is relevant only for standard swaps with constant notional and regular payments.
* @param fixedCouponSwap The underlying swap.
* @param forward The swap forward.
* @return The cash annuity derivative.
*/
public double getAnnuityCashDerivative(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final double forward) {
final int nbFixedPeriod = fixedCouponSwap.getFixedLeg().getPayments().length;
final int nbFixedPaymentYear = (int) Math.round(1.0 / fixedCouponSwap.getFixedLeg().getNthPayment(0).getPaymentYearFraction());
final double notional = Math.abs(fixedCouponSwap.getFixedLeg().getNthPayment(0).getNotional());
double annuityCashDerivative = -1.0 / (forward * forward) * (1.0 - 1.0 / Math.pow(1 + forward / nbFixedPaymentYear, nbFixedPeriod)) * notional;
annuityCashDerivative += 1.0 / (forward * nbFixedPaymentYear) * nbFixedPeriod * Math.pow(1 + forward / nbFixedPaymentYear, -nbFixedPeriod - 1.0) * notional;
return annuityCashDerivative;
}
/**
* Computes the second derivative of cash annuity with respect to the forward. The computation is relevant only for standard swaps with constant notional and regular payments.
* @param fixedCouponSwap The underlying swap.
* @param forward The swap forward.
* @return The cash annuity second derivative.
*/
public double getAnnuityCashSecondDerivative(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final double forward) {
final int nbFixedPeriod = fixedCouponSwap.getFixedLeg().getPayments().length;
final int nbFixedPaymentYear = (int) Math.round(1.0 / fixedCouponSwap.getFixedLeg().getNthPayment(0).getPaymentYearFraction());
final double notional = Math.abs(fixedCouponSwap.getFixedLeg().getNthPayment(0).getNotional());
double annuityCashDerivative = 2.0 / (forward * forward * forward) * (1.0 - 1.0 / Math.pow(1 + forward / nbFixedPaymentYear, nbFixedPeriod)) * notional;
annuityCashDerivative -= 2.0 / (forward * forward * nbFixedPaymentYear) * nbFixedPeriod * Math.pow(1 + forward / nbFixedPaymentYear, -nbFixedPeriod - 1.0) * notional;
annuityCashDerivative -= 1.0 / (forward * nbFixedPaymentYear * nbFixedPaymentYear) * nbFixedPeriod * (nbFixedPeriod + 1.) * Math.pow(1 + forward / nbFixedPaymentYear, -nbFixedPeriod - 2.0) *
notional;
return annuityCashDerivative;
}
/**
* Computes the physical annuity (also called PVBP or level) of the fixed leg of a swap.
* @param fixedCouponSwap The underlying swap.
* @param multicurves The multi-curves provider.
* @return The physical annuity.
*/
public double presentValueBasisPoint(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final MulticurveProviderInterface multicurves) {
final AnnuityCouponFixed annuityFixed = fixedCouponSwap.getFixedLeg();
double pvbp = 0;
for (int loopcpn = 0; loopcpn < annuityFixed.getPayments().length; loopcpn++) {
pvbp += annuityFixed.getNthPayment(loopcpn).getPaymentYearFraction() * Math.abs(annuityFixed.getNthPayment(loopcpn).getNotional())
* multicurves.getDiscountFactor(annuityFixed.getNthPayment(loopcpn).getCurrency(), annuityFixed.getNthPayment(loopcpn).getPaymentTime());
}
return pvbp;
}
/**
* Computes the physical annuity (also called PVBP or level) of the fixed leg of a swap modified by a day count.
* @param fixedCouponSwap The underlying swap.
* @param dayCount Day count convention for the PVBP modification.
* @param multicurves The multi-curves provider.
* @return The physical annuity.
*/
public double presentValueBasisPoint(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final DayCount dayCount, final MulticurveProviderInterface multicurves) {
ArgumentChecker.notNull(fixedCouponSwap, "swap");
ArgumentChecker.notNull(dayCount, "day count");
ArgumentChecker.notNull(multicurves, "Multi-curves provider");
final AnnuityCouponFixed annuityFixed = fixedCouponSwap.getFixedLeg();
double pvbp = 0;
for (int loopcpn = 0; loopcpn < annuityFixed.getPayments().length; loopcpn++) {
pvbp += dayCount.getDayCountFraction(annuityFixed.getNthPayment(loopcpn).getAccrualStartDate(), annuityFixed.getNthPayment(loopcpn).getAccrualEndDate())
* Math.abs(annuityFixed.getNthPayment(loopcpn).getNotional())
* multicurves.getDiscountFactor(annuityFixed.getNthPayment(loopcpn).getCurrency(), annuityFixed.getNthPayment(loopcpn).getPaymentTime());
}
return pvbp;
}
/**
* Computes the physical annuity (also called PVBP or level) of the fixed leg of a swap modified by a day count.
* @param fixedCouponSwap The underlying swap.
* @param dayCount Day count convention for the PVBP modification.
* @param calendar The calendar
* @param multicurves The multi-curves provider.
* @return The physical annuity.
*/
public double presentValueBasisPoint(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final DayCount dayCount, final Calendar calendar,
final MulticurveProviderInterface multicurves) {
ArgumentChecker.notNull(fixedCouponSwap, "swap");
ArgumentChecker.notNull(dayCount, "day count");
ArgumentChecker.notNull(multicurves, "Multi-curves provider");
final AnnuityCouponFixed annuityFixed = fixedCouponSwap.getFixedLeg();
double pvbp = 0;
for (int loopcpn = 0; loopcpn < annuityFixed.getPayments().length; loopcpn++) {
pvbp += dayCount.getDayCountFraction(annuityFixed.getNthPayment(loopcpn).getAccrualStartDate(), annuityFixed.getNthPayment(loopcpn).getAccrualEndDate(), calendar)
* Math.abs(annuityFixed.getNthPayment(loopcpn).getNotional())
* multicurves.getDiscountFactor(annuityFixed.getNthPayment(loopcpn).getCurrency(), annuityFixed.getNthPayment(loopcpn).getPaymentTime());
}
return pvbp;
}
/**
* Compute the sensitivity of the PVBP to the discounting curve.
* @param fixedCouponSwap The swap.
* @param multicurves The multi-curves provider.
* @return The sensitivity.
*/
public MulticurveSensitivity presentValueBasisPointCurveSensitivity(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final MulticurveProviderInterface multicurves) {
final AnnuityCouponFixed annuityFixed = fixedCouponSwap.getFixedLeg();
final Currency ccy = annuityFixed.getCurrency();
double time;
final List<DoublesPair> list = new ArrayList<>();
for (int loopcpn = 0; loopcpn < annuityFixed.getPayments().length; loopcpn++) {
time = annuityFixed.getNthPayment(loopcpn).getPaymentTime();
final DoublesPair s = DoublesPair.of(time, -time * multicurves.getDiscountFactor(ccy, time) * annuityFixed.getNthPayment(loopcpn).getPaymentYearFraction()
* Math.abs(annuityFixed.getNthPayment(loopcpn).getNotional()));
list.add(s);
}
final Map<String, List<DoublesPair>> mapDsc = new HashMap<>();
mapDsc.put(multicurves.getName(annuityFixed.getCurrency()), list);
final MulticurveSensitivity result = MulticurveSensitivity.ofYieldDiscounting(mapDsc);
return result;
}
/**
* Compute the sensitivity of the PVBP to the discounting curve.
* @param fixedCouponSwap The swap.
* @param dayCount Day count convention for the PVBP modification.
* @param calendar The calendar
* @param multicurves The multi-curves provider.
* @return The sensitivity.
*/
public MulticurveSensitivity presentValueBasisPointCurveSensitivity(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final DayCount dayCount,
final Calendar calendar, final MulticurveProviderInterface multicurves) {
final AnnuityCouponFixed annuityFixed = fixedCouponSwap.getFixedLeg();
final Currency ccy = annuityFixed.getCurrency();
double time;
final List<DoublesPair> list = new ArrayList<>();
for (int loopcpn = 0; loopcpn < annuityFixed.getPayments().length; loopcpn++) {
time = annuityFixed.getNthPayment(loopcpn).getPaymentTime();
final DoublesPair s = DoublesPair.of(time, -time * multicurves.getDiscountFactor(ccy, time)
* dayCount.getDayCountFraction(annuityFixed.getNthPayment(loopcpn).getAccrualStartDate(), annuityFixed.getNthPayment(loopcpn).getAccrualEndDate(), calendar)
* Math.abs(annuityFixed.getNthPayment(loopcpn).getNotional()));
list.add(s);
}
final Map<String, List<DoublesPair>> mapDsc = new HashMap<>();
mapDsc.put(multicurves.getName(annuityFixed.getCurrency()), list);
final MulticurveSensitivity result = MulticurveSensitivity.ofYieldDiscounting(mapDsc);
return result;
}
/**
* Compute the second order sensitivity of the PVBP to the discounting curve.
* @param fixedCouponSwap The swap.
* @param dayCount Day count convention for the PVBP modification.
* @param calendar The calendar
* @param multicurves The multi-curves provider.
* @return The sensitivity.
*/
public MulticurveSensitivity presentValueBasisPointSecondOrderCurveSensitivity(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final DayCount dayCount,
final Calendar calendar, final MulticurveProviderInterface multicurves) {
final AnnuityCouponFixed annuityFixed = fixedCouponSwap.getFixedLeg();
final Currency ccy = annuityFixed.getCurrency();
double time;
final List<DoublesPair> list = new ArrayList<>();
for (int loopcpn = 0; loopcpn < annuityFixed.getPayments().length; loopcpn++) {
time = annuityFixed.getNthPayment(loopcpn).getPaymentTime();
final DoublesPair s = DoublesPair.of(time, time * time * multicurves.getDiscountFactor(ccy, time)
* dayCount.getDayCountFraction(annuityFixed.getNthPayment(loopcpn).getAccrualStartDate(), annuityFixed.getNthPayment(loopcpn).getAccrualEndDate(), calendar)
* Math.abs(annuityFixed.getNthPayment(loopcpn).getNotional()));
list.add(s);
}
final Map<String, List<DoublesPair>> mapDsc = new HashMap<>();
mapDsc.put(multicurves.getName(annuityFixed.getCurrency()), list);
final MulticurveSensitivity result = MulticurveSensitivity.ofYieldDiscounting(mapDsc);
return result;
}
/**
* Compute the sensitivity of the PVBP to the discounting curve.
* @param fixedCouponSwap The swap.
* @param dayCount Day count convention for the PVBP modification.
* @param multicurves The multi-curves provider.
* @return The sensitivity.
*/
public MulticurveSensitivity presentValueBasisPointCurveSensitivity(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final DayCount dayCount,
final MulticurveProviderInterface multicurves) {
final AnnuityCouponFixed annuityFixed = fixedCouponSwap.getFixedLeg();
final Currency ccy = annuityFixed.getCurrency();
double time;
final List<DoublesPair> list = new ArrayList<>();
for (int loopcpn = 0; loopcpn < annuityFixed.getPayments().length; loopcpn++) {
time = annuityFixed.getNthPayment(loopcpn).getPaymentTime();
final DoublesPair s = DoublesPair.of(time, -time * multicurves.getDiscountFactor(ccy, time)
* dayCount.getDayCountFraction(annuityFixed.getNthPayment(loopcpn).getAccrualStartDate(), annuityFixed.getNthPayment(loopcpn).getAccrualEndDate())
* Math.abs(annuityFixed.getNthPayment(loopcpn).getNotional()));
list.add(s);
}
final Map<String, List<DoublesPair>> mapDsc = new HashMap<>();
mapDsc.put(multicurves.getName(annuityFixed.getCurrency()), list);
final MulticurveSensitivity result = MulticurveSensitivity.ofYieldDiscounting(mapDsc);
return result;
}
/**
* Compute the sensitivity of the PVBP to the discounting curve.
* @param fixedCouponSwap The swap.
* @param dayCount Day count convention for the PVBP modification.
* @param multicurves The multi-curves provider.
* @return The sensitivity.
*/
public MulticurveSensitivity presentValueBasisPointSecondOrderCurveSensitivity(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final DayCount dayCount,
final MulticurveProviderInterface multicurves) {
final AnnuityCouponFixed annuityFixed = fixedCouponSwap.getFixedLeg();
final Currency ccy = annuityFixed.getCurrency();
double time;
final List<DoublesPair> list = new ArrayList<>();
for (int loopcpn = 0; loopcpn < annuityFixed.getPayments().length; loopcpn++) {
time = annuityFixed.getNthPayment(loopcpn).getPaymentTime();
final DoublesPair s = DoublesPair.of(time, time * time * multicurves.getDiscountFactor(ccy, time)
* dayCount.getDayCountFraction(annuityFixed.getNthPayment(loopcpn).getAccrualStartDate(), annuityFixed.getNthPayment(loopcpn).getAccrualEndDate())
* Math.abs(annuityFixed.getNthPayment(loopcpn).getNotional()));
list.add(s);
}
final Map<String, List<DoublesPair>> mapDsc = new HashMap<>();
mapDsc.put(multicurves.getName(annuityFixed.getCurrency()), list);
final MulticurveSensitivity result = MulticurveSensitivity.ofYieldDiscounting(mapDsc);
return result;
}
/**
* Computes the coupon equivalent of a swap (without margins).
* @param fixedCouponSwap The underlying swap.
* @param pvbp The swap PVBP.
* @param multicurves The multi-curves provider.
* @return The coupon equivalent.
*/
public double couponEquivalent(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final double pvbp, final MulticurveProviderInterface multicurves) {
return METHOD_ANNUITY.presentValuePositiveNotional(fixedCouponSwap.getFixedLeg(), multicurves).getAmount() / pvbp;
}
/**
* Computes the coupon equivalent of a swap (without margins).
* @param fixedCouponSwap The underlying swap.
* @param multicurves The multi-curves provider.
* @return The coupon equivalent.
*/
public double couponEquivalent(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final MulticurveProviderInterface multicurves) {
final double pvbp = presentValueBasisPoint(fixedCouponSwap, multicurves);
return couponEquivalent(fixedCouponSwap, pvbp, multicurves);
}
/**
* Computes the coupon equivalent of a swap (without margins).
* @param fixedCouponSwap The underlying swap.
* @param dayCount Day count convention for the PVBP modification.
* @param calendar The calendar
* @param multicurves The multi-curves provider.
* @return The coupon equivalent.
*/
public double couponEquivalent(final SwapFixedCoupon<? extends Payment> fixedCouponSwap, final DayCount dayCount, final Calendar calendar,
final MulticurveProviderInterface multicurves) {
final double pvbp = presentValueBasisPoint(fixedCouponSwap, dayCount, calendar, multicurves);
return couponEquivalent(fixedCouponSwap, pvbp, multicurves);
}
// TODO: par rate ?
}