Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix calendar advance for unadjusted convention #1917

Merged
merged 4 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions ql/time/calendar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,15 @@ namespace QuantLib {
Date d1 = d + n*unit;

// we are sure the unit is Months or Years
if (endOfMonth && isEndOfMonth(d))
return Calendar::endOfMonth(d1);

if (endOfMonth){
if (c == Unadjusted && Date::isEndOfMonth(d)){
// move to end of calendar day if using Unadjusted convention and d is last calendar day
return Date::endOfMonth(d1);
} else if (isEndOfMonth(d)) {
// move to end of business day if d is last bussiness day
return Calendar::endOfMonth(d1);
}
}
return adjust(d1, c);
}
}
Expand Down
2 changes: 1 addition & 1 deletion test-suite/businessdayconventions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(testConventions) {
//Unadjusted
SingleCase(SouthAfrica(), Unadjusted, Date(3,February,2015), Period(1,Months), false, Date(3,March,2015)),
SingleCase(SouthAfrica(), Unadjusted, Date(3,February,2015), Period(4,Days), false, Date(9,February,2015)),
SingleCase(SouthAfrica(), Unadjusted, Date(31,January,2015), Period(1,Months), true, Date(27,February,2015)),
SingleCase(SouthAfrica(), Unadjusted, Date(31,January,2015), Period(1,Months), true, Date(28,February,2015)),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@igitur it's been a long time, but git tells me you wrote this test — do you think it makes sense to change the result?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lballabio Hi, yes, the fix looks correct. I probably used QuantlibXL to generate test data for the existing business day conventions, thereby including the bug in the test data. Sorry.

SingleCase(SouthAfrica(), Unadjusted, Date(31,January,2015), Period(1,Months), false, Date(28,February,2015)),

//HalfMonthModifiedFollowing
Expand Down
32 changes: 32 additions & 0 deletions test-suite/cashflows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,38 @@ BOOST_AUTO_TEST_CASE(testIrregularFirstCouponReferenceDatesAtEndOfMonth) {
"got " << firstCoupon->referencePeriodStart());
}

BOOST_AUTO_TEST_CASE(testIrregularFirstCouponReferenceDatesAtEndOfCalendarMonth) {
BOOST_TEST_MESSAGE("Testing irregular first coupon reference dates at end of calendar month with end of month enabled...");
Schedule schedule =
MakeSchedule()
.withCalendar(UnitedStates(UnitedStates::GovernmentBond))
.from(Date(30, September, 2017)).to(Date(30, September, 2022))
.withTenor(6*Months)
.withConvention(Unadjusted)
.withTerminationDateConvention(Unadjusted)
.withFirstDate(Date(31, March, 2018))
.withNextToLastDate(Date(31, March, 2022))
.endOfMonth()
.backwards();

Leg leg = FixedRateLeg(schedule)
.withNotionals(100.0)
.withCouponRates(0.01875, ActualActual(ActualActual::ISMA));

for (const auto& elem : leg) {
BOOST_TEST_MESSAGE("Reference Period: " << ext::dynamic_pointer_cast<Coupon>(elem)->referencePeriodStart() << " - " << ext::dynamic_pointer_cast<Coupon>(elem)->referencePeriodEnd());
BOOST_TEST_MESSAGE("Amount: " << ext::dynamic_pointer_cast<Coupon>(elem)->amount());
}

ext::shared_ptr<Coupon> firstCoupon =
ext::dynamic_pointer_cast<Coupon>(leg.front());
if (firstCoupon->referencePeriodStart() != Date(30, September, 2017))
BOOST_ERROR("Expected reference start date at end of calendar day of the month, "
"got " << firstCoupon->referencePeriodStart());
// Expect first cashflow to be 0.9375
BOOST_TEST(firstCoupon->amount() == 0.9375, boost::test_tools::tolerance(0.0001));
}

BOOST_AUTO_TEST_CASE(testIrregularLastCouponReferenceDatesAtEndOfMonth) {
BOOST_TEST_MESSAGE("Testing irregular last coupon reference dates with end of month enabled...");
Schedule schedule =
Expand Down
Loading