double presentValuePremiumLeg = 0.0;
final ZonedDateTime[] premiumLegSchedule = PREMIUM_LEG_SCHEDULE.constructISDACompliantCreditDefaultSwapPremiumLegSchedule(cds);
final ZonedDateTime[] accruedLegIntegrationSchedule = ACRRUED_INTEGRATION_SCHEDULE.constructCreditDefaultSwapAccruedLegIntegrationSchedule(cds, curves);
final ZonedDateTime adjustedMaturityDate = PREMIUM_LEG_SCHEDULE.getAdjustedMaturityDate(cds);
ArgumentChecker.isTrue(!valuationDate.isAfter(adjustedMaturityDate), "Valuation date {} must be on or before the adjusted maturity date {}", valuationDate, adjustedMaturityDate);
final ISDADateCurve yieldCurve = curves.getYieldCurve();
final HazardRateCurve hazardRateCurve = curves.getHazardRateCurve();
// TODO : Check the effective date calc here
// If the valuation date is exactly the adjusted maturity date then simply return zero
/*
if (valuationDate.equals(adjustedMaturityDate) || cds.getEffectiveDate().equals(adjustedMaturityDate)) {
return 0.0;
}
*/
final ZonedDateTime today = valuationDate;
final ZonedDateTime stepinDate = cds.getEffectiveDate(); //TODO this relies on the person that's set up the CDS to know that effective date = 1 day after valuation date by convention
// The value date is when cash settlement is made
// TODO : Add the extra logic for this calculation
final ZonedDateTime matDate = cds.getMaturityDate();
// TODO : Check valueDate >= today and stepinDate >= today
if (today.isAfter(matDate) || stepinDate.isAfter(matDate)) {
return 0;
}
double thisPV = 0.0;
for (int i = 1; i < premiumLegSchedule.length; i++) {
final int obsOffset = cds.getProtectionStart() ? -1 : 0;
final ZonedDateTime accrualStartDate = premiumLegSchedule[i - 1];
ZonedDateTime accrualEndDate = premiumLegSchedule[i];
final ZonedDateTime discountDate = accrualEndDate;
// The last coupon date has an extra day of accrued
if (i == premiumLegSchedule.length - 1) {
accrualEndDate = accrualEndDate.plusDays(1);
}
final double delta = accrualEndDate.isAfter(stepinDate) ? 1 : 0;
final double accTime = ACT_360.getDayCountFraction(accrualStartDate, accrualEndDate);
final ZonedDateTime offsetAccrual = accrualEndDate.plusDays(obsOffset);
double tObsOffset = today.isAfter(offsetAccrual) ? -ACT_365.getDayCountFraction(offsetAccrual, today) : ACT_365.getDayCountFraction(today, offsetAccrual);
if (Double.compare(tObsOffset, -0.0) == 0) {
tObsOffset = 0;
}
double t = today.isAfter(discountDate) ? -ACT_365.getDayCountFraction(discountDate, today) : ACT_365.getDayCountFraction(today, discountDate);
final double survival = hazardRateCurve.getSurvivalProbability(tObsOffset);
final double discount = yieldCurve.getDiscountFactor(t);
thisPV += delta * accTime * discount * survival;
double myPV = 0.0;
if (cds.getIncludeAccruedPremium()) {
final ZonedDateTime offsetStepinDate = stepinDate.plusDays(obsOffset); // stepinDate
final ZonedDateTime offsetAccStartDate = accrualStartDate.plusDays(obsOffset); // startDate
final ZonedDateTime offsetAccEndDate = accrualEndDate.plusDays(obsOffset); // endDate
// TODO : Check endDate > startDate
final ZonedDateTime[] truncatedDateList = ScheduleUtils.getTruncatedTimeLine(accruedLegIntegrationSchedule, offsetAccStartDate, offsetAccEndDate, true);
ZonedDateTime subStartDate = offsetStepinDate.isAfter(offsetAccStartDate) ? offsetStepinDate : offsetAccStartDate;
final double tAcc = ACT_365.getDayCountFraction(offsetAccStartDate, offsetAccEndDate);
final double accRate = accTime / tAcc;
if (today.equals(subStartDate)) {
t = 0;
} else {
t = today.isBefore(subStartDate) ? ACT_365.getDayCountFraction(today, subStartDate) : -ACT_365.getDayCountFraction(subStartDate, today);
}
if (Double.compare(t, -0.0) == 0) {
t = 0;
}
double s0 = hazardRateCurve.getSurvivalProbability(t);
double df0 = yieldCurve.getDiscountFactor(t);
for (int j = 1; j < truncatedDateList.length; ++j) {
double thisAccPV = 0.0;
final ZonedDateTime date = truncatedDateList[j];
if (date.isAfter(offsetStepinDate)) {
t = today.isBefore(date) ? ACT_365.getDayCountFraction(today, date) : -ACT_365.getDayCountFraction(date, today); //TimeCalculator.getTimeBetween(today, truncatedDateList[j], ACT_365);
if (Double.compare(t, -0.0) == 0) {
t = 0;
}
final double s1 = hazardRateCurve.getSurvivalProbability(t);
final double df1 = yieldCurve.getDiscountFactor(t);
final double t0 = (offsetAccStartDate.isBefore(subStartDate) ? ACT_365.getDayCountFraction(offsetAccStartDate, subStartDate)
: ACT_365.getDayCountFraction(subStartDate, offsetAccStartDate)) + 0.5 / 365.0;
final double t1 = (offsetAccStartDate.isBefore(date) ? ACT_365.getDayCountFraction(offsetAccStartDate, date)
: ACT_365.getDayCountFraction(date, offsetAccStartDate)) + 0.5 / 365.0;
t = t1 - t0;
final double lambda = Math.log(s0 / s1) / t;
final double fwdRate = Math.log(df0 / df1) / t;
final double lambdafwdRate = lambda + fwdRate + 1.0e-50;
thisAccPV = lambda * accRate * s0 * df0 * ((t0 + 1.0 / (lambdafwdRate)) / (lambdafwdRate) - (t1 + 1.0 / (lambdafwdRate)) / (lambdafwdRate) * s1 / s0 * df1 / df0);
myPV += thisAccPV;
s0 = s1;
df0 = df1;
subStartDate = date;
}
}
}
thisPV += myPV;
}
presentValuePremiumLeg = thisPV;
// TODO : Check this calculation - maybe move it out of this routine and into the PV calculation routine?
// TODO : Note the cash settlement date is hardcoded at 3 days
ZonedDateTime bdaCashSettlementDate = valuationDate.plusDays(SPOT_DAYS);
if (ADJUST_CASH_SETTLEMENT_DATE) {
bdaCashSettlementDate = BDA.adjustDate(cds.getCalendar(), bdaCashSettlementDate);
}
final double tSett = ACT_ACT.getDayCountFraction(valuationDate, bdaCashSettlementDate);
final double valueDatePV = yieldCurve.getDiscountFactor(tSett);
presentValuePremiumLeg /= valueDatePV;
if (priceType == PriceType.CLEAN) {
//TODO not looked at this yet
// pass in stepinDate as 'today' 31/1/2013
double ai = 0.0;