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

Calendar::advance() by business days when starting on a holiday #2093

Open
eltoder opened this issue Oct 14, 2024 · 8 comments
Open

Calendar::advance() by business days when starting on a holiday #2093

eltoder opened this issue Oct 14, 2024 · 8 comments

Comments

@eltoder
Copy link
Contributor

eltoder commented Oct 14, 2024

When calling Calendar::advance() with business days starting on a weekend or a holiday, the current logic "consumes" one day to get to a good day and then advances by N-1 business days. In other words (assuming a positive N)

advance(holiday, N, Days) == advance(adjust(holiday), N-1, Days)

for example,

>>> cal = ql.WeekendsOnly()
>>> cal.advance(ql.Date(12, 10, 2024), 1, ql.Days)  # one business day after Saturday
Date(14,10,2024)  # we got Monday, but should be Tuesday

This isn't right. The logic should be

advance(holiday, N, Days) == advance(adjust(holiday), N, Days)

In other words, we should adjust the date first (Following or Preceding depending on the sign of N), and then move by N business days.

I see that a number of places in the code work-around the issue by explicitly calling adjust() on the date before passing it into advance(). This seems error-prone though and easy to forget. For example, it is missing in payment dates calculation in coupons (here and here).

What should we do: add adjust() to all calls where it is missing, or change advance() to handle this itself? I realize the latter is a potentially scary change since it can affect a lot of users, but the former is probably more maintenance and potential for bugs long term.

@eltoder
Copy link
Contributor Author

eltoder commented Oct 14, 2024

One argument for why advance() should work like this, is that otherwise it is inconsistent with businessDaysBetween():

businessDaysBetween(date, advance(date, N, Days)) == N

should hold for all date and N.

@jongbongan
Copy link
Contributor

jongbongan commented Oct 15, 2024

I think the latter is more plausible. Because, I think the following should hold, even if i or j == 0

advance(base, i+j, Days) == advance(advance(base, i, Days), j, Days)

@lballabio
Copy link
Owner

Do we have an actual example of a bond with no adjustment for the accrual dates and a payment lag? If so, looking at its payment dates would tell us whether the code in the coupons is right or wrong before we go and change it...

@eltoder
Copy link
Contributor Author

eltoder commented Oct 15, 2024

@lballabio schedule and payment calendars can be different, so an adjusted schedule date can still be a payment holiday.

That said, I think there were some swaps with unadjusted schedules and a payment lag. Let me see if we can find one.

@lballabio
Copy link
Owner

Thanks! An adjusted schedule date which is a payment holiday would be just as good as a check, though.

@eltoder
Copy link
Contributor Author

eltoder commented Oct 17, 2024

@lballabio I can't find a document with the spec, but according to Bloomberg, some BRL NDIRS have unadjusted accrual dates and 1 day payment delay. So a 10y swap starting on 2024-10-15 (Tue) ends on 2034-10-15 (Sun) and has a payment on 2034-10-17, not 2034-10-16.

@lballabio
Copy link
Owner

Ok, so one way or another we have to fix the payment date calculation. I'm a bit more inclined to add the adjust, though, since advance has been like this for more than 20 years and nobody ever complained... (and besides, it's defensible as "the nth business day after the passed date")

@attack68
Copy link

Not completely convinced by this tbh, Brazilian instruments use business days in the DCFs so might be a specialized treatment above.

FWIW in our office internals we have an add_bus_days function which will raise if the start is not a business day (for the reason of this issue) and our advance function has the same approach as Quantlib's with regard to starting on a holiday.

If a Bond coupon period ended on Friday with a 1b.d. payment lag on Monday, we would naturally expect that the same Monday payment date if it ended on Saturday or Sunday. Unfortunately I can't find an instrument that yet demonstrates this.

I'm also now open to the possibility that one size might not fit all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants