daycounts
Financial day-count conventions in pure Python with zero dependencies.
Implements seven conventions, a year_fraction dispatcher, and an accrued_interest helper. Requires only the Python standard library (datetime).
pip install daycounts
Conventions
| Convention | Enum | Description |
|---|---|---|
| Actual/360 | Convention.ACTUAL_360 |
Actual days / 360 |
| Actual/365 Fixed | Convention.ACTUAL_365_FIXED |
Actual days / 365 |
| Actual/Actual ISDA | Convention.ACTUAL_ACTUAL_ISDA |
Split by calendar year; leap/non-leap denominators |
| 30/360 US Bond Basis | Convention.THIRTY_360_US |
30-day months, 360-day year; EOM February rules apply |
| 30E/360 Eurobond | Convention.THIRTY_E_360 |
30-day months, 360-day year; no February adjustment |
| NL/365 | Convention.NL_365 |
(Actual days - Feb 29 count in period) / 365 |
| Actual/365L | Convention.ACTUAL_365L |
Actual days / 366 if period or end-year is leap, else / 365 |
See docs/architecture.md for exact formulas and ISDA 2006 references.
NL/365 (Actual/365 No-Leap)
Numerator = actual days between d1 and d2, minus the number of February 29 occurrences in the half-open interval [d1, d2). Denominator = 365.
year_fraction = (actual_days - leap_days_in_period) / 365
Example: 2020-02-28 to 2020-03-01 -- 2 actual days, 1 Feb 29 in interval, fraction = (2 - 1) / 365 = 1/365.
For periods containing no Feb 29, NL/365 agrees exactly with Actual/365 Fixed.
Actual/365L (ISMA-Year)
Numerator = actual days. Denominator = 366 if either:
- the period [d1, d2) contains a February 29, or
- d2 falls in a leap year;
otherwise denominator = 365.
Rule per ISMA Rule Book (2001) Appendix A; also documented in OpenGamma Strata
as DayCounts.ACT_365L.
denom = 366 if (isleap(d2.year) or period_contains_feb29) else 365
year_fraction = actual_days / denom
Examples:
- 2023-01-01 to 2023-07-01: 181 days, d2 in non-leap 2023, no Feb 29 -- 181/365
- 2023-07-01 to 2024-01-01: 184 days, d2 in leap 2024 -- 184/366
- 2020-01-01 to 2020-07-01: 182 days, Feb 29 2020 in period -- 182/366
Usage
import datetime
from daycounts import (
Convention,
actual_360,
actual_365_fixed,
actual_365l,
actual_actual_isda,
nl_365,
thirty_360_us,
thirty_e_360,
year_fraction,
accrued_interest,
)
d1 = datetime.date(2024, 1, 1)
d2 = datetime.date(2024, 7, 1)
# Direct functions
print(actual_360(d1, d2)) # 0.5055... (182/360)
print(actual_365_fixed(d1, d2)) # 0.4986... (182/365)
print(actual_actual_isda(d1, d2)) # 0.4972... (182/366, all in leap 2024)
print(thirty_360_us(d1, d2)) # 0.5000 (180/360)
print(thirty_e_360(d1, d2)) # 0.5000 (180/360)
print(nl_365(d1, d2)) # 0.4959... (181/365, Feb 29 removed)
print(actual_365l(d1, d2)) # 0.4972... (182/366, 2024 is leap)
# Dispatcher (accepts enum or string)
print(year_fraction(d1, d2, Convention.ACTUAL_360))
print(year_fraction(d1, d2, "actual/360"))
print(year_fraction(d1, d2, Convention.NL_365))
print(year_fraction(d1, d2, "nl/365"))
print(year_fraction(d1, d2, Convention.ACTUAL_365L))
print(year_fraction(d1, d2, "actual/365l"))
Accrued interest
# USD 1,000,000 notional, 5% annual coupon, Actual/360
ai = accrued_interest(
face=1_000_000,
rate=0.05,
d1=datetime.date(2024, 1, 1),
d2=datetime.date(2024, 7, 1),
convention="actual/360",
)
# -> 25277.78
Convention string values
# Canonical strings (case-insensitive):
"actual/360"
"actual/365f"
"actual/actual isda"
"30/360 us"
"30e/360"
"nl/365"
"actual/365l"
Install
pip install daycounts
uv add daycounts
Requires Python 3.10+. Zero runtime dependencies.
PyPI publication is pending (new-project rate limit); install from source in the meantime:
pip install git+https://github.com/amaar-mc/daycounts.git
Development
git clone https://github.com/amaar-mc/daycounts
cd daycounts
uv venv .venv && source .venv/bin/activate
uv pip install -e ".[dev]"
pytest -q
ruff check .
mypy src
vs yearfrac / QuantLib
- yearfrac (Excel-compatible): supports ~4 conventions, no Actual/Actual ISDA split-year logic.
- QuantLib / financepy / rateslib: correct and comprehensive, but pull in large scientific-stack dependencies (NumPy, Cython, or C++ extensions).
- daycounts: zero-dependency pip package focused on the five most common ISDA conventions, with strict typing and a clean pure-Python API.
License
MIT. Copyright (c) 2026 Amaar Chughtai.
Metadata
Release files for daycounts 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| daycounts-0.2.0.tar.gz | 842.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| daycounts-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 852.9 kB
Release files / daycounts-0.2.0.tar.gz
| Download URL | daycounts-0.2.0.tar.gz |
|---|---|
| Size | 842.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d2f93a5ebb26280f15fa4485687bdc1b51f274d600a73dbe3ab32c82031ed960
|
|
BLAKE2b-256 checksum How to use checksums |
2a4e7d50e4189a6b1443473fe71ba22c54d36da3e549233d39f764833965ff66
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Release files / daycounts-0.2.0-py3-none-any.whl
| Download URL | daycounts-0.2.0-py3-none-any.whl |
|---|---|
| Size | 10.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
179ab69c3c080367724b4d474458475b1e4be76064a032e4786f476a5544e7df
|
|
BLAKE2b-256 checksum How to use checksums |
4be8794386f27727882bb5d0f9fc7dbaa554f0c17a9a3e10f7be7896b0096dee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|