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 string| LHS | 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)
integers.)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) |
DateWhen 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_listsWindows: C:/Users/[USERNAME]/.dateroll/calendars/holiday_listsshelve (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"DateDateString (any Date)
conventions: 'american', 'european', and 'international'
american: monthdayyear, slashes/dashes/nothing
"7/2/84" or "07-02-1984"european: daydayyear, slashes/dashes/nothing
"2/7/1984" or "02-07-84"international: yearmonthday, slashes/dashes/nothing
"19840702" or "19840702"DateDurationString (any Duration)
DurationPeriodString
unit and period
intbd d w m q s (semester) h (halves), and y5y3m 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 DateMathStringY is is DateString, TodayString, or DateMathStringZ is DurationStringSchedule(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