/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.credit.isdastandardmodel;
import org.threeten.bp.LocalDate;
import com.opengamma.financial.convention.daycount.DayCount;
import com.opengamma.financial.convention.daycount.DayCounts;
import com.opengamma.util.ArgumentChecker;
/**
* This represents one payment period on the premium leg of a CDS
*/
public class CDSCoupon {
private static final DayCount ACT_365 = DayCounts.ACT_365;
private static final DayCount ACT_360 = DayCounts.ACT_360;
private static final boolean PROTECTION_FROM_START = true;
private final double _effStart;
private final double _effEnd;
private final double _paymentTime;
private final double _yearFrac;
private final double _ycRatio;
/**
* Make a set of CDSCoupon used by {@link CDSAnalytic} given a trade date and the schedule of the accrual periods
* @param tradeDate The trade date
* @param leg schedule of the accrual periods
* @param protectionFromStartOfDay If true the protection is from the start of day and the effective accrual start and end dates are one day less. The exception is the final
* accrual end date which has one day added (if protectionFromStartOfDay = true) in ISDAPremiumLegSchedule to compensate for this, so the accrual end date is just the CDS maturity.
* The effect of having protectionFromStartOfDay = true is to add an extra day of protection.
* @param accrualDCC The day count used to compute accrual periods
* @param curveDCC Day count used on curve (NOTE ISDA uses ACT/365 (fixed) and it is not recommended to change this)
* @see {@link CDSAnalytic}
* @return A set of CDSCoupon
*/
public static CDSCoupon[] makeCoupons(final LocalDate tradeDate, final ISDAPremiumLegSchedule leg, final boolean protectionFromStartOfDay, final DayCount accrualDCC, final DayCount curveDCC) {
ArgumentChecker.notNull(leg, "leg");
final int n = leg.getNumPayments();
final CDSCoupon[] res = new CDSCoupon[n];
for (int i = 0; i < n; i++) {
final LocalDate[] dates = leg.getAccPaymentDateTriplet(i);
res[i] = new CDSCoupon(tradeDate, dates[0], dates[1], dates[2], protectionFromStartOfDay, accrualDCC, curveDCC);
}
return res;
}
/**
* Make a set of CDSCoupon used by {@link CDSAnalytic} given a trade date and a set of {@link CDSCouponDes}
* @param tradeDate The trade date
* @param couponsDes Description of CDS accrual periods with LocalDate
* @param protectionFromStartOfDay If true the protection is from the start of day and the effective accrual start and end dates are one day less. The exception is the final
* accrual end date which should have one day added (if protectionFromStartOfDay = true) in the final CDSCouponDes to compensate for this, so the accrual end date is just the
* CDS maturity. The effect of having protectionFromStartOfDay = true is to add an extra day of protection.
* @param curveDCC Day count used on curve (NOTE ISDA uses ACT/365 (fixed) and it is not recommended to change this)
* @return A set of CDSCoupon
*/
public static CDSCoupon[] makeCoupons(final LocalDate tradeDate, final CDSCouponDes[] couponsDes, final boolean protectionFromStartOfDay, final DayCount curveDCC) {
ArgumentChecker.noNulls(couponsDes, "couponsDes");
final int n = couponsDes.length;
ArgumentChecker.isTrue(couponsDes[n - 1].getPaymentDate().isAfter(tradeDate), "all coupons have expired");
int count = 0;
while (tradeDate.isAfter(couponsDes[count].getPaymentDate())) {
count++;
}
final int nCoupons = n - count;
final CDSCoupon[] coupons = new CDSCoupon[nCoupons];
for (int i = 0; i < nCoupons; i++) {
coupons[i] = new CDSCoupon(tradeDate, couponsDes[i + count], protectionFromStartOfDay, curveDCC);
}
return coupons;
}
/**
* Turn a date based description of a CDS accrual period ({@link CDSCouponDes}) into an analytic description ({@link CDSCoupon}). This has protection from start of day
* and uses ACT/360 for the accrual day count.
* @param tradeDate The trade date
* @param coupon A date based description of a CDS accrual period
*/
public CDSCoupon(final LocalDate tradeDate, final CDSCouponDes coupon) {
this(tradeDate, coupon, PROTECTION_FROM_START, ACT_360);
}
/**
* Turn a date based description of a CDS accrual period ({@link CDSCouponDes}) into an analytic description ({@link CDSCoupon}). This has protection from start of day
* and uses ACT/360 for the accrual day count.
* @param tradeDate The trade date
* @param coupon A date based description of a CDS accrual period
* @param curveDCC Day count used on curve (NOTE ISDA uses ACT/365 (fixed) and it is not recommended to change this)
*/
public CDSCoupon(final LocalDate tradeDate, final CDSCouponDes coupon, final DayCount curveDCC) {
this(tradeDate, coupon, PROTECTION_FROM_START, curveDCC);
}
/**
* Turn a date based description of a CDS accrual period ({@link CDSCouponDes}) into an analytic description ({@link CDSCoupon}).
* This uses ACT/360 for the accrual day count.
* @param tradeDate The trade date
* @param coupon A date based description of a CDS accrual period
* @param protectionFromStartOfDay If true the protection is from the start of day and the effective accrual start and end dates are one day less. The exception is the final
* accrual end date which should have one day added (if protectionFromStartOfDay = true) in the final CDSCouponDes to compensate for this, so the accrual end date is just the
* CDS maturity. The effect of having protectionFromStartOfDay = true is to add an extra day of protection.
*/
public CDSCoupon(final LocalDate tradeDate, final CDSCouponDes coupon, final boolean protectionFromStartOfDay) {
this(tradeDate, coupon, protectionFromStartOfDay, ACT_360);
}
/**
* Turn a date based description of a CDS accrual period ({@link CDSCouponDes}) into an analytic description ({@link CDSCoupon}).
* This uses ACT/360 for the accrual day count.
* @param tradeDate The trade date
* @param coupon A date based description of a CDS accrual period
* @param protectionFromStartOfDay If true the protection is from the start of day and the effective accrual start and end dates are one day less. The exception is the final
* accrual end date which should have one day added (if protectionFromStartOfDay = true) in the final CDSCouponDes to compensate for this, so the accrual end date is just the
* CDS maturity. The effect of having protectionFromStartOfDay = true is to add an extra day of protection.
* @param curveDCC Day count used on curve (NOTE ISDA uses ACT/365 (fixed) and it is not recommended to change this)
*/
public CDSCoupon(final LocalDate tradeDate, final CDSCouponDes coupon, final boolean protectionFromStartOfDay, final DayCount curveDCC) {
ArgumentChecker.notNull(coupon, "coupon");
ArgumentChecker.notNull(curveDCC, "curveDCC");
ArgumentChecker.isFalse(tradeDate.isAfter(coupon.getPaymentDate()), "coupon payment is in the past");
final LocalDate effStart = protectionFromStartOfDay ? coupon.getAccStart().minusDays(1) : coupon.getAccStart();
final LocalDate effEnd = protectionFromStartOfDay ? coupon.getAccEnd().minusDays(1) : coupon.getAccEnd();
_effStart = effStart.isBefore(tradeDate) ? -curveDCC.getDayCountFraction(effStart, tradeDate) : curveDCC.getDayCountFraction(tradeDate, effStart);
_effEnd = curveDCC.getDayCountFraction(tradeDate, effEnd);
_paymentTime = curveDCC.getDayCountFraction(tradeDate, coupon.getPaymentDate());
_yearFrac = coupon.getYearFrac();
_ycRatio = _yearFrac / curveDCC.getDayCountFraction(coupon.getAccStart(), coupon.getAccEnd());
}
/**
* Setup a analytic description (i.e. involving only doubles) of a single CDS premium payment period seen from a particular trade
* date. Protection is taken from start of day; ACT/360 is used for the accrual DCC and ACT/365F for the curve DCC
* @param tradeDate The trade date (this is the base date that discount factors and survival probabilities are measured from)
* @param premiumDateTriplet The three dates: start and end of the accrual period and the payment time
*/
public CDSCoupon(final LocalDate tradeDate, final LocalDate... premiumDateTriplet) {
this(toDoubles(tradeDate, PROTECTION_FROM_START, ACT_360, ACT_365, premiumDateTriplet));
}
/**
*
* Setup a analytic description (i.e. involving only doubles) of a single CDS premium payment period seen from a particular trade
* date. ACT/360 is used for the accrual DCC and ACT/365F for the curve DCC
* @param tradeDate The trade date (this is the base date that discount factors and survival probabilities are measured from)
* @param accStart The start of the accrual period
* @param accEnd The end of the accrual period
* @param paymentDate The date of the premium payment
* @param protectionFromStartOfDay true if protection is from the start of day (true for standard CDS)
*/
public CDSCoupon(final LocalDate tradeDate, final LocalDate accStart, final LocalDate accEnd, final LocalDate paymentDate, final boolean protectionFromStartOfDay) {
this(toDoubles(tradeDate, protectionFromStartOfDay, ACT_360, ACT_365, accStart, accEnd, paymentDate));
}
/**
* Setup a analytic description (i.e. involving only doubles) of a single CDS premium payment period seen from a particular trade
* date.
* @param tradeDate The trade date (this is the base date that discount factors and survival probabilities are measured from)
* @param premiumDateTriplet The three dates: start and end of the accrual period and the payment time
* @param protectionFromStartOfDay true if protection is from the start of day (true for standard CDS)
* @param accrualDCC The day-count-convention used for calculation the accrual period (ACT/360 for standard CDS)
* @param curveDCC The day-count-convention used for converting dates to time intervals along curves - this should be ACT/365F
*/
public CDSCoupon(final LocalDate tradeDate, final LocalDate[] premiumDateTriplet, final boolean protectionFromStartOfDay, final DayCount accrualDCC, final DayCount curveDCC) {
this(toDoubles(tradeDate, protectionFromStartOfDay, accrualDCC, curveDCC, premiumDateTriplet));
}
/**
* Setup a analytic description (i.e. involving only doubles) of a single CDS premium payment period seen from a particular trade
* date.
* @param tradeDate The trade date (this is the base date that discount factors and survival probabilities are measured from)
* @param accStart The start of the accrual period
* @param accEnd The end of the accrual period
* @param paymentDate The date of the premium payment
* @param protectionFromStartOfDay true if protection is from the start of day (true for standard CDS)
* @param accrualDCC The day-count-convention used for calculation the accrual period (ACT/360 for standard CDS)
* @param curveDCC The day-count-convention used for converting dates to time intervals along curves - this should be ACT/365F
*/
public CDSCoupon(final LocalDate tradeDate, final LocalDate accStart, final LocalDate accEnd, final LocalDate paymentDate, final boolean protectionFromStartOfDay, final DayCount accrualDCC,
final DayCount curveDCC) {
this(toDoubles(tradeDate, protectionFromStartOfDay, accrualDCC, curveDCC, accStart, accEnd, paymentDate));
}
private CDSCoupon(final double... data) {
_effStart = data[0];
_effEnd = data[1];
_paymentTime = data[2];
_yearFrac = data[3];
_ycRatio = data[4];
}
@SuppressWarnings("unused")
private CDSCoupon(final CDSCoupon other) {
ArgumentChecker.notNull(other, "other");
_paymentTime = other._paymentTime;
_yearFrac = other._yearFrac;
_effStart = other._effStart;
_effEnd = other._effEnd;
_ycRatio = other._ycRatio;
}
private static double[] toDoubles(final LocalDate tradeDate, final boolean protectionFromStartOfDay, final DayCount accrualDCC, final DayCount curveDCC, final LocalDate... premDates) {
ArgumentChecker.notNull(tradeDate, "tradeDate");
ArgumentChecker.noNulls(premDates, "premDates");
ArgumentChecker.isTrue(3 == premDates.length, "premDates must be length 3");
ArgumentChecker.notNull(accrualDCC, "accrualDCC");
ArgumentChecker.notNull(curveDCC, "curveDCC");
final LocalDate accStart = premDates[0];
final LocalDate accEnd = premDates[1];
final LocalDate paymentDate = premDates[2];
ArgumentChecker.isTrue(accEnd.isAfter(accStart), "require accEnd after accStart");
// ArgumentChecker.isTrue(paymentDate.isAfter(accStart), "require paymentDate after accStart");
ArgumentChecker.isFalse(tradeDate.isAfter(paymentDate), "coupon payment is in the past");
final LocalDate effStart = protectionFromStartOfDay ? accStart.minusDays(1) : accStart;
final LocalDate effEnd = protectionFromStartOfDay ? accEnd.minusDays(1) : accEnd;
final double[] res = new double[5];
res[0] = effStart.isBefore(tradeDate) ? -curveDCC.getDayCountFraction(effStart, tradeDate) : curveDCC.getDayCountFraction(tradeDate, effStart);
res[1] = curveDCC.getDayCountFraction(tradeDate, effEnd);
res[2] = curveDCC.getDayCountFraction(tradeDate, paymentDate);
res[3] = accrualDCC.getDayCountFraction(accStart, accEnd);
res[4] = res[3] / curveDCC.getDayCountFraction(accStart, accEnd);
return res;
}
/**
* Gets the paymentTime.
* @return the paymentTime
*/
public double getPaymentTime() {
return _paymentTime;
}
/**
* Gets the yearFrac.
* @return the yearFrac
*/
public double getYearFrac() {
return _yearFrac;
}
/**
* Gets the effStart.
* @return the effStart
*/
public double getEffStart() {
return _effStart;
}
/**
* Gets the effEnd.
* @return the effEnd
*/
public double getEffEnd() {
return _effEnd;
}
/**
* Gets the ratio of the accrual period year fraction calculated using the accrual DCC to that calculated using the curve DCC. This is used in accrual on default calculations
* @return the year fraction ratio
*/
public double getYFRatio() {
return _ycRatio;
}
/**
* Produce a coupon with payments and accrual start/end offset by a given amount. For example if an offset of 0.5 was applied to a coupon with
* effStart, effEnd and payment time of 0, 0.25 and 0.25, the new coupon would have 0.5, 0.75, 0.75 (effStart, effEnd, payment time)
* @param offset amount of offset (in years)
* @return offset coupon
*/
public CDSCoupon withOffset(final double offset) {
return new CDSCoupon(_effStart + offset, _effEnd + offset, _paymentTime + offset, _yearFrac, _ycRatio);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(_effEnd);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(_effStart);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(_paymentTime);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(_ycRatio);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(_yearFrac);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CDSCoupon other = (CDSCoupon) obj;
if (Double.doubleToLongBits(_effEnd) != Double.doubleToLongBits(other._effEnd)) {
return false;
}
if (Double.doubleToLongBits(_effStart) != Double.doubleToLongBits(other._effStart)) {
return false;
}
if (Double.doubleToLongBits(_paymentTime) != Double.doubleToLongBits(other._paymentTime)) {
return false;
}
if (Double.doubleToLongBits(_ycRatio) != Double.doubleToLongBits(other._ycRatio)) {
return false;
}
if (Double.doubleToLongBits(_yearFrac) != Double.doubleToLongBits(other._yearFrac)) {
return false;
}
return true;
}
}