FA-61191 / Bond day-count conventions / Open access
Canadian-style semi-annual accrued interest: the 183rd day still uses forward accrual · case 01
Settlements exactly 183 days into the period use the wrong formula.
ROOT CAUSE
The branch test uses days > 183.
VERIFIED REPAIR
Switch to the complement at 183 accrued days or more.
Unsuccessful approach: Lowering the threshold to 182 switches a day too early.
Case contract
Inputs prev and next coupon dates, settlement in [prev, next) and annual rate (semi-annual coupons). c = 100*rate, days = days(prev, settle). If days >= 183 accrued = c/2 - c*days(settle, next)/365, else accrued = c*days/365. Round to 6 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(prev, nxt, settle, rate):
P = datetime.date(*prev)
Q = datetime.date(*nxt)
S = datetime.date(*settle)
days = (S - P).days
c = 100 * rate
if days > 183:
acc = c / 2 - c * (Q - S).days / 365
else:
acc = c * days / 365
return round(acc, 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression complement threshold 1', [[2016, 8, 1], [2017, 2, 1], [2017, 1, 31], 0.0825], 4.102397], ['regression complement threshold 2', [[2022, 7, 31], [2023, 1, 31], [2023, 1, 30], 0.0825], 4.102397], ['partial repair probe 1', [[2015, 12, 7], [2016, 6, 7], [2016, 6, 6], 0.0125], 0.623288], ['partial repair probe 2', [[2034, 3, 30], [2034, 9, 30], [2034, 9, 28], 0.0375], 1.869863], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2035, 10, 31], [2036, 4, 30], [2036, 4, 29], 0.0125], 0.619863], ['normal control 2', [[2036, 10, 12], [2037, 4, 12], [2037, 4, 11], 0.025], 1.239726], ['normal control 3', [[2039, 11, 29], [2040, 5, 29], [2040, 5, 28], 0.0375], 1.859589]], [['regression complement threshold 1', [[2016, 5, 30], [2016, 11, 30], [2016, 11, 29], 0.0825], 4.102397], ['regression complement threshold 2', [[2033, 7, 30], [2034, 1, 30], [2034, 1, 29], 0.0375], 1.864726], ['partial repair probe 1', [[2015, 12, 7], [2016, 6, 7], [2016, 6, 6], 0.025], 1.246575], ['partial repair probe 2', [[2024, 5, 28], [2024, 11, 28], [2024, 11, 26], 0.05], 2.493151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2025, 9, 30], [2026, 3, 30], [2026, 3, 29], 0.0825], 4.068493], ['normal control 2', [[2027, 9, 6], [2028, 3, 6], [2028, 3, 5], 0.05], 2.479452], ['normal control 3', [[2039, 11, 1], [2040, 5, 1], [2040, 1, 22], 0.025], 0.561644]], [['regression complement threshold 1', [[2012, 5, 29], [2012, 11, 29], [2012, 11, 28], 0.025], 1.243151], ['regression complement threshold 2', [[2036, 7, 13], [2037, 1, 13], [2037, 1, 12], 0.025], 1.243151], ['partial repair probe 1', [[2025, 5, 14], [2025, 11, 14], [2025, 11, 12], 0.0125], 0.623288], ['partial repair probe 2', [[2011, 7, 30], [2012, 1, 30], [2012, 1, 28], 0.0375], 1.869863], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2023, 11, 28], [2024, 5, 28], [2024, 5, 27], 0.05], 2.479452], ['normal control 2', [[2032, 2, 27], [2032, 8, 27], [2032, 8, 13], 0.0825], 3.79726], ['normal control 3', [[2013, 5, 3], [2013, 11, 3], [2013, 6, 11], 0.0825], 0.881507]], [['regression complement threshold 1', [[2036, 7, 1], [2037, 1, 1], [2036, 12, 31], 0.0825], 4.102397], ['regression complement threshold 2', [[2037, 7, 27], [2038, 1, 27], [2038, 1, 26], 0.05], 2.486301], ['partial repair probe 1', [[2032, 5, 31], [2032, 11, 30], [2032, 11, 29], 0.0825], 4.113699], ['partial repair probe 2', [[2021, 5, 13], [2021, 11, 13], [2021, 11, 11], 0.05], 2.493151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2011, 5, 30], [2011, 11, 30], [2011, 6, 10], 0.05], 0.150685], ['normal control 2', [[2021, 1, 7], [2021, 7, 7], [2021, 7, 6], 0.025], 1.232877], ['normal control 3', [[2030, 2, 27], [2030, 8, 27], [2030, 8, 26], 0.0125], 0.616438]], [['regression complement threshold 1', [[2016, 3, 2], [2016, 9, 2], [2016, 9, 1], 0.0825], 4.102397], ['regression complement threshold 2', [[2017, 5, 30], [2017, 11, 30], [2017, 11, 29], 0.05], 2.486301], ['partial repair probe 1', [[2014, 6, 11], [2014, 12, 11], [2014, 12, 10], 0.0125], 0.623288], ['partial repair probe 2', [[2029, 3, 23], [2029, 9, 23], [2029, 9, 21], 0.0825], 4.113699], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2031, 11, 28], [2032, 5, 28], [2032, 5, 27], 0.0375], 1.859589], ['normal control 2', [[2018, 9, 9], [2019, 3, 9], [2019, 3, 6], 0.0125], 0.609589], ['normal control 3', [[2015, 2, 18], [2015, 8, 18], [2015, 8, 17], 0.025], 1.232877]]]
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 complement threshold 1 | 4.136301 | 4.102397 | Failed |
| regression complement threshold 2 | 4.136301 | 4.102397 | Failed |
| partial repair probe 1 | 0.623288 | 0.623288 | Passed |
| partial repair probe 2 | 1.869863 | 1.869863 | Passed |
| boundary control 1 | 0.0 | 0.0 | Passed |
| normal control 1 | 0.619863 | 0.619863 | Passed |
| normal control 2 | 1.239726 | 1.239726 | Passed |
| normal control 3 | 1.859589 | 1.859589 | Passed |
SHA-256 / 78f390c015e468d66feb7867992bae6472cdc1a7ae04b1977f91e4ddf364cbd3
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
N = 1
observations = []
def solve(prev, nxt, settle, rate):
P = datetime.date(*prev)
Q = datetime.date(*nxt)
S = datetime.date(*settle)
days = (S - P).days
c = 100 * rate
if days >= 182:
acc = c / 2 - c * (Q - S).days / 365
else:
acc = c * days / 365
return round(acc, 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression complement threshold 1', [[2016, 8, 1], [2017, 2, 1], [2017, 1, 31], 0.0825], 4.102397], ['regression complement threshold 2', [[2022, 7, 31], [2023, 1, 31], [2023, 1, 30], 0.0825], 4.102397], ['partial repair probe 1', [[2015, 12, 7], [2016, 6, 7], [2016, 6, 6], 0.0125], 0.623288], ['partial repair probe 2', [[2034, 3, 30], [2034, 9, 30], [2034, 9, 28], 0.0375], 1.869863], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2035, 10, 31], [2036, 4, 30], [2036, 4, 29], 0.0125], 0.619863], ['normal control 2', [[2036, 10, 12], [2037, 4, 12], [2037, 4, 11], 0.025], 1.239726], ['normal control 3', [[2039, 11, 29], [2040, 5, 29], [2040, 5, 28], 0.0375], 1.859589]], [['regression complement threshold 1', [[2016, 5, 30], [2016, 11, 30], [2016, 11, 29], 0.0825], 4.102397], ['regression complement threshold 2', [[2033, 7, 30], [2034, 1, 30], [2034, 1, 29], 0.0375], 1.864726], ['partial repair probe 1', [[2015, 12, 7], [2016, 6, 7], [2016, 6, 6], 0.025], 1.246575], ['partial repair probe 2', [[2024, 5, 28], [2024, 11, 28], [2024, 11, 26], 0.05], 2.493151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2025, 9, 30], [2026, 3, 30], [2026, 3, 29], 0.0825], 4.068493], ['normal control 2', [[2027, 9, 6], [2028, 3, 6], [2028, 3, 5], 0.05], 2.479452], ['normal control 3', [[2039, 11, 1], [2040, 5, 1], [2040, 1, 22], 0.025], 0.561644]], [['regression complement threshold 1', [[2012, 5, 29], [2012, 11, 29], [2012, 11, 28], 0.025], 1.243151], ['regression complement threshold 2', [[2036, 7, 13], [2037, 1, 13], [2037, 1, 12], 0.025], 1.243151], ['partial repair probe 1', [[2025, 5, 14], [2025, 11, 14], [2025, 11, 12], 0.0125], 0.623288], ['partial repair probe 2', [[2011, 7, 30], [2012, 1, 30], [2012, 1, 28], 0.0375], 1.869863], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2023, 11, 28], [2024, 5, 28], [2024, 5, 27], 0.05], 2.479452], ['normal control 2', [[2032, 2, 27], [2032, 8, 27], [2032, 8, 13], 0.0825], 3.79726], ['normal control 3', [[2013, 5, 3], [2013, 11, 3], [2013, 6, 11], 0.0825], 0.881507]], [['regression complement threshold 1', [[2036, 7, 1], [2037, 1, 1], [2036, 12, 31], 0.0825], 4.102397], ['regression complement threshold 2', [[2037, 7, 27], [2038, 1, 27], [2038, 1, 26], 0.05], 2.486301], ['partial repair probe 1', [[2032, 5, 31], [2032, 11, 30], [2032, 11, 29], 0.0825], 4.113699], ['partial repair probe 2', [[2021, 5, 13], [2021, 11, 13], [2021, 11, 11], 0.05], 2.493151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2011, 5, 30], [2011, 11, 30], [2011, 6, 10], 0.05], 0.150685], ['normal control 2', [[2021, 1, 7], [2021, 7, 7], [2021, 7, 6], 0.025], 1.232877], ['normal control 3', [[2030, 2, 27], [2030, 8, 27], [2030, 8, 26], 0.0125], 0.616438]], [['regression complement threshold 1', [[2016, 3, 2], [2016, 9, 2], [2016, 9, 1], 0.0825], 4.102397], ['regression complement threshold 2', [[2017, 5, 30], [2017, 11, 30], [2017, 11, 29], 0.05], 2.486301], ['partial repair probe 1', [[2014, 6, 11], [2014, 12, 11], [2014, 12, 10], 0.0125], 0.623288], ['partial repair probe 2', [[2029, 3, 23], [2029, 9, 23], [2029, 9, 21], 0.0825], 4.113699], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2031, 11, 28], [2032, 5, 28], [2032, 5, 27], 0.0375], 1.859589], ['normal control 2', [[2018, 9, 9], [2019, 3, 9], [2019, 3, 6], 0.0125], 0.609589], ['normal control 3', [[2015, 2, 18], [2015, 8, 18], [2015, 8, 17], 0.025], 1.232877]]]
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 complement threshold 1 | 4.102397 | 4.102397 | Passed |
| regression complement threshold 2 | 4.102397 | 4.102397 | Passed |
| partial repair probe 1 | 0.621575 | 0.623288 | Failed |
| partial repair probe 2 | 1.854452 | 1.869863 | Failed |
| boundary control 1 | 0.0 | 0.0 | Passed |
| normal control 1 | 0.619863 | 0.619863 | Passed |
| normal control 2 | 1.239726 | 1.239726 | Passed |
| normal control 3 | 1.859589 | 1.859589 | Passed |
SHA-256 / 40f8627fe841f8257813472a6fa8ed9a651eddd634fe2235098a0e398e44b712
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
N = 1
observations = []
def solve(prev, nxt, settle, rate):
P = datetime.date(*prev)
Q = datetime.date(*nxt)
S = datetime.date(*settle)
days = (S - P).days
c = 100 * rate
if days >= 183:
acc = c / 2 - c * (Q - S).days / 365
else:
acc = c * days / 365
return round(acc, 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression complement threshold 1', [[2016, 8, 1], [2017, 2, 1], [2017, 1, 31], 0.0825], 4.102397], ['regression complement threshold 2', [[2022, 7, 31], [2023, 1, 31], [2023, 1, 30], 0.0825], 4.102397], ['partial repair probe 1', [[2015, 12, 7], [2016, 6, 7], [2016, 6, 6], 0.0125], 0.623288], ['partial repair probe 2', [[2034, 3, 30], [2034, 9, 30], [2034, 9, 28], 0.0375], 1.869863], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2035, 10, 31], [2036, 4, 30], [2036, 4, 29], 0.0125], 0.619863], ['normal control 2', [[2036, 10, 12], [2037, 4, 12], [2037, 4, 11], 0.025], 1.239726], ['normal control 3', [[2039, 11, 29], [2040, 5, 29], [2040, 5, 28], 0.0375], 1.859589]], [['regression complement threshold 1', [[2016, 5, 30], [2016, 11, 30], [2016, 11, 29], 0.0825], 4.102397], ['regression complement threshold 2', [[2033, 7, 30], [2034, 1, 30], [2034, 1, 29], 0.0375], 1.864726], ['partial repair probe 1', [[2015, 12, 7], [2016, 6, 7], [2016, 6, 6], 0.025], 1.246575], ['partial repair probe 2', [[2024, 5, 28], [2024, 11, 28], [2024, 11, 26], 0.05], 2.493151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2025, 9, 30], [2026, 3, 30], [2026, 3, 29], 0.0825], 4.068493], ['normal control 2', [[2027, 9, 6], [2028, 3, 6], [2028, 3, 5], 0.05], 2.479452], ['normal control 3', [[2039, 11, 1], [2040, 5, 1], [2040, 1, 22], 0.025], 0.561644]], [['regression complement threshold 1', [[2012, 5, 29], [2012, 11, 29], [2012, 11, 28], 0.025], 1.243151], ['regression complement threshold 2', [[2036, 7, 13], [2037, 1, 13], [2037, 1, 12], 0.025], 1.243151], ['partial repair probe 1', [[2025, 5, 14], [2025, 11, 14], [2025, 11, 12], 0.0125], 0.623288], ['partial repair probe 2', [[2011, 7, 30], [2012, 1, 30], [2012, 1, 28], 0.0375], 1.869863], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2023, 11, 28], [2024, 5, 28], [2024, 5, 27], 0.05], 2.479452], ['normal control 2', [[2032, 2, 27], [2032, 8, 27], [2032, 8, 13], 0.0825], 3.79726], ['normal control 3', [[2013, 5, 3], [2013, 11, 3], [2013, 6, 11], 0.0825], 0.881507]], [['regression complement threshold 1', [[2036, 7, 1], [2037, 1, 1], [2036, 12, 31], 0.0825], 4.102397], ['regression complement threshold 2', [[2037, 7, 27], [2038, 1, 27], [2038, 1, 26], 0.05], 2.486301], ['partial repair probe 1', [[2032, 5, 31], [2032, 11, 30], [2032, 11, 29], 0.0825], 4.113699], ['partial repair probe 2', [[2021, 5, 13], [2021, 11, 13], [2021, 11, 11], 0.05], 2.493151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2011, 5, 30], [2011, 11, 30], [2011, 6, 10], 0.05], 0.150685], ['normal control 2', [[2021, 1, 7], [2021, 7, 7], [2021, 7, 6], 0.025], 1.232877], ['normal control 3', [[2030, 2, 27], [2030, 8, 27], [2030, 8, 26], 0.0125], 0.616438]], [['regression complement threshold 1', [[2016, 3, 2], [2016, 9, 2], [2016, 9, 1], 0.0825], 4.102397], ['regression complement threshold 2', [[2017, 5, 30], [2017, 11, 30], [2017, 11, 29], 0.05], 2.486301], ['partial repair probe 1', [[2014, 6, 11], [2014, 12, 11], [2014, 12, 10], 0.0125], 0.623288], ['partial repair probe 2', [[2029, 3, 23], [2029, 9, 23], [2029, 9, 21], 0.0825], 4.113699], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2031, 11, 28], [2032, 5, 28], [2032, 5, 27], 0.0375], 1.859589], ['normal control 2', [[2018, 9, 9], [2019, 3, 9], [2019, 3, 6], 0.0125], 0.609589], ['normal control 3', [[2015, 2, 18], [2015, 8, 18], [2015, 8, 17], 0.025], 1.232877]]]
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 complement threshold 1 | 4.102397 | 4.102397 | Passed |
| regression complement threshold 2 | 4.102397 | 4.102397 | Passed |
| partial repair probe 1 | 0.623288 | 0.623288 | Passed |
| partial repair probe 2 | 1.869863 | 1.869863 | Passed |
| boundary control 1 | 0.0 | 0.0 | Passed |
| normal control 1 | 0.619863 | 0.619863 | Passed |
| normal control 2 | 1.239726 | 1.239726 | Passed |
| normal control 3 | 1.859589 | 1.859589 | Passed |
SHA-256 / 8072e06f20147dcb76db4e3b228aca9276ffedbefd5210e6b12e0885218602a9
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:52.819287+00:00.
Case digest / 11a9deb2b20f1fcc4f96067a16c29040aac84041926d9a5dd3c1b960d808f7f3