FAILURE MAP
← Case archive

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

ICMA regular-period accrued with ex-coupon: the accrual denominator is a nominal fraction of 365 days · case 01

Accrued interest drifts from the ICMA value for periods that are not exactly 365/freq days.

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

ROOT CAUSE

The period length is taken as 365 divided by frequency rather than the actual days between coupons.

VERIFIED REPAIR

Use the actual number of days from the previous to the next coupon date.

Unsuccessful approach: Switching to 366/freq in leap years still ignores the actual period length.

Case contract

Inputs prev, nxt, settle ([y,m,d]), annual rate, frequency and ex-coupon days. Settlement outside [prev, nxt] returns "settlement outside period"; settlement on nxt returns 0.0. If the settlement is within ex-coupon days of nxt (days to nxt <= exdays), accrued is negative: -coupon*days(settle,nxt)/days(prev,nxt). Otherwise accrued = coupon*days(prev,settle)/days(prev,nxt). coupon=100*rate/freq; round the final value 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
from fractions import Fraction
N = 1
observations = []
def solve(prev, nxt, settle, rate, freq, exdays):
    P = datetime.date(*prev)
    Q = datetime.date(*nxt)
    S = datetime.date(*settle)
    if not (P <= S <= Q):
        return 'settlement outside period'
    if S == Q:
        return 0.0
    period = 365 / freq
    coupon = 100 * rate / freq
    if (Q - S).days <= exdays:
        return round(-coupon * (Q - S).days / period, 6)
    days = (S - P).days
    return round(coupon * days / period, 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression period denominator 1', [[2036, 2, 15], [2036, 8, 15], [2036, 8, 11], 0.0275, 2, 3], 1.34478], ['regression period denominator 2', [[2048, 7, 1], [2048, 8, 1], [2048, 7, 26], 0.05, 12, 0], 0.336022], ['partial repair probe 1', [[2012, 8, 30], [2013, 8, 30], [2013, 8, 23], 0.04125, 1, 7], -0.07911], ['partial repair probe 2', [[2004, 5, 31], [2005, 5, 31], [2004, 6, 3], 0.0275, 1, 3], 0.022603], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2045, 1, 31], [2045, 2, 28], [2045, 2, 28], 0.04125, 12, 10], 0.0], ['normal control 2', [[2041, 2, 1], [2041, 8, 1], [2041, 2, 1], 0.05, 2, 0], 0.0]], [['regression period denominator 1', [[2035, 4, 29], [2036, 4, 29], [2035, 8, 11], 0.02, 1, 7], 0.568306], ['regression period denominator 2', [[2011, 3, 31], [2012, 3, 31], [2011, 6, 26], 0.035, 1, 7], 0.831967], ['partial repair probe 1', [[2060, 7, 14], [2061, 7, 14], [2061, 7, 8], 0.04125, 1, 0], 4.057192], ['partial repair probe 2', [[2044, 3, 7], [2045, 3, 7], [2045, 2, 25], 0.04125, 1, 0], 4.011986], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2037, 7, 30], [2038, 7, 30], [2037, 10, 8], 0.0125, 1, 5], 0.239726], ['normal control 2', [[2016, 3, 13], [2016, 4, 13], [2016, 3, 13], 0.035, 12, 0], 0.0]], [['regression period denominator 1', [[2054, 4, 30], [2054, 7, 30], [2054, 7, 20], 0.04125, 4, 0], 0.917926], ['regression period denominator 2', [[2006, 11, 7], [2006, 12, 7], [2006, 12, 5], 0.0275, 12, 3], -0.015278], ['partial repair probe 1', [[2048, 8, 20], [2049, 8, 20], [2048, 8, 29], 0.02, 1, 10], 0.049315], ['partial repair probe 2', [[2036, 12, 24], [2037, 12, 24], [2037, 4, 22], 0.07, 1, 7], 2.282192], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2030, 12, 17], [2031, 12, 17], [2031, 12, 9], 0.05, 1, 0], 4.890411], ['normal control 2', [[2054, 12, 24], [2055, 1, 24], [2054, 12, 24], 0.0125, 12, 0], 0.0]], [['regression period denominator 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 8], 0.05, 2, 7], -0.096154], ['regression period denominator 2', [[2047, 11, 28], [2047, 12, 28], [2047, 12, 12], 0.0125, 12, 5], 0.048611], ['partial repair probe 1', [[2028, 6, 30], [2029, 6, 30], [2029, 6, 26], 0.05, 1, 7], -0.054795], ['partial repair probe 2', [[2000, 7, 1], [2001, 7, 1], [2001, 1, 25], 0.0125, 1, 10], 0.712329], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['normal control 1', [[2050, 4, 30], [2051, 4, 30], [2050, 5, 10], 0.0125, 1, 7], 0.034247], ['normal control 2', [[2033, 5, 12], [2033, 6, 12], [2033, 5, 12], 0.05, 12, 7], 0.0]], [['regression period denominator 1', [[2025, 5, 28], [2025, 8, 28], [2025, 7, 18], 0.02, 4, 7], 0.277174], ['regression period denominator 2', [[2009, 12, 30], [2010, 6, 30], [2010, 6, 24], 0.04125, 2, 0], 1.994505], ['partial repair probe 1', [[2016, 10, 10], [2017, 10, 10], [2017, 10, 1], 0.0625, 1, 7], 6.09589], ['partial repair probe 2', [[2028, 4, 24], [2029, 4, 24], [2028, 7, 14], 0.04125, 1, 5], 0.915411], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2049, 11, 30], [2050, 11, 30], [2050, 11, 30], 0.0625, 1, 0], 0.0], ['normal control 2', [[2022, 8, 18], [2023, 8, 18], [2023, 8, 11], 0.04125, 1, 10], -0.07911]]]
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 period denominator 11.3410961.34478Failed
regression period denominator 20.3424660.336022Failed
partial repair probe 1-0.07911-0.07911Passed
partial repair probe 20.0226030.022603Passed
boundary control 1settlement outside periodsettlement outside periodPassed
boundary control 20.00.0Passed
normal control 10.00.0Passed
normal control 20.00.0Passed

SHA-256 / ebc28121b2521ff1062eeb3cb24c8d5ea183c7ea1077ab44b7ee5745e4453505

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
from fractions import Fraction
N = 1
observations = []
def solve(prev, nxt, settle, rate, freq, exdays):
    P = datetime.date(*prev)
    Q = datetime.date(*nxt)
    S = datetime.date(*settle)
    if not (P <= S <= Q):
        return 'settlement outside period'
    if S == Q:
        return 0.0
    period = (366 if P.year % 4 == 0 else 365) / freq
    coupon = 100 * rate / freq
    if (Q - S).days <= exdays:
        return round(-coupon * (Q - S).days / period, 6)
    days = (S - P).days
    return round(coupon * days / period, 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression period denominator 1', [[2036, 2, 15], [2036, 8, 15], [2036, 8, 11], 0.0275, 2, 3], 1.34478], ['regression period denominator 2', [[2048, 7, 1], [2048, 8, 1], [2048, 7, 26], 0.05, 12, 0], 0.336022], ['partial repair probe 1', [[2012, 8, 30], [2013, 8, 30], [2013, 8, 23], 0.04125, 1, 7], -0.07911], ['partial repair probe 2', [[2004, 5, 31], [2005, 5, 31], [2004, 6, 3], 0.0275, 1, 3], 0.022603], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2045, 1, 31], [2045, 2, 28], [2045, 2, 28], 0.04125, 12, 10], 0.0], ['normal control 2', [[2041, 2, 1], [2041, 8, 1], [2041, 2, 1], 0.05, 2, 0], 0.0]], [['regression period denominator 1', [[2035, 4, 29], [2036, 4, 29], [2035, 8, 11], 0.02, 1, 7], 0.568306], ['regression period denominator 2', [[2011, 3, 31], [2012, 3, 31], [2011, 6, 26], 0.035, 1, 7], 0.831967], ['partial repair probe 1', [[2060, 7, 14], [2061, 7, 14], [2061, 7, 8], 0.04125, 1, 0], 4.057192], ['partial repair probe 2', [[2044, 3, 7], [2045, 3, 7], [2045, 2, 25], 0.04125, 1, 0], 4.011986], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2037, 7, 30], [2038, 7, 30], [2037, 10, 8], 0.0125, 1, 5], 0.239726], ['normal control 2', [[2016, 3, 13], [2016, 4, 13], [2016, 3, 13], 0.035, 12, 0], 0.0]], [['regression period denominator 1', [[2054, 4, 30], [2054, 7, 30], [2054, 7, 20], 0.04125, 4, 0], 0.917926], ['regression period denominator 2', [[2006, 11, 7], [2006, 12, 7], [2006, 12, 5], 0.0275, 12, 3], -0.015278], ['partial repair probe 1', [[2048, 8, 20], [2049, 8, 20], [2048, 8, 29], 0.02, 1, 10], 0.049315], ['partial repair probe 2', [[2036, 12, 24], [2037, 12, 24], [2037, 4, 22], 0.07, 1, 7], 2.282192], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2030, 12, 17], [2031, 12, 17], [2031, 12, 9], 0.05, 1, 0], 4.890411], ['normal control 2', [[2054, 12, 24], [2055, 1, 24], [2054, 12, 24], 0.0125, 12, 0], 0.0]], [['regression period denominator 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 8], 0.05, 2, 7], -0.096154], ['regression period denominator 2', [[2047, 11, 28], [2047, 12, 28], [2047, 12, 12], 0.0125, 12, 5], 0.048611], ['partial repair probe 1', [[2028, 6, 30], [2029, 6, 30], [2029, 6, 26], 0.05, 1, 7], -0.054795], ['partial repair probe 2', [[2000, 7, 1], [2001, 7, 1], [2001, 1, 25], 0.0125, 1, 10], 0.712329], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['normal control 1', [[2050, 4, 30], [2051, 4, 30], [2050, 5, 10], 0.0125, 1, 7], 0.034247], ['normal control 2', [[2033, 5, 12], [2033, 6, 12], [2033, 5, 12], 0.05, 12, 7], 0.0]], [['regression period denominator 1', [[2025, 5, 28], [2025, 8, 28], [2025, 7, 18], 0.02, 4, 7], 0.277174], ['regression period denominator 2', [[2009, 12, 30], [2010, 6, 30], [2010, 6, 24], 0.04125, 2, 0], 1.994505], ['partial repair probe 1', [[2016, 10, 10], [2017, 10, 10], [2017, 10, 1], 0.0625, 1, 7], 6.09589], ['partial repair probe 2', [[2028, 4, 24], [2029, 4, 24], [2028, 7, 14], 0.04125, 1, 5], 0.915411], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2049, 11, 30], [2050, 11, 30], [2050, 11, 30], 0.0625, 1, 0], 0.0], ['normal control 2', [[2022, 8, 18], [2023, 8, 18], [2023, 8, 11], 0.04125, 1, 10], -0.07911]]]
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 period denominator 11.3374321.34478Failed
regression period denominator 20.341530.336022Failed
partial repair probe 1-0.078893-0.07911Failed
partial repair probe 20.0225410.022603Failed
boundary control 1settlement outside periodsettlement outside periodPassed
boundary control 20.00.0Passed
normal control 10.00.0Passed
normal control 20.00.0Passed

SHA-256 / 561e8dea70906fc6ae80bfc991e5dbf0b1fcba7c809f43979cfe6850e49da2a9

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
from fractions import Fraction
N = 1
observations = []
def solve(prev, nxt, settle, rate, freq, exdays):
    P = datetime.date(*prev)
    Q = datetime.date(*nxt)
    S = datetime.date(*settle)
    if not (P <= S <= Q):
        return 'settlement outside period'
    if S == Q:
        return 0.0
    period = (Q - P).days
    coupon = 100 * rate / freq
    if (Q - S).days <= exdays:
        return round(-coupon * (Q - S).days / period, 6)
    days = (S - P).days
    return round(coupon * days / period, 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression period denominator 1', [[2036, 2, 15], [2036, 8, 15], [2036, 8, 11], 0.0275, 2, 3], 1.34478], ['regression period denominator 2', [[2048, 7, 1], [2048, 8, 1], [2048, 7, 26], 0.05, 12, 0], 0.336022], ['partial repair probe 1', [[2012, 8, 30], [2013, 8, 30], [2013, 8, 23], 0.04125, 1, 7], -0.07911], ['partial repair probe 2', [[2004, 5, 31], [2005, 5, 31], [2004, 6, 3], 0.0275, 1, 3], 0.022603], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2045, 1, 31], [2045, 2, 28], [2045, 2, 28], 0.04125, 12, 10], 0.0], ['normal control 2', [[2041, 2, 1], [2041, 8, 1], [2041, 2, 1], 0.05, 2, 0], 0.0]], [['regression period denominator 1', [[2035, 4, 29], [2036, 4, 29], [2035, 8, 11], 0.02, 1, 7], 0.568306], ['regression period denominator 2', [[2011, 3, 31], [2012, 3, 31], [2011, 6, 26], 0.035, 1, 7], 0.831967], ['partial repair probe 1', [[2060, 7, 14], [2061, 7, 14], [2061, 7, 8], 0.04125, 1, 0], 4.057192], ['partial repair probe 2', [[2044, 3, 7], [2045, 3, 7], [2045, 2, 25], 0.04125, 1, 0], 4.011986], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2037, 7, 30], [2038, 7, 30], [2037, 10, 8], 0.0125, 1, 5], 0.239726], ['normal control 2', [[2016, 3, 13], [2016, 4, 13], [2016, 3, 13], 0.035, 12, 0], 0.0]], [['regression period denominator 1', [[2054, 4, 30], [2054, 7, 30], [2054, 7, 20], 0.04125, 4, 0], 0.917926], ['regression period denominator 2', [[2006, 11, 7], [2006, 12, 7], [2006, 12, 5], 0.0275, 12, 3], -0.015278], ['partial repair probe 1', [[2048, 8, 20], [2049, 8, 20], [2048, 8, 29], 0.02, 1, 10], 0.049315], ['partial repair probe 2', [[2036, 12, 24], [2037, 12, 24], [2037, 4, 22], 0.07, 1, 7], 2.282192], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2030, 12, 17], [2031, 12, 17], [2031, 12, 9], 0.05, 1, 0], 4.890411], ['normal control 2', [[2054, 12, 24], [2055, 1, 24], [2054, 12, 24], 0.0125, 12, 0], 0.0]], [['regression period denominator 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 8], 0.05, 2, 7], -0.096154], ['regression period denominator 2', [[2047, 11, 28], [2047, 12, 28], [2047, 12, 12], 0.0125, 12, 5], 0.048611], ['partial repair probe 1', [[2028, 6, 30], [2029, 6, 30], [2029, 6, 26], 0.05, 1, 7], -0.054795], ['partial repair probe 2', [[2000, 7, 1], [2001, 7, 1], [2001, 1, 25], 0.0125, 1, 10], 0.712329], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['normal control 1', [[2050, 4, 30], [2051, 4, 30], [2050, 5, 10], 0.0125, 1, 7], 0.034247], ['normal control 2', [[2033, 5, 12], [2033, 6, 12], [2033, 5, 12], 0.05, 12, 7], 0.0]], [['regression period denominator 1', [[2025, 5, 28], [2025, 8, 28], [2025, 7, 18], 0.02, 4, 7], 0.277174], ['regression period denominator 2', [[2009, 12, 30], [2010, 6, 30], [2010, 6, 24], 0.04125, 2, 0], 1.994505], ['partial repair probe 1', [[2016, 10, 10], [2017, 10, 10], [2017, 10, 1], 0.0625, 1, 7], 6.09589], ['partial repair probe 2', [[2028, 4, 24], [2029, 4, 24], [2028, 7, 14], 0.04125, 1, 5], 0.915411], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 16], 0.05, 2, 7], 'settlement outside period'], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2049, 11, 30], [2050, 11, 30], [2050, 11, 30], 0.0625, 1, 0], 0.0], ['normal control 2', [[2022, 8, 18], [2023, 8, 18], [2023, 8, 11], 0.04125, 1, 10], -0.07911]]]
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 period denominator 11.344781.34478Passed
regression period denominator 20.3360220.336022Passed
partial repair probe 1-0.07911-0.07911Passed
partial repair probe 20.0226030.022603Passed
boundary control 1settlement outside periodsettlement outside periodPassed
boundary control 20.00.0Passed
normal control 10.00.0Passed
normal control 20.00.0Passed

SHA-256 / 666f86f835ba81b5b27cbfb8c0b97bb18b8db8ee770ec61abffa4a97c0657b7d

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

Case digest / 3c6a189d1fe94167b54940adc8fcfce90f21ee294c54fd05e729a07b7373836f