FAILURE MAP
← Case archive

FA-61186 / Bond day-count conventions / Open access

Canadian-style semi-annual accrued interest: the late-period branch accrues forward like the early branch · case 01

Accrued interest near the end of a 184-day period exceeds the half-year coupon.

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

ROOT CAUSE

The late-period formula was written as c*days/365 instead of the complement from the coupon.

VERIFIED REPAIR

Late in the period, accrue as half coupon minus c*days-to-next/365.

Unsuccessful approach: Capping forward accrual at half a coupon still differs from the complement rule.

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 * 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 late-period complement 1', [[2028, 7, 12], [2029, 1, 12], [2029, 1, 11], 0.025], 1.243151], ['regression late-period complement 2', [[2023, 3, 7], [2023, 9, 7], [2023, 9, 6], 0.05], 2.486301], ['partial repair probe 1', [[2022, 5, 13], [2022, 11, 13], [2022, 11, 12], 0.025], 1.243151], ['partial repair probe 2', [[2025, 3, 28], [2025, 9, 28], [2025, 9, 27], 0.025], 1.243151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2030, 2, 20], [2030, 8, 20], [2030, 8, 19], 0.05], 2.465753], ['normal control 2', [[2038, 9, 30], [2039, 3, 30], [2039, 3, 29], 0.0825], 4.068493], ['normal control 3', [[2038, 8, 22], [2039, 2, 22], [2039, 2, 20], 0.0125], 0.623288]], [['regression late-period complement 1', [[2025, 3, 25], [2025, 9, 25], [2025, 9, 24], 0.05], 2.486301], ['regression late-period complement 2', [[2020, 3, 14], [2020, 9, 14], [2020, 9, 13], 0.0125], 0.621575], ['partial repair probe 1', [[2031, 5, 8], [2031, 11, 8], [2031, 11, 7], 0.05], 2.486301], ['partial repair probe 2', [[2015, 3, 30], [2015, 9, 30], [2015, 9, 29], 0.025], 1.243151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2038, 10, 4], [2039, 4, 4], [2039, 4, 3], 0.0825], 4.091096], ['normal control 2', [[2036, 2, 29], [2036, 8, 29], [2036, 8, 28], 0.05], 2.479452], ['normal control 3', [[2023, 9, 1], [2024, 3, 1], [2024, 2, 29], 0.0125], 0.619863]], [['regression late-period complement 1', [[2021, 7, 8], [2022, 1, 8], [2022, 1, 7], 0.0125], 0.621575], ['regression late-period complement 2', [[2029, 3, 24], [2029, 9, 24], [2029, 9, 23], 0.0125], 0.621575], ['partial repair probe 1', [[2016, 7, 25], [2017, 1, 25], [2017, 1, 24], 0.0125], 0.621575], ['partial repair probe 2', [[2027, 8, 27], [2028, 2, 27], [2028, 2, 26], 0.0375], 1.864726], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2028, 9, 17], [2029, 3, 17], [2029, 3, 16], 0.025], 1.232877], ['normal control 2', [[2026, 4, 16], [2026, 10, 16], [2026, 10, 15], 0.025], 1.246575], ['normal control 3', [[2019, 9, 30], [2020, 3, 30], [2019, 12, 1], 0.0375], 0.636986]], [['regression late-period complement 1', [[2017, 5, 1], [2017, 11, 1], [2017, 10, 31], 0.0125], 0.621575], ['regression late-period complement 2', [[2025, 5, 30], [2025, 11, 30], [2025, 11, 29], 0.0125], 0.621575], ['partial repair probe 1', [[2011, 3, 5], [2011, 9, 5], [2011, 9, 4], 0.0125], 0.621575], ['partial repair probe 2', [[2038, 7, 14], [2039, 1, 14], [2039, 1, 13], 0.025], 1.243151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2039, 4, 1], [2039, 10, 1], [2039, 9, 30], 0.05], 2.493151], ['normal control 2', [[2018, 4, 15], [2018, 10, 15], [2018, 10, 14], 0.0375], 1.869863], ['normal control 3', [[2023, 1, 31], [2023, 7, 31], [2023, 7, 29], 0.05], 2.452055]], [['regression late-period complement 1', [[2019, 5, 4], [2019, 11, 4], [2019, 11, 3], 0.0825], 4.102397], ['regression late-period complement 2', [[2028, 7, 19], [2029, 1, 19], [2029, 1, 18], 0.0125], 0.621575], ['partial repair probe 1', [[2025, 7, 30], [2026, 1, 30], [2026, 1, 29], 0.0125], 0.621575], ['partial repair probe 2', [[2021, 7, 13], [2022, 1, 13], [2022, 1, 12], 0.0825], 4.102397], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2021, 11, 24], [2022, 5, 24], [2022, 5, 23], 0.0375], 1.849315], ['normal control 2', [[2038, 2, 28], [2038, 8, 28], [2038, 5, 3], 0.0125], 0.219178], ['normal control 3', [[2017, 10, 22], [2018, 4, 22], [2018, 4, 21], 0.05], 2.479452]]]
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 late-period complement 11.2534251.243151Failed
regression late-period complement 22.5068492.486301Failed
partial repair probe 11.2534251.243151Failed
partial repair probe 21.2534251.243151Failed
boundary control 10.00.0Passed
normal control 12.4657532.465753Passed
normal control 24.0684934.068493Passed
normal control 30.6232880.623288Passed

SHA-256 / 7ee170f317e475da8402cd9d50061bf62824e0b4fe8fa9abe2a32f57740f2740

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 >= 183:
        acc = min(c / 2, c * 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 late-period complement 1', [[2028, 7, 12], [2029, 1, 12], [2029, 1, 11], 0.025], 1.243151], ['regression late-period complement 2', [[2023, 3, 7], [2023, 9, 7], [2023, 9, 6], 0.05], 2.486301], ['partial repair probe 1', [[2022, 5, 13], [2022, 11, 13], [2022, 11, 12], 0.025], 1.243151], ['partial repair probe 2', [[2025, 3, 28], [2025, 9, 28], [2025, 9, 27], 0.025], 1.243151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2030, 2, 20], [2030, 8, 20], [2030, 8, 19], 0.05], 2.465753], ['normal control 2', [[2038, 9, 30], [2039, 3, 30], [2039, 3, 29], 0.0825], 4.068493], ['normal control 3', [[2038, 8, 22], [2039, 2, 22], [2039, 2, 20], 0.0125], 0.623288]], [['regression late-period complement 1', [[2025, 3, 25], [2025, 9, 25], [2025, 9, 24], 0.05], 2.486301], ['regression late-period complement 2', [[2020, 3, 14], [2020, 9, 14], [2020, 9, 13], 0.0125], 0.621575], ['partial repair probe 1', [[2031, 5, 8], [2031, 11, 8], [2031, 11, 7], 0.05], 2.486301], ['partial repair probe 2', [[2015, 3, 30], [2015, 9, 30], [2015, 9, 29], 0.025], 1.243151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2038, 10, 4], [2039, 4, 4], [2039, 4, 3], 0.0825], 4.091096], ['normal control 2', [[2036, 2, 29], [2036, 8, 29], [2036, 8, 28], 0.05], 2.479452], ['normal control 3', [[2023, 9, 1], [2024, 3, 1], [2024, 2, 29], 0.0125], 0.619863]], [['regression late-period complement 1', [[2021, 7, 8], [2022, 1, 8], [2022, 1, 7], 0.0125], 0.621575], ['regression late-period complement 2', [[2029, 3, 24], [2029, 9, 24], [2029, 9, 23], 0.0125], 0.621575], ['partial repair probe 1', [[2016, 7, 25], [2017, 1, 25], [2017, 1, 24], 0.0125], 0.621575], ['partial repair probe 2', [[2027, 8, 27], [2028, 2, 27], [2028, 2, 26], 0.0375], 1.864726], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2028, 9, 17], [2029, 3, 17], [2029, 3, 16], 0.025], 1.232877], ['normal control 2', [[2026, 4, 16], [2026, 10, 16], [2026, 10, 15], 0.025], 1.246575], ['normal control 3', [[2019, 9, 30], [2020, 3, 30], [2019, 12, 1], 0.0375], 0.636986]], [['regression late-period complement 1', [[2017, 5, 1], [2017, 11, 1], [2017, 10, 31], 0.0125], 0.621575], ['regression late-period complement 2', [[2025, 5, 30], [2025, 11, 30], [2025, 11, 29], 0.0125], 0.621575], ['partial repair probe 1', [[2011, 3, 5], [2011, 9, 5], [2011, 9, 4], 0.0125], 0.621575], ['partial repair probe 2', [[2038, 7, 14], [2039, 1, 14], [2039, 1, 13], 0.025], 1.243151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2039, 4, 1], [2039, 10, 1], [2039, 9, 30], 0.05], 2.493151], ['normal control 2', [[2018, 4, 15], [2018, 10, 15], [2018, 10, 14], 0.0375], 1.869863], ['normal control 3', [[2023, 1, 31], [2023, 7, 31], [2023, 7, 29], 0.05], 2.452055]], [['regression late-period complement 1', [[2019, 5, 4], [2019, 11, 4], [2019, 11, 3], 0.0825], 4.102397], ['regression late-period complement 2', [[2028, 7, 19], [2029, 1, 19], [2029, 1, 18], 0.0125], 0.621575], ['partial repair probe 1', [[2025, 7, 30], [2026, 1, 30], [2026, 1, 29], 0.0125], 0.621575], ['partial repair probe 2', [[2021, 7, 13], [2022, 1, 13], [2022, 1, 12], 0.0825], 4.102397], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2021, 11, 24], [2022, 5, 24], [2022, 5, 23], 0.0375], 1.849315], ['normal control 2', [[2038, 2, 28], [2038, 8, 28], [2038, 5, 3], 0.0125], 0.219178], ['normal control 3', [[2017, 10, 22], [2018, 4, 22], [2018, 4, 21], 0.05], 2.479452]]]
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 late-period complement 11.251.243151Failed
regression late-period complement 22.52.486301Failed
partial repair probe 11.251.243151Failed
partial repair probe 21.251.243151Failed
boundary control 10.00.0Passed
normal control 12.4657532.465753Passed
normal control 24.0684934.068493Passed
normal control 30.6232880.623288Passed

SHA-256 / 8c54a214c26911bc7aee912bed4680c7ce406195c358fca4a07bbeac18b16927

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 late-period complement 1', [[2028, 7, 12], [2029, 1, 12], [2029, 1, 11], 0.025], 1.243151], ['regression late-period complement 2', [[2023, 3, 7], [2023, 9, 7], [2023, 9, 6], 0.05], 2.486301], ['partial repair probe 1', [[2022, 5, 13], [2022, 11, 13], [2022, 11, 12], 0.025], 1.243151], ['partial repair probe 2', [[2025, 3, 28], [2025, 9, 28], [2025, 9, 27], 0.025], 1.243151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2030, 2, 20], [2030, 8, 20], [2030, 8, 19], 0.05], 2.465753], ['normal control 2', [[2038, 9, 30], [2039, 3, 30], [2039, 3, 29], 0.0825], 4.068493], ['normal control 3', [[2038, 8, 22], [2039, 2, 22], [2039, 2, 20], 0.0125], 0.623288]], [['regression late-period complement 1', [[2025, 3, 25], [2025, 9, 25], [2025, 9, 24], 0.05], 2.486301], ['regression late-period complement 2', [[2020, 3, 14], [2020, 9, 14], [2020, 9, 13], 0.0125], 0.621575], ['partial repair probe 1', [[2031, 5, 8], [2031, 11, 8], [2031, 11, 7], 0.05], 2.486301], ['partial repair probe 2', [[2015, 3, 30], [2015, 9, 30], [2015, 9, 29], 0.025], 1.243151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2038, 10, 4], [2039, 4, 4], [2039, 4, 3], 0.0825], 4.091096], ['normal control 2', [[2036, 2, 29], [2036, 8, 29], [2036, 8, 28], 0.05], 2.479452], ['normal control 3', [[2023, 9, 1], [2024, 3, 1], [2024, 2, 29], 0.0125], 0.619863]], [['regression late-period complement 1', [[2021, 7, 8], [2022, 1, 8], [2022, 1, 7], 0.0125], 0.621575], ['regression late-period complement 2', [[2029, 3, 24], [2029, 9, 24], [2029, 9, 23], 0.0125], 0.621575], ['partial repair probe 1', [[2016, 7, 25], [2017, 1, 25], [2017, 1, 24], 0.0125], 0.621575], ['partial repair probe 2', [[2027, 8, 27], [2028, 2, 27], [2028, 2, 26], 0.0375], 1.864726], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2028, 9, 17], [2029, 3, 17], [2029, 3, 16], 0.025], 1.232877], ['normal control 2', [[2026, 4, 16], [2026, 10, 16], [2026, 10, 15], 0.025], 1.246575], ['normal control 3', [[2019, 9, 30], [2020, 3, 30], [2019, 12, 1], 0.0375], 0.636986]], [['regression late-period complement 1', [[2017, 5, 1], [2017, 11, 1], [2017, 10, 31], 0.0125], 0.621575], ['regression late-period complement 2', [[2025, 5, 30], [2025, 11, 30], [2025, 11, 29], 0.0125], 0.621575], ['partial repair probe 1', [[2011, 3, 5], [2011, 9, 5], [2011, 9, 4], 0.0125], 0.621575], ['partial repair probe 2', [[2038, 7, 14], [2039, 1, 14], [2039, 1, 13], 0.025], 1.243151], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2039, 4, 1], [2039, 10, 1], [2039, 9, 30], 0.05], 2.493151], ['normal control 2', [[2018, 4, 15], [2018, 10, 15], [2018, 10, 14], 0.0375], 1.869863], ['normal control 3', [[2023, 1, 31], [2023, 7, 31], [2023, 7, 29], 0.05], 2.452055]], [['regression late-period complement 1', [[2019, 5, 4], [2019, 11, 4], [2019, 11, 3], 0.0825], 4.102397], ['regression late-period complement 2', [[2028, 7, 19], [2029, 1, 19], [2029, 1, 18], 0.0125], 0.621575], ['partial repair probe 1', [[2025, 7, 30], [2026, 1, 30], [2026, 1, 29], 0.0125], 0.621575], ['partial repair probe 2', [[2021, 7, 13], [2022, 1, 13], [2022, 1, 12], 0.0825], 4.102397], ['boundary control 1', [[2024, 1, 1], [2024, 7, 1], [2024, 1, 1], 0.05], 0.0], ['normal control 1', [[2021, 11, 24], [2022, 5, 24], [2022, 5, 23], 0.0375], 1.849315], ['normal control 2', [[2038, 2, 28], [2038, 8, 28], [2038, 5, 3], 0.0125], 0.219178], ['normal control 3', [[2017, 10, 22], [2018, 4, 22], [2018, 4, 21], 0.05], 2.479452]]]
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 late-period complement 11.2431511.243151Passed
regression late-period complement 22.4863012.486301Passed
partial repair probe 11.2431511.243151Passed
partial repair probe 21.2431511.243151Passed
boundary control 10.00.0Passed
normal control 12.4657532.465753Passed
normal control 24.0684934.068493Passed
normal control 30.6232880.623288Passed

SHA-256 / 4b3e8e859f160c9b82c9ac2abf6fe3a9c29c0c3ded497532b53f5b5a93271533

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.814284+00:00.

Case digest / 1e4bcb88e1219636dddbe55e59a54db094f0b55c0877809a151c721ca2b76e9e