Bizcal – Pythonic Business (Trading) Calendar
Bizcal is a powerful yet simple business calendar package with three unique features:
- Compatibility:
bizcal.Dateis a drop-in replacement fordatetime.date. - Pythonic: intuitive syntax for calendar semantics.
yesterday = day - 1,tomorrow = day + 1prev_bizday = day << 1,next_bizday = day >> 1
- Holiday-aware: not just when not trading, but also why.
Bizcal is listed on PyPI.
Compatibility
Bizcal abstracts dates with Date class, a direct subclass of the standard
datetime.date, allowing it to seamlessly integrate into legacy codebases as
a drop-in replacement for datetime.date.
Additionally, Date includes several convenient methods and properties designed
to simplify your code.
Pythonic
Code snippets speak for themselves. Check more in unit tests.
from bizcal import Calendar
cne = Calendar([
'2023: 0101-2, 0121-29, 0405, 0429-0503, 0622-5, 0929-1008',
'2024: 0101, 0209-18, 0404-7, 0501-5, 0608-10, 0914-17, 1001-7',
])
day = cne('20240210')
# shift trading day with >> and <<
while not day:
day = day >> 1
prev_trading_day = day << 1
next_trading_day = day >> 1
# shift calendar day with + and -
yesterday = day - 1
tomorrow = day + 1
# period abstraction
period = cne[20240101, 20240131]
print(len(period), 'trading days')
# whole period
print(sum(1 for d in cne['*'].days if d.open), 'trading days')
# iterate trading days
for day in period:
print(f'{day} is a trading day')
# iterate calendar days
for day in period.days:
flag = day and 'trading' or (day.holiday and 'holiday' or 'weekend')
print(f'{day} is {flag}')
Holiday-aware
Certain exchanges, like SHFE and DCE, adjust their trading hours based on holiday schedules. Bizcal not only identifies business (open) and non-business (closed) days but also specifies if a non-business day is a holiday.
Crypto
Crypto markets operate continuously, so dedicated trading calendar utilities may appear unnecessary. However, when a system must support multiple assets, a single interface for managing trading calendars is still valuable, and often essential.
Bizcal supports crypto markets by allowing the holiday set to be empty.
API Reference
Calendar definition
from bizcal import Calendar
cal = Calendar('/path/to/calendar.file')
cal = Calendar([
'2023: 0101-2, 0121-29, 0405, 0429-0503, 0622-25, 0929-1008',
'2024: 0101, 0209-18, 0404-7, 0501-5, 0608-10, 0914-17, 1001-7',
])
cal.min # 2023-01-01
cal.max # 2024-12-31
# define crypto trading calendar for 2020-2025
cal2 = Calendar('2020-5')
The Calendar(spec) constructor accepts spec as either a string or a list
of strings.
- If
specis a string, it is treated as a path to a calendar definition file. Each line in the file defines the holidays (and potentially weekends) for a specific year.- The only exception is: when it starts with four digits, it is treated as a range of years for crypto markets, where no holidays exist.
- If
specis a list of strings, each string represents a line of calendar definitions for one year. - The year line format is
YYYY: holidays, holidays, ..., whereholidaysare specified as date ranges in the formatMMDD,MMDD-DD, orMMDD-MMDD. - Properties
minandmaxgives lower- and upper-bound of this calendar.
Exclude dates from business days
from bizcal import Calendar
cal = Calendar('2024: 0101, 0209-18, 0404-7, 0501-5, 0608-10, 0914-17, 1001-7')
cal.exclude([20240102, '20240103'])
cal(20240102).open # False
cal(20240102).holiday # True
- In some cases, ad-hoc exclusion of special days could be convenient.
- Excluded dates are treated as holidays.
- YYYYMMDD string, integer, or a list of them are accepted.
Date creation
Date objects can only be created from a Calendar object using call
syntax. Multiple input formats are supported.
cal = Calendar([...])
day = cal(20240501) # integer
day = cal('20240501') # YYYYMMDD (string)
day = cal('2024-05-01') # YYYY-MM-DD
day = cal('2024.05.01') # YYYY.MM.DD
day = cal(2024, 5, 1) # year, month, day
day = cal((2024, 5, 1)) # tuple
day = cal([2024, 5, 1]) # list
day = cal(date) # date or even Date object
day = cal([2025, 1, 1]) # error, as date is out of calendar scope
Date manipulation
A Date object can be shifted both as calendar days and business days. The
+ and - operators shift the date as calendar days, while the >> and <<
operators shift the date as business days.
cal = Calendar([...])
day = cal(20240501)
day + 10 # 2024-05-11
day - 1 # 2024-04-30
day >> 10 # 2024-05-17
day << 10 # 2024-04-17
Date methods and properties
Date is a subclass of datetime.date, so all date methods are supported.
Extra Properties
Date.open: Indicates if it is a business day (boolean).Date.__bool__(): Boolean operator, returns if it is a business day.
Date.holiday: Indicates if it is a holiday (boolean).Date.num: Returns the date as anYYYYMMDDinteger.Date.str: Returns the date as a"YYYYMMDD"string.Date.eom: Indicates if it is the end of the month (boolean).
Extra Methods
Date.spec(sep=''): Returns the date as aYYYY{sep}MM{sep}DDstring.Date.shift(days): Performs an explicit business day shift.
Period definition
A period of dates can be defined using the indexing (brackets) syntax.
cal = Calendar([...])
period = cal[20240101, 20240131]
jan = cal['20240101-31']
q1 = cal['202401-3']
h1 = cal['202401-06']
years = cal['2023-4']
period = cal[cal('20240101'), cal(2024, 1, 1) + 30]
print(len(jan)) # count trading days
print(jan.spec) # period spec string
The length of a period (len(period)) is the number of business days in that
period.
Period iteration
The primary use case of a period is date iteration. Two generator properties,
bizdays and days, are implemented for business day and calendar day
iteration, respectively. The default iteration method (__iter__) is for
business days.
# iterate business days
for day in cal['202401']:
print(f'{day} is a trading day')
# iterate calendar days
for day in cal['20240215-20'].days:
flag = day and 'trading' or (day.holiday and 'holiday' or 'weekend')
print(f'{day} is {flag}')
Date span parsing
Static method Calendar.span(spec: str) parses a string spec into
(beg, end) tuple of dates. datetime.date objects are returned.
from bizcal import Calendar
beg, end = Calendar.span('202402')
Metadata
Release files for bizcal 0.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bizcal-0.5.1-py3-none-any.whl | Python 3 | none | any | Details |
Release files / bizcal-0.5.1-py3-none-any.whl
| Download URL | bizcal-0.5.1-py3-none-any.whl |
|---|---|
| Size | 7.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4f25bae589c570654641616581f60bc5d120c84ba54f7190642f83f68e25da2b
|
|
BLAKE2b-256 checksum How to use checksums |
233d70fac3a3f4941208a698d9f6de0d886d35c9c7d67bc79c9d886aac01facd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Security for bizcal 0.5.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.