Skip to content

Use a 365.25 denominator for the JULIAN day count - #253

Open
dylanpulver wants to merge 1 commit into
domokane:masterfrom
dylanpulver:fix-julian-day-count-denominator
Open

Use a 365.25 denominator for the JULIAN day count#253
dylanpulver wants to merge 1 commit into
domokane:masterfrom
dylanpulver:fix-julian-day-count-denominator

Conversation

@dylanpulver

Copy link
Copy Markdown

DayCountTypes.JULIAN documents itself at day_count.py:60 as "actual divided by 365.25 as in Julian Calendar", but year_frac divides by 365 at day_count.py:284. A Julian year is 365.25 days by definition, so the member as shipped returns exactly the same accrual factor as ACT_365F and carries no distinct meaning.

On current master, 1 Jan 2019 -> 21 May 2019:

ACT_365F   acc_factor=0.3835616438356164 den=365
JULIAN     acc_factor=0.3835616438356164 den=365.0   <- identical

after: JULIAN acc_factor=0.38329911019849416 den=365.25.

How this happened (310bab06, "Significant edits to improve DiscountCurve..."): that commit renamed SIMPLE -> JULIAN, rewrote the comment to say 365.25, and replaced den = G_DAYS_IN_YEAR with a hardcoded den = 365.0 — the old constant's value rather than the new definition's. It also repointed the test at DayCountTypes.ASTRONOMICAL, which does not exist; 3c1c7f0c repaired the test to JULIAN two days later but left the pre-rename expected value, so the test has been pinning 365 ever since. G_DAYS_IN_YEAR is still imported at day_count.py:12 and is now otherwise unused, which is the leftover of that inline.

Change: den = 365.25, and the unit test's expected value re-derived from the definition rather than from the implementation — 140 days / 365.25 = 0.38330 — plus an assertion that JULIAN does not collapse onto ACT_365F.

Measured, same command and venv (Python 3.13, pytest unit_tests, which is what testpaths gives CI):

  • before, unmodified master 8ba5e2c0: 957 passed
  • after: 957 passed

Two mutants, to show the test is not vacuous:

  • revert day_count.py only, keep the new test -> assert 0.3836 == 0.3833, 1 failed / 9 passed
  • naive fix den = G_DAYS_IN_YEAR (restoring the now-dead import) -> same failure, which is the point: 365 in any spelling is not the Julian year

How I found it: not from real FinancePy usage. I was systematically reading day-count convention tables across quant libraries looking for branches that are byte-identical to a sibling while their own comment says otherwise. JULIAN matched, and the git history confirmed the intent rather than my guess at it.

Not tested: regression_tests/ (not in testpaths) — but grep -rn JULIAN finds no use of this member anywhere in financepy/ outside its own definition, so no internal pricing path changes. If you intended JULIAN to stay a 365-denominator alias, the right fix is the comment instead and I'm happy to flip it.

This was written with AI assistance (Claude).

The JULIAN member documents itself as "actual divided by 365.25 as in
Julian Calendar", but year_frac divides by 365, which makes it return
exactly the same accrual factor as ACT_365F.

The rename from SIMPLE to JULIAN inlined the old G_DAYS_IN_YEAR (365)
constant rather than the 365.25 the new name and comment call for; the
unit test kept the pre-rename expected value, so it pinned 365.

Update the test to the value implied by the definition: 1 Jan 2019 to
21 May 2019 is 140 days, and 140 / 365.25 = 0.38330.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant