/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.impl.credit.isda;
import java.time.LocalDate;
import com.opengamma.strata.basics.date.DayCount;
import com.opengamma.strata.basics.date.DayCounts;
import com.opengamma.strata.collect.ArgChecker;
/**
* This represents one payment period on the premium leg of a CDS
*/
public class CdsCoupon {
private static final DayCount ACT_365 = DayCounts.ACT_365F;
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 CdsAnalytic
* @return A set of CDSCoupon
*/
public static CdsCoupon[] makeCoupons(
LocalDate tradeDate,
IsdaPremiumLegSchedule leg,
boolean protectionFromStartOfDay,
DayCount accrualDCC,
DayCount curveDCC) {
ArgChecker.notNull(leg, "leg");
int n = leg.getNumPayments();
CdsCoupon[] res = new CdsCoupon[n];
for (int i = 0; i < n; i++) {
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 accrual end date which should have one day
* added (if protectionFromStartOfDay = true) in the 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(
LocalDate tradeDate,
CdsCouponDes[] couponsDes,
boolean protectionFromStartOfDay,
DayCount curveDCC) {
ArgChecker.noNulls(couponsDes, "couponsDes");
int n = couponsDes.length;
ArgChecker.isTrue(couponsDes[n - 1].getPaymentDate().isAfter(tradeDate), "all coupons have expired");
int count = 0;
while (tradeDate.isAfter(couponsDes[count].getPaymentDate())) {
count++;
}
int nCoupons = n - count;
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(LocalDate tradeDate, 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(LocalDate tradeDate, CdsCouponDes coupon, 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 accrual end date which should have one day
* added (if protectionFromStartOfDay = true) in the 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(LocalDate tradeDate, CdsCouponDes coupon, 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 accrual end date which should have one day
* added (if protectionFromStartOfDay = true) in the 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(LocalDate tradeDate, CdsCouponDes coupon, boolean protectionFromStartOfDay, DayCount curveDCC) {
ArgChecker.notNull(coupon, "coupon");
ArgChecker.notNull(curveDCC, "curveDCC");
ArgChecker.isFalse(tradeDate.isAfter(coupon.getPaymentDate()), "coupon payment is in the past");
LocalDate effStart = protectionFromStartOfDay ? coupon.getAccStart().minusDays(1) : coupon.getAccStart();
LocalDate effEnd = protectionFromStartOfDay ? coupon.getAccEnd().minusDays(1) : coupon.getAccEnd();
this.effStart = effStart.isBefore(tradeDate) ?
-curveDCC.yearFraction(effStart, tradeDate) :
curveDCC.yearFraction(tradeDate, effStart);
this.effEnd = curveDCC.yearFraction(tradeDate, effEnd);
this.paymentTime = curveDCC.yearFraction(tradeDate, coupon.getPaymentDate());
this.yearFrac = coupon.getYearFrac();
this.ycRatio = yearFrac / curveDCC.yearFraction(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(LocalDate tradeDate, 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(
LocalDate tradeDate,
LocalDate accStart,
LocalDate accEnd,
LocalDate paymentDate,
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(
LocalDate tradeDate,
LocalDate[] premiumDateTriplet,
boolean protectionFromStartOfDay,
DayCount accrualDCC,
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(
LocalDate tradeDate,
LocalDate accStart,
LocalDate accEnd,
LocalDate paymentDate,
boolean protectionFromStartOfDay,
DayCount accrualDCC,
DayCount curveDCC) {
this(toDoubles(tradeDate, protectionFromStartOfDay, accrualDCC, curveDCC, accStart, accEnd, paymentDate));
}
private CdsCoupon(double... data) {
this.effStart = data[0];
this.effEnd = data[1];
this.paymentTime = data[2];
this.yearFrac = data[3];
this.ycRatio = data[4];
}
@SuppressWarnings("unused")
private CdsCoupon(CdsCoupon other) {
ArgChecker.notNull(other, "other");
this.paymentTime = other.paymentTime;
this.yearFrac = other.yearFrac;
this.effStart = other.effStart;
this.effEnd = other.effEnd;
this.ycRatio = other.ycRatio;
}
private static double[] toDoubles(
LocalDate tradeDate,
boolean protectionFromStartOfDay,
DayCount accrualDCC,
DayCount curveDCC,
LocalDate... premDates) {
ArgChecker.notNull(tradeDate, "tradeDate");
ArgChecker.noNulls(premDates, "premDates");
ArgChecker.isTrue(3 == premDates.length, "premDates must be length 3");
ArgChecker.notNull(accrualDCC, "accrualDCC");
ArgChecker.notNull(curveDCC, "curveDCC");
LocalDate accStart = premDates[0];
LocalDate accEnd = premDates[1];
LocalDate paymentDate = premDates[2];
ArgChecker.isTrue(accEnd.isAfter(accStart), "require accEnd after accStart");
ArgChecker.isFalse(tradeDate.isAfter(paymentDate), "coupon payment is in the past");
LocalDate effStart = protectionFromStartOfDay ? accStart.minusDays(1) : accStart;
LocalDate effEnd = protectionFromStartOfDay ? accEnd.minusDays(1) : accEnd;
double[] res = new double[5];
res[0] = effStart.isBefore(tradeDate) ?
-curveDCC.yearFraction(effStart, tradeDate) :
curveDCC.yearFraction(tradeDate, effStart);
res[1] = curveDCC.yearFraction(tradeDate, effEnd);
res[2] = curveDCC.yearFraction(tradeDate, paymentDate);
res[3] = accrualDCC.yearFraction(accStart, accEnd);
res[4] = res[3] / curveDCC.yearFraction(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(double offset) {
return new CdsCoupon(effStart + offset, effEnd + offset, paymentTime + offset, yearFrac, ycRatio);
}
@Override
public int hashCode() {
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(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
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;
}
}