FA-61306 / Bond day-count conventions / Open access
Act/Act AFB whole-year decomposition: the stub denominator follows the leap status of the stub end year · case 01
Stubs that contain a leap day from the previous year, or none at all, use the wrong year length.
ROOT CAUSE
The denominator tests leap(stub_end.year) instead of searching the stub for 29 February.
VERIFIED REPAIR
Use 366 only when a 29 February lies inside the stub.
Unsuccessful approach: Testing the start year has the mirror-image problem.
Case contract
Inputs start < end [y,m,d]. Count whole years n stepping back from the end date (an end of 29 February maps to 28 February in non-leap years) while the stepped date is not before start. The stub runs from start to the end stepped back n years; its denominator is 366 if a 29 February lies in (start, stub end], else 365. Return n + stub days/denominator rounded to 9 decimals.
Why this case matters
Bond accrual and pricing systems depend on exact day-count arithmetic; a single-day error changes settlement cash.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
N = 1
observations = []
def solve(a, b):
A = datetime.date(*a)
B = datetime.date(*b)
def leap(y):
return (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
def back_years(n):
y = B.year - n
if B.month == 2 and B.day == 29 and not leap(y):
return datetime.date(y, 2, 28)
return datetime.date(y, B.month, B.day)
n = 0
while back_years(n + 1) >= A:
n += 1
stub_end = back_years(n)
has29 = any(leap(y) and A < datetime.date(y, 2, 29) <= stub_end for y in range(A.year, stub_end.year + 1))
den = 366 if leap(stub_end.year) else 365
return round(n + (stub_end - A).days / den, 9)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression stub denominator source 1', [[2032, 5, 5], [2036, 10, 2]], 4.410958904], ['regression stub denominator source 2', [[2052, 9, 30], [2060, 10, 9]], 8.024657534], ['partial repair probe 1', [[1999, 8, 31], [2007, 4, 1]], 7.584699454], ['partial repair probe 2', [[1996, 12, 22], [2004, 2, 29]], 7.18630137], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2045, 1, 30], [2052, 5, 23]], 7.309589041], ['normal control 2', [[2001, 6, 1], [2003, 5, 15]], 1.953424658]], [['regression stub denominator source 1', [[2055, 10, 31], [2062, 2, 9]], 6.276712329], ['regression stub denominator source 2', [[2020, 6, 30], [2021, 9, 8]], 1.191780822], ['partial repair probe 1', [[1991, 12, 29], [1993, 10, 14]], 1.792349727], ['partial repair probe 2', [[2007, 3, 19], [2008, 2, 29]], 0.948087432], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2090, 4, 30], [2094, 9, 11]], 4.367123288], ['normal control 2', [[2020, 2, 15], [2024, 2, 29]], 4.038251366]], [['regression stub denominator source 1', [[2048, 11, 1], [2050, 11, 11]], 2.02739726], ['regression stub denominator source 2', [[2068, 4, 18], [2069, 12, 1]], 1.621917808], ['partial repair probe 1', [[2048, 12, 15], [2055, 4, 3]], 6.298630137], ['partial repair probe 2', [[2003, 7, 18], [2008, 2, 29]], 4.617486339], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2021, 2, 21], [2024, 2, 29]], 3.019178082], ['normal control 2', [[2014, 5, 4], [2016, 2, 29]], 1.821917808]], [['regression stub denominator source 1', [[2080, 5, 22], [2086, 7, 16]], 6.150684932], ['regression stub denominator source 2', [[2032, 4, 16], [2038, 9, 3]], 6.383561644], ['partial repair probe 1', [[1992, 7, 29], [1997, 7, 8]], 4.942465753], ['partial repair probe 2', [[2000, 12, 6], [2008, 2, 29]], 7.230136986], ['boundary control 1', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['boundary control 2', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['normal control 1', [[2002, 2, 11], [2008, 2, 29]], 6.046575342], ['normal control 2', [[2079, 4, 12], [2082, 5, 2]], 3.054794521]], [['regression stub denominator source 1', [[2024, 2, 29], [2027, 7, 20]], 3.389041096], ['regression stub denominator source 2', [[2016, 5, 31], [2018, 11, 21]], 2.476712329], ['partial repair probe 1', [[2027, 8, 10], [2028, 2, 29]], 0.554644809], ['partial repair probe 2', [[2067, 10, 31], [2069, 10, 1]], 1.918032787], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2009, 2, 17], [2016, 2, 29]], 7.030136986], ['normal control 2', [[2012, 2, 21], [2020, 2, 29]], 8.021857923]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression stub denominator source 1 | 4.409836066 | 4.410958904 | Failed |
| regression stub denominator source 2 | 8.024590164 | 8.024657534 | Failed |
| partial repair probe 1 | 7.584699454 | 7.584699454 | Passed |
| partial repair probe 2 | 7.18630137 | 7.18630137 | Passed |
| boundary control 1 | 1.0 | 1.0 | Passed |
| boundary control 2 | 1.0 | 1.0 | Passed |
| normal control 1 | 7.309589041 | 7.309589041 | Passed |
| normal control 2 | 1.953424658 | 1.953424658 | Passed |
SHA-256 / 82b38913abd3060366c0631b060f3b8e5f050487a228d82dc836af679fcdfd76
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
N = 1
observations = []
def solve(a, b):
A = datetime.date(*a)
B = datetime.date(*b)
def leap(y):
return (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
def back_years(n):
y = B.year - n
if B.month == 2 and B.day == 29 and not leap(y):
return datetime.date(y, 2, 28)
return datetime.date(y, B.month, B.day)
n = 0
while back_years(n + 1) >= A:
n += 1
stub_end = back_years(n)
has29 = any(leap(y) and A < datetime.date(y, 2, 29) <= stub_end for y in range(A.year, stub_end.year + 1))
den = 366 if leap(A.year) else 365
return round(n + (stub_end - A).days / den, 9)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression stub denominator source 1', [[2032, 5, 5], [2036, 10, 2]], 4.410958904], ['regression stub denominator source 2', [[2052, 9, 30], [2060, 10, 9]], 8.024657534], ['partial repair probe 1', [[1999, 8, 31], [2007, 4, 1]], 7.584699454], ['partial repair probe 2', [[1996, 12, 22], [2004, 2, 29]], 7.18630137], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2045, 1, 30], [2052, 5, 23]], 7.309589041], ['normal control 2', [[2001, 6, 1], [2003, 5, 15]], 1.953424658]], [['regression stub denominator source 1', [[2055, 10, 31], [2062, 2, 9]], 6.276712329], ['regression stub denominator source 2', [[2020, 6, 30], [2021, 9, 8]], 1.191780822], ['partial repair probe 1', [[1991, 12, 29], [1993, 10, 14]], 1.792349727], ['partial repair probe 2', [[2007, 3, 19], [2008, 2, 29]], 0.948087432], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2090, 4, 30], [2094, 9, 11]], 4.367123288], ['normal control 2', [[2020, 2, 15], [2024, 2, 29]], 4.038251366]], [['regression stub denominator source 1', [[2048, 11, 1], [2050, 11, 11]], 2.02739726], ['regression stub denominator source 2', [[2068, 4, 18], [2069, 12, 1]], 1.621917808], ['partial repair probe 1', [[2048, 12, 15], [2055, 4, 3]], 6.298630137], ['partial repair probe 2', [[2003, 7, 18], [2008, 2, 29]], 4.617486339], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2021, 2, 21], [2024, 2, 29]], 3.019178082], ['normal control 2', [[2014, 5, 4], [2016, 2, 29]], 1.821917808]], [['regression stub denominator source 1', [[2080, 5, 22], [2086, 7, 16]], 6.150684932], ['regression stub denominator source 2', [[2032, 4, 16], [2038, 9, 3]], 6.383561644], ['partial repair probe 1', [[1992, 7, 29], [1997, 7, 8]], 4.942465753], ['partial repair probe 2', [[2000, 12, 6], [2008, 2, 29]], 7.230136986], ['boundary control 1', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['boundary control 2', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['normal control 1', [[2002, 2, 11], [2008, 2, 29]], 6.046575342], ['normal control 2', [[2079, 4, 12], [2082, 5, 2]], 3.054794521]], [['regression stub denominator source 1', [[2024, 2, 29], [2027, 7, 20]], 3.389041096], ['regression stub denominator source 2', [[2016, 5, 31], [2018, 11, 21]], 2.476712329], ['partial repair probe 1', [[2027, 8, 10], [2028, 2, 29]], 0.554644809], ['partial repair probe 2', [[2067, 10, 31], [2069, 10, 1]], 1.918032787], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2009, 2, 17], [2016, 2, 29]], 7.030136986], ['normal control 2', [[2012, 2, 21], [2020, 2, 29]], 8.021857923]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression stub denominator source 1 | 4.409836066 | 4.410958904 | Failed |
| regression stub denominator source 2 | 8.024590164 | 8.024657534 | Failed |
| partial repair probe 1 | 7.58630137 | 7.584699454 | Failed |
| partial repair probe 2 | 7.18579235 | 7.18630137 | Failed |
| boundary control 1 | 1.0 | 1.0 | Passed |
| boundary control 2 | 1.0 | 1.0 | Passed |
| normal control 1 | 7.309589041 | 7.309589041 | Passed |
| normal control 2 | 1.953424658 | 1.953424658 | Passed |
SHA-256 / b10d3b30b6057efb1a69e5a1db5caeed741a7aadbfa144cc2dfa7c56c34221d8
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
N = 1
observations = []
def solve(a, b):
A = datetime.date(*a)
B = datetime.date(*b)
def leap(y):
return (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
def back_years(n):
y = B.year - n
if B.month == 2 and B.day == 29 and not leap(y):
return datetime.date(y, 2, 28)
return datetime.date(y, B.month, B.day)
n = 0
while back_years(n + 1) >= A:
n += 1
stub_end = back_years(n)
has29 = any(leap(y) and A < datetime.date(y, 2, 29) <= stub_end for y in range(A.year, stub_end.year + 1))
den = 366 if has29 else 365
return round(n + (stub_end - A).days / den, 9)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression stub denominator source 1', [[2032, 5, 5], [2036, 10, 2]], 4.410958904], ['regression stub denominator source 2', [[2052, 9, 30], [2060, 10, 9]], 8.024657534], ['partial repair probe 1', [[1999, 8, 31], [2007, 4, 1]], 7.584699454], ['partial repair probe 2', [[1996, 12, 22], [2004, 2, 29]], 7.18630137], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2045, 1, 30], [2052, 5, 23]], 7.309589041], ['normal control 2', [[2001, 6, 1], [2003, 5, 15]], 1.953424658]], [['regression stub denominator source 1', [[2055, 10, 31], [2062, 2, 9]], 6.276712329], ['regression stub denominator source 2', [[2020, 6, 30], [2021, 9, 8]], 1.191780822], ['partial repair probe 1', [[1991, 12, 29], [1993, 10, 14]], 1.792349727], ['partial repair probe 2', [[2007, 3, 19], [2008, 2, 29]], 0.948087432], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2090, 4, 30], [2094, 9, 11]], 4.367123288], ['normal control 2', [[2020, 2, 15], [2024, 2, 29]], 4.038251366]], [['regression stub denominator source 1', [[2048, 11, 1], [2050, 11, 11]], 2.02739726], ['regression stub denominator source 2', [[2068, 4, 18], [2069, 12, 1]], 1.621917808], ['partial repair probe 1', [[2048, 12, 15], [2055, 4, 3]], 6.298630137], ['partial repair probe 2', [[2003, 7, 18], [2008, 2, 29]], 4.617486339], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2021, 2, 21], [2024, 2, 29]], 3.019178082], ['normal control 2', [[2014, 5, 4], [2016, 2, 29]], 1.821917808]], [['regression stub denominator source 1', [[2080, 5, 22], [2086, 7, 16]], 6.150684932], ['regression stub denominator source 2', [[2032, 4, 16], [2038, 9, 3]], 6.383561644], ['partial repair probe 1', [[1992, 7, 29], [1997, 7, 8]], 4.942465753], ['partial repair probe 2', [[2000, 12, 6], [2008, 2, 29]], 7.230136986], ['boundary control 1', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['boundary control 2', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['normal control 1', [[2002, 2, 11], [2008, 2, 29]], 6.046575342], ['normal control 2', [[2079, 4, 12], [2082, 5, 2]], 3.054794521]], [['regression stub denominator source 1', [[2024, 2, 29], [2027, 7, 20]], 3.389041096], ['regression stub denominator source 2', [[2016, 5, 31], [2018, 11, 21]], 2.476712329], ['partial repair probe 1', [[2027, 8, 10], [2028, 2, 29]], 0.554644809], ['partial repair probe 2', [[2067, 10, 31], [2069, 10, 1]], 1.918032787], ['boundary control 1', [[2024, 1, 1], [2025, 1, 1]], 1.0], ['boundary control 2', [[2023, 2, 28], [2024, 2, 29]], 1.0], ['normal control 1', [[2009, 2, 17], [2016, 2, 29]], 7.030136986], ['normal control 2', [[2012, 2, 21], [2020, 2, 29]], 8.021857923]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression stub denominator source 1 | 4.410958904 | 4.410958904 | Passed |
| regression stub denominator source 2 | 8.024657534 | 8.024657534 | Passed |
| partial repair probe 1 | 7.584699454 | 7.584699454 | Passed |
| partial repair probe 2 | 7.18630137 | 7.18630137 | Passed |
| boundary control 1 | 1.0 | 1.0 | Passed |
| boundary control 2 | 1.0 | 1.0 | Passed |
| normal control 1 | 7.309589041 | 7.309589041 | Passed |
| normal control 2 | 1.953424658 | 1.953424658 | Passed |
SHA-256 / ccb5ad41e9112fd0e877c3e3f8b313d4b83254f67a8b8e9d5616448444e39464
Verification & scope
A deterministic toy contract stated explicitly in the contract field; no claim of conformance to any published convention text. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:46:54.143374+00:00.
Case digest / 1aba9944d12c668363c49fd2855eddd5942bfae7e96bf5187ce2fa678a1c000d