FA-60856 / Bond day-count conventions / Open access
30/360 US bond basis: the D2 rule is evaluated before D1 is promoted from February end · case 01
A period from the end of February to a 31st keeps D2=31 and overstates the count by one day.
ROOT CAUSE
The D2=31 test runs before the February-end promotion of D1, so it sees the raw 28 or 29.
VERIFIED REPAIR
Promote a February-end D1 to 30 before testing D2=31 against D1.
Unsuccessful approach: Lowering the D2 threshold to 28 instead also clamps D2 for ordinary starts on the 28th or 29th.
Case contract
Inputs are two [year, month, day] dates. Apply, in order: if both dates are the last day of February, D2 becomes 30; if D1 is the last day of February, D1 becomes 30; if D2 is 31 and (adjusted) D1 is 30 or 31, D2 becomes 30; if D1 is 31, D1 becomes 30. Return 360*(Y2-Y1)+30*(M2-M1)+(D2-D1) as an integer day count.
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
N = 1
observations = []
def solve(a, b):
y1, m1, d1 = a
y2, m2, d2 = b
def feb_end(y, m, d):
leap = (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
return m == 2 and d == (29 if leap else 28)
e1 = feb_end(y1, m1, d1)
e2 = feb_end(y2, m2, d2)
if e1 and e2:
d2 = 30
if d2 == 31 and d1 >= 30:
d2 = 30
if e1:
d1 = 30
if d1 == 31:
d1 = 30
return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression adjustment ordering 1', [[2004, 2, 29], [2007, 7, 31]], 1230], ['regression adjustment ordering 2', [[2075, 2, 28], [2075, 5, 31]], 90], ['partial repair probe 1', [[2000, 12, 29], [2002, 10, 31]], 662], ['partial repair probe 2', [[2074, 10, 28], [2076, 8, 31]], 663], ['boundary control 1', [[2024, 2, 29], [2025, 2, 28]], 360], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2024, 2, 29], [2026, 2, 28]], 720], ['normal control 2', [[2049, 6, 15], [2051, 7, 31]], 766]], [['regression adjustment ordering 1', [[2001, 2, 28], [2001, 3, 31]], 30], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2041, 1, 28], [2041, 3, 31]], 63], ['partial repair probe 2', [[2096, 2, 28], [2099, 10, 31]], 1323], ['boundary control 1', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2059, 6, 30], [2060, 8, 31]], 420], ['normal control 2', [[2003, 8, 14], [2004, 12, 7]], 473]], [['regression adjustment ordering 1', [[2096, 2, 29], [2098, 5, 31]], 810], ['regression adjustment ordering 2', [[2000, 2, 29], [2000, 3, 31]], 30], ['partial repair probe 1', [[2002, 9, 28], [2005, 5, 31]], 963], ['partial repair probe 2', [[2096, 2, 28], [2099, 1, 31]], 1053], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2043, 11, 30], [2046, 2, 28]], 808], ['normal control 2', [[2024, 2, 28], [2026, 2, 28]], 720]], [['regression adjustment ordering 1', [[2096, 2, 29], [2097, 7, 31]], 510], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2073, 12, 29], [2074, 8, 31]], 242], ['partial repair probe 2', [[2000, 2, 28], [2002, 5, 31]], 813], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[1998, 6, 14], [2000, 2, 29]], 615], ['normal control 2', [[2068, 2, 9], [2070, 10, 13]], 964]], [['regression adjustment ordering 1', [[2096, 2, 29], [2099, 3, 31]], 1110], ['regression adjustment ordering 2', [[2096, 2, 29], [2096, 1, 31]], -30], ['partial repair probe 1', [[2024, 2, 28], [2025, 3, 31]], 393], ['partial repair probe 2', [[2046, 8, 28], [2048, 7, 31]], 693], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[2052, 12, 30], [2053, 8, 16]], 226], ['normal control 2', [[2049, 9, 25], [2052, 8, 30]], 1055]]]
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 adjustment ordering 1 | 1231 | 1230 | Failed |
| regression adjustment ordering 2 | 91 | 90 | Failed |
| partial repair probe 1 | 662 | 662 | Passed |
| partial repair probe 2 | 663 | 663 | Passed |
| boundary control 1 | 360 | 360 | Passed |
| boundary control 2 | 180 | 180 | Passed |
| normal control 1 | 720 | 720 | Passed |
| normal control 2 | 766 | 766 | Passed |
SHA-256 / 7386d7f4469c5f004267f9f22174127119385ac56fcada44ab4fe0a581ada1b6
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(a, b):
y1, m1, d1 = a
y2, m2, d2 = b
def feb_end(y, m, d):
leap = (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
return m == 2 and d == (29 if leap else 28)
e1 = feb_end(y1, m1, d1)
e2 = feb_end(y2, m2, d2)
if e1 and e2:
d2 = 30
if d2 == 31 and d1 >= 28:
d2 = 30
if e1:
d1 = 30
if d1 == 31:
d1 = 30
return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression adjustment ordering 1', [[2004, 2, 29], [2007, 7, 31]], 1230], ['regression adjustment ordering 2', [[2075, 2, 28], [2075, 5, 31]], 90], ['partial repair probe 1', [[2000, 12, 29], [2002, 10, 31]], 662], ['partial repair probe 2', [[2074, 10, 28], [2076, 8, 31]], 663], ['boundary control 1', [[2024, 2, 29], [2025, 2, 28]], 360], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2024, 2, 29], [2026, 2, 28]], 720], ['normal control 2', [[2049, 6, 15], [2051, 7, 31]], 766]], [['regression adjustment ordering 1', [[2001, 2, 28], [2001, 3, 31]], 30], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2041, 1, 28], [2041, 3, 31]], 63], ['partial repair probe 2', [[2096, 2, 28], [2099, 10, 31]], 1323], ['boundary control 1', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2059, 6, 30], [2060, 8, 31]], 420], ['normal control 2', [[2003, 8, 14], [2004, 12, 7]], 473]], [['regression adjustment ordering 1', [[2096, 2, 29], [2098, 5, 31]], 810], ['regression adjustment ordering 2', [[2000, 2, 29], [2000, 3, 31]], 30], ['partial repair probe 1', [[2002, 9, 28], [2005, 5, 31]], 963], ['partial repair probe 2', [[2096, 2, 28], [2099, 1, 31]], 1053], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2043, 11, 30], [2046, 2, 28]], 808], ['normal control 2', [[2024, 2, 28], [2026, 2, 28]], 720]], [['regression adjustment ordering 1', [[2096, 2, 29], [2097, 7, 31]], 510], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2073, 12, 29], [2074, 8, 31]], 242], ['partial repair probe 2', [[2000, 2, 28], [2002, 5, 31]], 813], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[1998, 6, 14], [2000, 2, 29]], 615], ['normal control 2', [[2068, 2, 9], [2070, 10, 13]], 964]], [['regression adjustment ordering 1', [[2096, 2, 29], [2099, 3, 31]], 1110], ['regression adjustment ordering 2', [[2096, 2, 29], [2096, 1, 31]], -30], ['partial repair probe 1', [[2024, 2, 28], [2025, 3, 31]], 393], ['partial repair probe 2', [[2046, 8, 28], [2048, 7, 31]], 693], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[2052, 12, 30], [2053, 8, 16]], 226], ['normal control 2', [[2049, 9, 25], [2052, 8, 30]], 1055]]]
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 adjustment ordering 1 | 1230 | 1230 | Passed |
| regression adjustment ordering 2 | 90 | 90 | Passed |
| partial repair probe 1 | 661 | 662 | Failed |
| partial repair probe 2 | 662 | 663 | Failed |
| boundary control 1 | 360 | 360 | Passed |
| boundary control 2 | 180 | 180 | Passed |
| normal control 1 | 720 | 720 | Passed |
| normal control 2 | 766 | 766 | Passed |
SHA-256 / 0c72421f46d4fa110547406b8ff253f139173d1a3e220ac0dfba27ed4e7d4caf
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(a, b):
y1, m1, d1 = a
y2, m2, d2 = b
def feb_end(y, m, d):
leap = (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
return m == 2 and d == (29 if leap else 28)
e1 = feb_end(y1, m1, d1)
e2 = feb_end(y2, m2, d2)
if e1 and e2:
d2 = 30
if e1:
d1 = 30
if d2 == 31 and d1 >= 30:
d2 = 30
if d1 == 31:
d1 = 30
return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression adjustment ordering 1', [[2004, 2, 29], [2007, 7, 31]], 1230], ['regression adjustment ordering 2', [[2075, 2, 28], [2075, 5, 31]], 90], ['partial repair probe 1', [[2000, 12, 29], [2002, 10, 31]], 662], ['partial repair probe 2', [[2074, 10, 28], [2076, 8, 31]], 663], ['boundary control 1', [[2024, 2, 29], [2025, 2, 28]], 360], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2024, 2, 29], [2026, 2, 28]], 720], ['normal control 2', [[2049, 6, 15], [2051, 7, 31]], 766]], [['regression adjustment ordering 1', [[2001, 2, 28], [2001, 3, 31]], 30], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2041, 1, 28], [2041, 3, 31]], 63], ['partial repair probe 2', [[2096, 2, 28], [2099, 10, 31]], 1323], ['boundary control 1', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2059, 6, 30], [2060, 8, 31]], 420], ['normal control 2', [[2003, 8, 14], [2004, 12, 7]], 473]], [['regression adjustment ordering 1', [[2096, 2, 29], [2098, 5, 31]], 810], ['regression adjustment ordering 2', [[2000, 2, 29], [2000, 3, 31]], 30], ['partial repair probe 1', [[2002, 9, 28], [2005, 5, 31]], 963], ['partial repair probe 2', [[2096, 2, 28], [2099, 1, 31]], 1053], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2043, 11, 30], [2046, 2, 28]], 808], ['normal control 2', [[2024, 2, 28], [2026, 2, 28]], 720]], [['regression adjustment ordering 1', [[2096, 2, 29], [2097, 7, 31]], 510], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2073, 12, 29], [2074, 8, 31]], 242], ['partial repair probe 2', [[2000, 2, 28], [2002, 5, 31]], 813], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[1998, 6, 14], [2000, 2, 29]], 615], ['normal control 2', [[2068, 2, 9], [2070, 10, 13]], 964]], [['regression adjustment ordering 1', [[2096, 2, 29], [2099, 3, 31]], 1110], ['regression adjustment ordering 2', [[2096, 2, 29], [2096, 1, 31]], -30], ['partial repair probe 1', [[2024, 2, 28], [2025, 3, 31]], 393], ['partial repair probe 2', [[2046, 8, 28], [2048, 7, 31]], 693], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[2052, 12, 30], [2053, 8, 16]], 226], ['normal control 2', [[2049, 9, 25], [2052, 8, 30]], 1055]]]
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 adjustment ordering 1 | 1230 | 1230 | Passed |
| regression adjustment ordering 2 | 90 | 90 | Passed |
| partial repair probe 1 | 662 | 662 | Passed |
| partial repair probe 2 | 663 | 663 | Passed |
| boundary control 1 | 360 | 360 | Passed |
| boundary control 2 | 180 | 180 | Passed |
| normal control 1 | 720 | 720 | Passed |
| normal control 2 | 766 | 766 | Passed |
SHA-256 / 6604ec644a255c6bdf5d107d6d89a7d601a1c7d577e8f7c41447ae2d75eb1c05
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:49.577016+00:00.
Case digest / 47448b1911c77433d945fe79751a026fd7c8cf02540db8f9c7a499ce38cbf3fe