bizdays
users usually argue me out of inconsistencies
between bizdays
and offset
functions. These
inconsistencies appear every time a non business day is used as argument
of one of these funcions. This might sound strange, but this is the
expected behavior for these functions. To handle that I introduced the
ability to create financial and non financial calendars. This vignette
shows that the pointed inconsistencies only appears when we work with
financial calendars and explains what to do in such situations. It also
illustrates how the business days are counted.
To compute the number of business days between two dates effectively
and with a good performance, bizdays
creates two indexes.
The two indexes are called forward and backward. These indexes associate
a number to each date from the calendar’s start_date
up to
its end_date
. In the forward index, the first business day
is 1, the second is 2 and so on. The backward index starts in the end
with the last business day equals 1 and goes increasing towards the
begining. This works fine in financial calculations because between two
consecutive business days we have 1 day, in both indexes. The non
business days are gaps and they share the index with their business days
neighbors, see the figure below. The blue boxes are working days and the
green ones nonworking days.
Differently we can imagine that each business day in the sequence of dates of the calendar contributes with 1 and the non business days with 0. The forward index does an accumulated sum starting from the begining and the backward does the same starting in the end.
As we can see in the figure above, from Tuesday to Monday we have 7 days (one week), starting at 1 and ending up at 7 in the current days index. In the business days indexes the counting starts at 1 and ends up at 4 and 4 is the total number of business days shown.
To compute the number of business days between two dates bizdays uses
these two indexes to handle the different situations where the arguments
from
and to
are nonworking days.
Let’s create a calendar a non financial calendar with these dates to illustrate.
library(bizdays)
create.calendar(name = "example1", weekdays = c("saturday", "sunday"), start.date = "2017-01-24", end.date = "2017-01-30", holidays = "2017-01-25", financial = FALSE)
calendars()[["example1"]] # view the calendar
## example1 non financial calendar
## 1 holidays
## 2 weekdays (saturday, sunday)
## range from 2017-01-24 to 2017-01-30
## bizdays arguments adjust
## from: none
## to: none
Calling the bizdays
function to compute the business
days from 2017-01-24 to 2017-01-26 results in:
## [1] 2
2 business days that is the amount of business days in the given interval.
In another example, starting and ending up at the holiday, we get
## [1] 1
## [1] 1
as expected it returns 1 in both cases. And also if you offset the
date 2017-01-25
by one business day, you end up in one of
its neighbors.
## [1] "2017-01-24" "2017-01-26"
In the situations where both, from
and to
,
are non business days, we get:
## [1] 2
## [1] 2
which is also correct because we have two business days into these intervals.
The devil is in the financial calendars. These examples give unexpected results. Let’s create a financial calendar with the same dates.
create.calendar(name = "example2", weekdays = c("saturday", "sunday"), start.date = "2017-01-24", end.date = "2017-01-30", holidays = "2017-01-25", financial = TRUE)
calendars()[["example2"]] # view the calendar
## example2 financial calendar
## 1 holidays
## 2 weekdays (saturday, sunday)
## range from 2017-01-24 to 2017-01-30
## bizdays arguments adjust
## from: none
## to: none
All strange situations appear when non business days are passed as
arguments to bizdays
and offset
function. In
the first example above, where we count the number of business days in
the interval (2017-01-25, 2017-01-26).
## [1] 0
The result is 0 indicating that we don’t have one business day to compound an interest rate. On the other hand, if we offset the non business day 2017-01-25 by one we have
## [1] "2017-01-26"
which might sound non sense, given that bizdays
returns
0. But, the non sense is the use of non business days as arguments while
working with financial calendars. To handle this situation I suggest the
functions preceding
(or adjust_previous
) and
following
(or adjust_next
). These functions
move the given date to the previous (or next) business date, if it is
not a business day, otherwise the given date is returned. In the example
above we can use preceding
.
## [1] "2017-01-24"
## [1] 1
## [1] "2017-01-26"
If you don’t want to call these functions every time you call
bizdays
, you can set the arguments adjust_from
and adjust_to
on create.calendar
. These
arguments have been created, to execute a date adjustment of the
arguments from
and to
according to users
needs. These arguments must be set with one of these three
functions:
adjust_none
: does not execute date adjustment, this is
the default valuepreceding
or adjust_previous
: move the
date for the previous business day if it is nonworking dayfollowing
or adjust_next
: move the date
for the next business day if it is nonworking dayIn our third example, let’s set these arguments
create.calendar(name = "example3", weekdays = c("saturday", "sunday"), start.date = "2017-01-24", end.date = "2017-01-30", holidays = "2017-01-25", financial = TRUE, adjust.from = preceding, adjust.to = following)
calendars()[["example3"]] # view the calendar
## example3 financial calendar
## 1 holidays
## 2 weekdays (saturday, sunday)
## range from 2017-01-24 to 2017-01-30
## bizdays arguments adjust
## from: preceding
## to: following
we have
## [1] 1
## [1] "2017-01-26"
as expected.