public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) { if (d1 == d2) { return(0); } if (d1 > d2) { return(-yearFraction(d2, d1, null, null)); } Date newD2 = d2, temp = d2; double sum = 0; while (temp > d1) { temp = newD2 - TimeUnit.Years; if (temp.Day == 28 && temp.Month == 2 && Date.IsLeapYear(temp.Year)) { temp += 1; } if (temp >= d1) { sum += 1; newD2 = temp; } } double den = 365; if (Date.IsLeapYear(newD2.Year)) { temp = new Date(29, Month.February, newD2.Year); if (newD2 > temp && d1 <= temp) { den += 1; } } else if (Date.IsLeapYear(d1.Year)) { temp = new Date(29, Month.February, d1.Year); if (newD2 > temp && d1 <= temp) { den += 1; } } return(sum + Date.daysBetween(d1, newD2) / den); }
public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) { if (d1 == d2) { return(0); } if (d1 > d2) { return(-yearFraction(d2, d1, null, null)); } int y1 = d1.Year, y2 = d2.Year; double dib1 = (Date.IsLeapYear(y1) ? 366 : 365), dib2 = (Date.IsLeapYear(y2) ? 366 : 365); double sum = y2 - y1 - 1; sum += Date.daysBetween(d1, new Date(1, Month.January, y1 + 1)) / dib1; sum += Date.daysBetween(new Date(1, Month.January, y2), d2) / dib2; return(sum); }
public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { if (d1 == d2) { return(0); } if (d1 > d2) { return(-yearFraction(d2, d1, d3, d4)); } // when the reference period is not specified, try taking it equal to (d1,d2) Date refPeriodStart = (d3 ?? d1); Date refPeriodEnd = (d4 ?? d2); Utils.QL_REQUIRE(refPeriodEnd > refPeriodStart && refPeriodEnd > d1, () => "Invalid reference period: date 1: " + d1 + ", date 2: " + d2 + ", reference period start: " + refPeriodStart + ", reference period end: " + refPeriodEnd); // estimate roughly the length in months of a period int months = (int)(0.5 + 12 * (refPeriodEnd - refPeriodStart) / 365.0); // for short periods... if (months == 0) { // ...take the reference period as 1 year from d1 refPeriodStart = d1; refPeriodEnd = d1 + TimeUnit.Years; months = 12; } double period = months / 12.0; if (d2 <= refPeriodEnd) { // here refPeriodEnd is a future (notional?) payment date if (d1 >= refPeriodStart) { // here refPeriodStart is the last (maybe notional) payment date. // refPeriodStart <= d1 <= d2 <= refPeriodEnd // [maybe the equality should be enforced, since refPeriodStart < d1 <= d2 < refPeriodEnd could give wrong results] ??? return(period * Date.daysBetween(d1, d2) / Date.daysBetween(refPeriodStart, refPeriodEnd)); } else { // here refPeriodStart is the next (maybe notional) payment date and refPeriodEnd is the second next (maybe notional) payment date. // d1 < refPeriodStart < refPeriodEnd // AND d2 <= refPeriodEnd // this case is long first coupon // the last notional payment date Date previousRef = refPeriodStart - new Period(months, TimeUnit.Months); if (d2 > refPeriodStart) { return(yearFraction(d1, refPeriodStart, previousRef, refPeriodStart) + yearFraction(refPeriodStart, d2, refPeriodStart, refPeriodEnd)); } else { return(yearFraction(d1, d2, previousRef, refPeriodStart)); } } } else { // here refPeriodEnd is the last (notional?) payment date // d1 < refPeriodEnd < d2 AND refPeriodStart < refPeriodEnd Utils.QL_REQUIRE(refPeriodStart <= d1, () => "invalid dates: d1 < refPeriodStart < refPeriodEnd < d2"); // now it is: refPeriodStart <= d1 < refPeriodEnd < d2 // the part from d1 to refPeriodEnd double sum = yearFraction(d1, refPeriodEnd, refPeriodStart, refPeriodEnd); // the part from refPeriodEnd to d2 // count how many regular periods are in [refPeriodEnd, d2], then add the remaining time int i = 0; Date newRefStart, newRefEnd; for (;;) { newRefStart = refPeriodEnd + new Period(months * i, TimeUnit.Months); newRefEnd = refPeriodEnd + new Period(months * (i + 1), TimeUnit.Months); if (d2 < newRefEnd) { break; } else { sum += period; i++; } } sum += yearFraction(newRefStart, d2, newRefStart, newRefEnd); return(sum); } }