PRE RELEASE
dateroll
makes working with 📅 dates less painful.
Excellent for modelling fixed income math on human-scale dates (y
)
ddh | Date | Duration | Schedule | Calendars | CalendarMath | Parser
Date duration helper function
that takes a str
and returns either a Date
, Duration
, or Schedule
.
from dateroll import ddh
ddh(some_string)
Flavor | Input | Output |
---|---|---|
Today string | ddh('t') |
Date(2024,2,2) |
Date string | ddh('12/31/22') |
Date(2022,12,31) |
Duration string | ddh('+3m') |
Duration(m=3) |
Date math string | ddh('t + 1y') |
Date(2025,2,2) |
Date schedule string | ddh('t, t+5y, 1m') |
[Date(2022,2,2), ...] |
See Parser section for valid strings.
ddh | Date | Duration | Schedule | Calendars | CalendarMath | Parser
Date class
inherits datetime.date
.
Adds business day handling, calendar math, and schedule generation when combined with ddh
, Duration
and Schedule
.
>>> from dateroll import Date
>>> Date(1984, 7 ,2)
Date(year, month, day)
Date(2024, 7, 2)
*^3 integer args: year, month, and day
Name | Input | Output |
---|---|---|
.datetime |
Date(2024,7,2).datetime |
datetime.datetime(2024, 7, 2, 0, 0) |
.date |
Date(2024,7,2).date |
datetime.date(2024, 7, 2, 0, 0) |
.iso |
Date(2024,7,2).iso |
20240702 |
.xls |
Date(2024,7,2).xls |
45475 |
.unix |
Date(2024,7,2).unix |
1719892800 |
.dotw |
Date(2024,7,2).dotw |
"Tue" |
.wotm |
Date(2024,7,2).wotm |
1 |
.woty |
Date(2024,7,2).woty |
27 |
.is_bd(calendar) ¹ |
Date(2024,7,2).is_bd("NY") |
False |
datetime.date.* |
see datetime | see datetime |
¹calendar
can be of the form: "NY"
, ["NY"]
, "NYuLN"
, or ["NY","LN"]
(see Parser
for more details.).
assume d = Date(2000,1,1)
Date
/ Duration
or Date
LHS | Operator | RHS | Result | Input | Output |
---|---|---|---|---|---|
Date |
Duration |
Date |
d + Duration(m=1) |
Date(2000,2,1) |
|
Date |
Duration |
Date |
d - Duration(y=1) |
Date(1999,1,1) |
|
Date |
Date |
Duration |
d - Date(1998,1,1) |
Duration(d=730) |
Date
/ int
(for calendar days)LHS | Operator | RHS | Result | Input | Output |
---|---|---|---|---|---|
Date |
int |
Duration |
d + 5 |
Date(2000,1,6) |
|
Date |
int |
Duration |
d - 1 |
Date(1999,12,31) |
Date
/ Date string or Duration stringLHS | Operator | RHS | Result | Input | Output |
---|---|---|---|---|---|
Date |
Date string | Duration |
d - "1/1/98" |
Duration(d=730) |
|
Date |
Duration string | Date |
d + "1m" |
Date(2000,2,1) |
|
Date |
Duration string | Date |
d - "1y" |
Date(1999,1,1) |
ddh | Date | Duration | Schedule | Calendars | CalendarMath | Parser
Duration class
herits from dateutil.relativedelta
for defining intervals (periods) of time.
Adds business day handling.
>>> from dateroll import Date
>>> Duration(years=1,months=3)
Duration(years=1,months=3)
int
egers.)n
is a postive or negative int
Description | Kwargs | Example |
---|---|---|
Years | years year y |
Duration(y=n) |
Quarters ( 3m) | qtrs qtr q |
Duration(q=n) |
Weeks ( 7d) | weeks week w |
Duration(w=n) |
Calendar days | days cd d |
Duration(d=-n) |
Calendar days w/ roll | roll=roll ¹ |
Duration(d=n,roll="F") |
Business days | bd |
Duration(bd=-n) |
Business days w/ calendar | bd cals=calendar ² |
Duration(bd=-1, cals="FED") |
Combinations | y m bd cals |
Duration(y=1, m=3, bd=-1, cals="FED") |
open items for 1st release: (1) split modified from roll direction into 2 settings, (2) add EOM rule, and (3) maybe add BRL longer months/shorter months rule.
¹roll
can one be following business day ("F"
), preceding business day ("P"
) or the equivalent modified conventions: modified following ("MF"
), or modified previous ("MP"
).
²calendar
can be of the form: "NY"
, ["NY"]
, "NYuLN"
, or ["NY","LN"]
(see Parser
for more details.).
Optional kwargs
for either anchor_start=Date(...)
or anchor_stop=Date(...)
can be user-supplied. These are automatically inherited when Duration
is the result of some math involving a Date
.
Name | Input | Output |
---|---|---|
.years |
Duration(y=5,m=4,d=3,bd=2) |
5 |
.months |
Duration(y=5,m=4,d=3,bd=2) |
4 |
.days |
Duration(y=5,m=4,d=3,bd=2) |
3 |
.bds |
Duration(y=5,m=4,d=3,bd=2) |
2 |
.anchor_start |
ddh('7/7/2000-7/5/2000') |
Dates(2000,7,7) |
.anchor_stop |
ddh('7/7/2000-7/5/2000') |
Dates(2000,7,5) |
.ndays() ¹ |
Duration(y=5,m=4,d=3,bd=2) |
~1478 |
.nyears(counter) ¹² |
Duration(y=5,m=4,d=3,bd=2) |
~4.05 |
¹The n*
methods may require period conversion based upon approximation. see Period Conversions.
²counter
is one of "ACT/360"
, "30/360"
, ... see day counters
assume dur = Duration(years=1)
Duration
/ Duration
LHS | Operator | RHS | Result | Input | Output |
---|---|---|---|---|---|
Duration |
Duration |
Duration |
d + Duration(m=1) |
Duration(y=1,m=1) |
|
Duration |
Duration |
Duration |
d - Duration(m=1) |
Duration(m=11) |
Duration
/ (Date string or Duration string)LHS | Operator | RHS | Result | Input | Output |
---|---|---|---|---|---|
Duration |
Duration string | Date |
d + "1m" |
Date(2000,2,1) |
|
Duration |
Duration string | Date |
d - "1y" |
Date(1999,1,1) |
Date
When a Duration
is the result of an operation with one or more Date
, then 2 additional properties (anchor_start
and/or anchor_stop
) will be set automatically.
Date(1999, 1, 1 )
Duration(m=1)
Duration(anchor_start=Date(1999, 1, 1)
Date(2000, 1, 1)
Date(1999, 1, 1)
Duration(y=1, anchor_start=Date(1999,1,1), anchor_end=date(2000, 1, 1)
When present, these properties allow for more exact period conversion.
If an approximate conversion is required a warning will be raised.
From period | Calendar Days |
---|---|
1y |
1d |
1q |
1d |
1m |
1d |
1w |
7d |
1d |
1d |
1bd |
¹Numerator is days in a year, denominator is 252bd
. Argument can be made for 250-260, just ask!
ddh | Date | Duration | Schedule | Calendars | CalendarMath | Parser
Schedule class
inherits list
.
A schedule is a list
of contiguous dates generated from some start date, stop date, and step increment.
>>> from dateroll import Schedule
>>> sch = Schedule(start=Date(1984, 7 ,2), stop=Date(2024, 7 ,2), step=Duration(y=1)
>>> sch.dates
[Date(1984, 7, 2), Date(1985, 7, 2), ..., Date(2024, 7, 2)]
Name | Description | Type |
---|---|---|
start |
First date in schedule | Date |
stop |
Last date in schedule | Date |
step |
Distance between dates in schedule | Duration |
step
, then schedule is generated backwards from last to first
sch[0:2]
step
, then the scheduled is generated forwards from first ot last
sch[-2:]
Name | Input | Output |
---|---|---|
.start |
sch.start |
Date(1984, 7 ,2) |
.stop |
sch.start |
Date(2024, 7 ,2) |
.step |
sch.start |
Duration(y=1) |
.dates |
Schedule(start=Date(1984, 7... |
[Date(1984, 7, 2), Date(...] (sorted) |
list.* |
see list |
ddh | Date | Duration | Schedule | Calendars | CalendarMath | Parser
Calendars is a database for your holidays.
It's a dict
whose data is always saved for global access. Keys are calendar names, values are ordinary lists of dates.
List calendars
>>> cals.keys()
['WE','ALL','NY']
Get one
>>> Duration(bd=0,cals=cals['NY']) # get / use#
>>> fed = cals.FED # "dot" notation works too
Create one
>>> from dateroll import cals
>>> cals['US'] = [Date(...), Date(...), ...] # add takes list of dates, or Schedule
Delete one
>>> del cals['NY'] # delete
WE
or WKD
for weekend. US
or FED
or NY
for US banking days.Saves in users's home folder
pathlib.Path.home()
Linux:
~/.dateroll/calendars/holiday_lists
Windows:
C:/Users/[USERNAME]/.dateroll/calendars/holiday_lists
shelve
(may added extension depending on python version)Name | Descriptions |
---|---|
.load_all_and_we() |
Creates ALL and WE ¹ |
.load_sample_data() |
Creates ALL ,WE ,LN ,EU ,BR ,NY ,FED ,ECB ,BOE ,BCB using worklendar |
dict.* |
see dict docs |
Name | Description | Source |
---|---|---|
ALL |
All calendar days | Generated for the interval ddh(-200y) ,ddh(+200y) |
WE |
Weekends (Sat/Sun's) | Generated for the interval ddh(-200y) ,ddh(+200y) |
NY FED |
US banking holidays | workalendar ¹ |
EU ECB |
ECB banking holidays | workalendar ¹ |
LN BOE |
UK holidays | workalendar ¹ |
BR BCB |
Brazil holidays | workalendar ¹ |
¹ We used workalender
and generated dates for the same interval as ALL
and WE
(above), data for available calendars is stored in JSON
to eliminate the library dependency. See workalendar for more details, they've got a lot of great coverage.
CAUTION using when someone else's calendars! always verify
ddh | Date | Duration | Schedule | Calendars | CalendarMath | Parser
CalendarMath
is an interim singelton class
that "compiles" Calendar date lists into hashes.
If you use ddh
predominately you'll never need to use this directly.
>>> from dateroll import calmath
Name | Usage | Descriptions |
---|---|---|
.add_bd( |
calmath.add_bd(from_date:Date, n:int, cals=calendar) ¹ Date |
Add n bd's |
.sub_bd( |
calmath.sub_bd(from_date, n, cals=calendar) ¹ Date |
Subtract n bd's` |
.is_bd( |
calmath.is_bd(from_date, cals=calendar) ¹ bool |
Is a bd?` |
.prev_bd( |
calmath.prev_bd(from_date, cals=calendar) ¹ Date |
Advance to next bd |
.next_bd( |
calmath.next_bd(from_date), cals=calendar ¹ Date |
Back up until previous bd |
¹calendar
can be of the form: "NY"
, ["NY"]
, "NYuLN"
, or ["NY","LN"]
(see Parser
for more details.).
A calendar "union" is simple a combination of two calendars (without duplication):
The first time a calendar union is specified (i.e. list of calendars is sent to CalendarMath
), then the hashes are created on the fly and cached. Local to CalendarMath
instance (dateroll.calmath
), not Calendars
instance (dateroll.cals
).
CalendarMath
stores an in-memory cache of "compiled" calendars. A compiled calendar is simply a collection of hash tables to do business data movements in time.
There's several mutation checks, and whenever a Calendars
value is written to, the the entire CalendarMath
instance cache is purged for safety.
ddh | Date | Duration | Schedule | Calendars | CalendarMath | Parser
Parses str
into a thing
: can be either Date
, Duration
, Schedule
.
This is the 🧠 brains behind ddh
.
It a combo of (1) really big regex
, and (2) date validation with dateutil.parser.parse
.
DateRollString
(any of the below)
TodayString
(represents TODAY's Date
)
"t"
, "t0"
, or "today"
Date
DateString
(any Date
)
conventions
: 'american'
, 'european'
, and 'international'
american
: month
day
year
, slashes/dashes/nothing
"7/2/84"
or "07-02-1984"
european
: day
day
year
, slashes/dashes/nothing
"2/7/1984"
or "02-07-84"
international
: year
month
day
, slashes/dashes/nothing
"19840702"
or "19840702"
Date
DurationString
(any Duration
)
DurationPeriodString
unit
and period
int
bd
d
w
m
q
s
(semester) h
(halves), and y
5y3m
or 7w2d
or 1y2bd
are allowedCalendarString
DurationPeriodString
in a DurationString
can be followed by the filtration (or pipe) operator "|"
to denote information from calendar lists of holidays which must be used to perform date aritmetic.
CalendarString
follows rules of calendar (2-3 letters A-Z, all uppercase), and union operator "u"
"|NY"
or "|FED"
"|NY u LN"
(2) or "|FED u BOE u BCB"
(3)
RollingConventionString
"/"
can be followed by one of the 4 rolling methods:
"/F"
if all period adjustments to a date land on non-business day roll forward to next business day"/P"
if all period adjustments to a date land on non-business day roll backwards to previous business day"/MF"
similar to "/F"
but if you cross a month boundary, go backwards to 1st business date in that month"/MP"
similar to "/P"
but if you cross a month boundary, go forwards to last business date in that month"+ 1y 2h 3s 4q 5m 6w 7d 8bd | NY u LN u JP / MF"
ScheduleString
(any Schedule
)
"X, Y, Z"
X
is DateString
, TodayString
, or DateMathString
Y
is is DateString
, TodayString
, or DateMathString
Z
is DurationString
Schedule(start=X, stop=Y, step=Z)
"t-5y, 1/15/25, 1y"
(from 5 years ago to Jan 15 next year, give me all years)DateMathString
(any datemath operation)
"X+Y"
or "Y-X"
X
or Y
can be either DateString
or DurationString
and add/sub operation logic must be supported by Date
and Duration
objects"t+1bd"
or "t-5y"
or "7/2/84+39y"
"t-1y2h3s4q5m6w7d8bd|NYuLNuJP/MF,07/02/1984+99y35bd|FED/P,3q7m5d|WE"
ddh
it, all calendars -unioned ("|NYuLNuJPuFEDuWE"
), modified convention would hold as it was specified once, and P
or F
whould depend on the direction of travel when being appled to a specific date. If approx is needed, it will warn
."t+1m-21bd"
, if you have an anchor date, if 1m
> 21bd
(net adjustment is in the future as opposed to past) it assumes you are rolling foward, and would impliy a following ("/F"
), period conversion is used, see period conversion rules. If modified was specified it would be inherited as well.ddh("t,t+5y,1m")
.==NEEDS WORK==
Usage | Formula |
---|---|
.nyears("30/360") |
|
.nyears("30/360 BB") |
|
.nyears("30/360") |
|
.nyears("30E/360") |
|
.nyears("30E/360 ISDA") |
/36?'s
Usage | Formula |
---|---|
.nyears("ACT/360") |
|
.nyears("30E/364") |
|
.nyears("ACT/365) |
|
.nyears("ACT/365F") |
|
.nyears("ACT/365L") |
/ACT's
Usage | Formula |
---|---|
.nyears("ACT/ACT AFB") |
|
.nyears("ACT/ACT ICMA") |
Usage | Formula |
---|---|
.nyears("BD/252") |
t1 = ddh(some_date_string)
t2 = ddh(another_date_string)
= t1.years
, = t1.years
= t1.months
, = t1.months
= t1.days
, = t1.days
Thank you to the teams behind dateutil
, datetime
, workalendar
, pandas
, numpy
, ICMA
, ISDA
, ISO 8601
, and the countless traders and bond/derivatives accountants worldwide.
It is our hope that fixed income and FI math can be a little less scary for everyone, because every single person in the world is impacted by it.
- The Disent Team