Skip to content

Commit

Permalink
Merge pull request #1107.
Browse files Browse the repository at this point in the history
More changes for 30/360 day count convention
  • Loading branch information
lballabio committed May 31, 2021
2 parents 3261dde + c1a2aa9 commit 44230dd
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 217 deletions.
43 changes: 35 additions & 8 deletions ql/time/daycounters/thirty360.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,18 @@

namespace QuantLib {

namespace {

bool isLastOfFebruary(Day d, Month m, Year y) {
return m == 2 && d == 28 + (Date::isLeap(y) ? 1 : 0);
}

}

ext::shared_ptr<DayCounter::Impl>
Thirty360::implementation(Thirty360::Convention c, bool isLastPeriod) {
Thirty360::implementation(Thirty360::Convention c, const Date& terminationDate, bool isLastPeriod) {
switch (c) {
case USA:
case NASD:
return ext::shared_ptr<DayCounter::Impl>(new US_Impl);
case European:
case EurobondBasis:
Expand All @@ -39,7 +46,9 @@ namespace QuantLib {
return ext::shared_ptr<DayCounter::Impl>(new ISMA_Impl);
case ISDA:
case German:
return ext::shared_ptr<DayCounter::Impl>(new ISDA_Impl(isLastPeriod));
return ext::shared_ptr<DayCounter::Impl>(new ISDA_Impl(terminationDate, isLastPeriod));
case NASD:
return ext::shared_ptr<DayCounter::Impl>(new NASD_Impl);
default:
QL_FAIL("unknown 30/360 convention");
}
Expand All @@ -48,20 +57,22 @@ namespace QuantLib {
Date::serial_type Thirty360::US_Impl::dayCount(const Date& d1,
const Date& d2) const {
Day dd1 = d1.dayOfMonth(), dd2 = d2.dayOfMonth();
Integer mm1 = d1.month(), mm2 = d2.month();
Month mm1 = d1.month(), mm2 = d2.month();
Year yy1 = d1.year(), yy2 = d2.year();

if (dd1 == 31) { dd1 = 30; }
if (dd2 == 31 && dd1 >= 30) { dd2 = 30; }
if (dd2 == 31 && dd1 < 30) { dd2 = 1; mm2++; }

if (isLastOfFebruary(dd2, mm2, yy2) && isLastOfFebruary(dd1, mm1, yy1)) { dd2 = 30; }
if (isLastOfFebruary(dd1, mm1, yy1)) { dd1 = 30; }

return 360*(yy2-yy1) + 30*(mm2-mm1) + (dd2-dd1);
}

Date::serial_type Thirty360::ISMA_Impl::dayCount(const Date& d1,
const Date& d2) const {
Day dd1 = d1.dayOfMonth(), dd2 = d2.dayOfMonth();
Integer mm1 = d1.month(), mm2 = d2.month();
Month mm1 = d1.month(), mm2 = d2.month();
Year yy1 = d1.year(), yy2 = d2.year();

if (dd1 == 31) { dd1 = 30; }
Expand Down Expand Up @@ -106,8 +117,24 @@ namespace QuantLib {
if (dd1 == 31) { dd1 = 30; }
if (dd2 == 31) { dd2 = 30; }

if (mm1 == 2 && dd1 == 28 + (Date::isLeap(yy1) ? 1 : 0)) { dd1 = 30; }
if (!isLastPeriod_ && mm2 == 2 && dd2 == 28 + (Date::isLeap(yy2) ? 1 : 0)) { dd2 = 30; }
if (isLastOfFebruary(dd1, mm1, yy1)) { dd1 = 30; }

bool isTerminationDate =
terminationDate_ == Date() ? isLastPeriod_ : d2 == terminationDate_;
if (!isTerminationDate && isLastOfFebruary(dd2, mm2, yy2)) { dd2 = 30; }

return 360*(yy2-yy1) + 30*(mm2-mm1) + (dd2-dd1);
}

Date::serial_type Thirty360::NASD_Impl::dayCount(const Date& d1,
const Date& d2) const {
Day dd1 = d1.dayOfMonth(), dd2 = d2.dayOfMonth();
Integer mm1 = d1.month(), mm2 = d2.month();
Year yy1 = d1.year(), yy2 = d2.year();

if (dd1 == 31) { dd1 = 30; }
if (dd2 == 31 && dd1 >= 30) { dd2 = 30; }
if (dd2 == 31 && dd1 < 30) { dd2 = 1; mm2++; }

return 360*(yy2-yy1) + 30*(mm2-mm1) + (dd2-dd1);
}
Expand Down
66 changes: 45 additions & 21 deletions ql/time/daycounters/thirty360.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,21 @@ namespace QuantLib {
/*! The 30/360 day count can be calculated according to a
number of conventions.
US (NASD) convention: if the starting date is the 31st of a
month, it becomes equal to the 30th of the same month.
If the ending date is the 31st of a month and the starting
date is earlier than the 30th of a month, the ending date
becomes equal to the 1st of the next month, otherwise the
ending date becomes equal to the 30th of the same month.
Also known as "30/360", "360/360", or "Bond Basis".
US (ISMA) convention: if the starting date is the 31st of a
US convention: if the starting date is the 31st of a month or
the last day of February, it becomes equal to the 30th of the
same month. If the ending date is the 31st of a month and the
starting date is the 30th or 31th of a month, the ending date
becomes equal to the 30th. If the ending date is the last of
February and the starting date is also the last of February,
the ending date becomes equal to the 30th.
Also known as "30/360" or "360/360".
Bond Basis convention: if the starting date is the 31st of a
month, it becomes equal to the 30th of the same month.
If the ending date is the 31st of a month and the starting
date is the 30th or 31th of a month, the ending date
also becomes equal to the 30th of the month.
Also known as "Bond Basis".
Also known as "US (ISMA)".
European convention: starting dates or ending dates that
occur on the 31st of a month become equal to the 30th of the
Expand All @@ -57,10 +58,18 @@ namespace QuantLib {
occur on February and are greater than 27 become equal to 30
for computational sake.
ISDA convention: starting dates or ending dates that
occur on the last day of February become equal to 30
for computational sake, except for the termination date.
Also known as "30E/360 ISDA", "30/360 ISDA", or "30/360 German".
ISDA convention: starting or ending dates on the 31st of the
month become equal to 30; starting dates or ending dates that
occur on the last day of February also become equal to 30,
except for the termination date. Also known as "30E/360
ISDA", "30/360 ISDA", or "30/360 German".
NASD convention: if the starting date is the 31st of a
month, it becomes equal to the 30th of the same month.
If the ending date is the 31st of a month and the starting
date is earlier than the 30th of a month, the ending date
becomes equal to the 1st of the next month, otherwise the
ending date becomes equal to the 30th of the same month.
\ingroup daycounters
*/
Expand All @@ -87,7 +96,7 @@ namespace QuantLib {
};
class US_Impl : public Thirty360_Impl {
public:
std::string name() const override { return std::string("30/360 (NASD)"); }
std::string name() const override { return std::string("30/360 (US)"); }
Date::serial_type dayCount(const Date& d1, const Date& d2) const override;
};
class ISMA_Impl : public Thirty360_Impl {
Expand All @@ -107,23 +116,38 @@ namespace QuantLib {
};
class ISDA_Impl : public Thirty360_Impl {
public:
explicit ISDA_Impl(bool isLastPeriod) : isLastPeriod_(isLastPeriod) {}
explicit ISDA_Impl(const Date& terminationDate, bool isLastPeriod)
: terminationDate_(terminationDate), isLastPeriod_(isLastPeriod) {}
std::string name() const override { return std::string("30E/360 (ISDA)"); }
Date::serial_type dayCount(const Date& d1, const Date& d2) const override;
private:
Date terminationDate_;
bool isLastPeriod_;
};
static ext::shared_ptr<DayCounter::Impl> implementation(Convention c, bool isLastPeriod);
class NASD_Impl : public Thirty360_Impl {
public:
std::string name() const override { return std::string("30/360 (NASD)"); }
Date::serial_type dayCount(const Date& d1, const Date& d2) const override;
};
static ext::shared_ptr<DayCounter::Impl>
implementation(Convention c, const Date& terminationDate, bool isLastPeriod);
public:
/*! \deprecated Use the other constructor.
/*! \deprecated Use the constructor taking a convention and possibly a termination date.
Deprecated in version 1.23.
*/
QL_DEPRECATED
Thirty360()
: DayCounter(implementation(Thirty360::BondBasis, false)) {}
: DayCounter(implementation(Thirty360::BondBasis, Date(), false)) {}

/*! \deprecated Use the constructor taking a convention and possibly a termination date.
Deprecated in version 1.23.
*/
QL_DEPRECATED
Thirty360(Convention c, bool isLastPeriod)
: DayCounter(implementation(c, Date(), isLastPeriod)) {}

explicit Thirty360(Convention c, bool isLastPeriod = false)
: DayCounter(implementation(c, isLastPeriod)) {}
explicit Thirty360(Convention c, const Date& terminationDate = Date())
: DayCounter(implementation(c, terminationDate, false)) {}
};

}
Expand Down
Loading

0 comments on commit 44230dd

Please sign in to comment.