FAILURE MAP
← Case archive

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.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

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 fixtureActualExpectedOutcome
regression stub denominator source 14.4098360664.410958904Failed
regression stub denominator source 28.0245901648.024657534Failed
partial repair probe 17.5846994547.584699454Passed
partial repair probe 27.186301377.18630137Passed
boundary control 11.01.0Passed
boundary control 21.01.0Passed
normal control 17.3095890417.309589041Passed
normal control 21.9534246581.953424658Passed

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 fixtureActualExpectedOutcome
regression stub denominator source 14.4098360664.410958904Failed
regression stub denominator source 28.0245901648.024657534Failed
partial repair probe 17.586301377.584699454Failed
partial repair probe 27.185792357.18630137Failed
boundary control 11.01.0Passed
boundary control 21.01.0Passed
normal control 17.3095890417.309589041Passed
normal control 21.9534246581.953424658Passed

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 fixtureActualExpectedOutcome
regression stub denominator source 14.4109589044.410958904Passed
regression stub denominator source 28.0246575348.024657534Passed
partial repair probe 17.5846994547.584699454Passed
partial repair probe 27.186301377.18630137Passed
boundary control 11.01.0Passed
boundary control 21.01.0Passed
normal control 17.3095890417.309589041Passed
normal control 21.9534246581.953424658Passed

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