FAILURE MAP
← Case archive

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

ICMA regular-period accrued with ex-coupon: ex-coupon trades report positive accrued interest · case 01

Buyers in the ex-coupon window are charged interest they will never receive.

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

ROOT CAUSE

The ex-coupon branch returns the rebate magnitude without the negative sign.

VERIFIED REPAIR

Return the negative of coupon times days to the next coupon over the period length.

Unsuccessful approach: Negating but measuring days from the previous coupon produces the wrong rebate size.

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 = (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 ex-coupon rebate sign 1', [[2031, 2, 28], [2031, 3, 28], [2031, 3, 22], 0.0625, 12, 7], -0.111607], ['regression ex-coupon rebate sign 2', [[2033, 7, 30], [2034, 7, 30], [2034, 7, 25], 0.035, 1, 10], -0.047945], ['partial repair probe 1', [[2008, 5, 27], [2008, 8, 27], [2008, 8, 20], 0.0125, 4, 7], -0.023777], ['partial repair probe 2', [[2059, 1, 29], [2059, 7, 29], [2059, 7, 24], 0.05, 2, 7], -0.069061], ['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, 7], 0.05, 2, 7], 2.39011], ['normal control 1', [[2008, 8, 31], [2008, 9, 30], [2008, 9, 30], 0.035, 12, 0], 0.0], ['normal control 2', [[2052, 10, 28], [2053, 4, 28], [2052, 10, 28], 0.0275, 2, 3], 0.0]], [['regression ex-coupon rebate sign 1', [[2050, 8, 31], [2050, 11, 30], [2050, 11, 25], 0.05, 4, 5], -0.068681], ['regression ex-coupon rebate sign 2', [[2036, 10, 18], [2037, 4, 18], [2037, 4, 15], 0.0625, 2, 10], -0.051511], ['partial repair probe 1', [[2029, 9, 30], [2029, 12, 30], [2029, 12, 26], 0.07, 4, 7], -0.076923], ['partial repair probe 2', [[2038, 9, 17], [2038, 10, 17], [2038, 10, 8], 0.04125, 12, 10], -0.103125], ['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, 7], 0.05, 2, 7], 2.39011], ['normal control 1', [[2024, 7, 28], [2025, 1, 28], [2024, 12, 17], 0.07, 2, 3], 2.701087], ['normal control 2', [[2015, 5, 1], [2015, 8, 1], [2015, 7, 17], 0.04125, 4, 0], 0.863111]], [['regression ex-coupon rebate sign 1', [[2029, 1, 29], [2029, 2, 28], [2029, 2, 26], 0.035, 12, 10], -0.019444], ['regression ex-coupon rebate sign 2', [[2047, 8, 29], [2047, 11, 29], [2047, 11, 27], 0.035, 4, 7], -0.019022], ['partial repair probe 1', [[2032, 8, 8], [2032, 9, 8], [2032, 9, 7], 0.0125, 12, 10], -0.00336], ['partial repair probe 2', [[2013, 4, 27], [2013, 10, 27], [2013, 10, 26], 0.04125, 2, 3], -0.01127], ['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', [[2028, 1, 31], [2028, 2, 29], [2028, 1, 31], 0.0125, 12, 7], 0.0], ['normal control 2', [[2044, 7, 23], [2044, 10, 23], [2044, 9, 12], 0.04125, 4, 0], 0.571671]], [['regression ex-coupon rebate sign 1', [[2042, 8, 31], [2043, 2, 28], [2043, 2, 25], 0.0125, 2, 10], -0.010359], ['regression ex-coupon rebate sign 2', [[2022, 7, 31], [2022, 10, 31], [2022, 10, 25], 0.04125, 4, 7], -0.067255], ['partial repair probe 1', [[2012, 4, 27], [2012, 7, 27], [2012, 7, 23], 0.0275, 4, 5], -0.03022], ['partial repair probe 2', [[2032, 10, 31], [2032, 11, 30], [2032, 11, 24], 0.05, 12, 10], -0.083333], ['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', [[2003, 4, 12], [2003, 10, 12], [2003, 10, 7], 0.0275, 2, 0], 1.337432], ['normal control 2', [[2056, 10, 21], [2056, 11, 21], [2056, 11, 12], 0.04125, 12, 7], 0.243952]], [['regression ex-coupon rebate sign 1', [[2060, 1, 31], [2060, 2, 29], [2060, 2, 22], 0.0625, 12, 10], -0.125718], ['regression ex-coupon rebate sign 2', [[2016, 11, 2], [2017, 2, 2], [2017, 1, 29], 0.05, 4, 5], -0.054348], ['partial repair probe 1', [[2052, 11, 21], [2052, 12, 21], [2052, 12, 14], 0.04125, 12, 10], -0.080208], ['partial repair probe 2', [[2020, 1, 31], [2020, 4, 30], [2020, 4, 29], 0.05, 4, 7], -0.013889], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 7], 0.05, 2, 7], 2.39011], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2036, 10, 10], [2037, 4, 10], [2037, 3, 30], 0.035, 2, 10], 1.644231], ['normal control 2', [[2044, 2, 25], [2044, 5, 25], [2044, 5, 16], 0.0125, 4, 0], 0.28125]]]
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 ex-coupon rebate sign 10.111607-0.111607Failed
regression ex-coupon rebate sign 20.047945-0.047945Failed
partial repair probe 10.023777-0.023777Failed
partial repair probe 20.069061-0.069061Failed
boundary control 1settlement outside periodsettlement outside periodPassed
boundary control 22.390112.39011Passed
normal control 10.00.0Passed
normal control 20.00.0Passed

SHA-256 / 517271e1ff2470c72fe0494a3178c634dfe9df34fe673277ed056685b27bb245

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 = (Q - P).days
    coupon = 100 * rate / freq
    if (Q - S).days <= exdays:
        return round(-coupon * (S - P).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 ex-coupon rebate sign 1', [[2031, 2, 28], [2031, 3, 28], [2031, 3, 22], 0.0625, 12, 7], -0.111607], ['regression ex-coupon rebate sign 2', [[2033, 7, 30], [2034, 7, 30], [2034, 7, 25], 0.035, 1, 10], -0.047945], ['partial repair probe 1', [[2008, 5, 27], [2008, 8, 27], [2008, 8, 20], 0.0125, 4, 7], -0.023777], ['partial repair probe 2', [[2059, 1, 29], [2059, 7, 29], [2059, 7, 24], 0.05, 2, 7], -0.069061], ['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, 7], 0.05, 2, 7], 2.39011], ['normal control 1', [[2008, 8, 31], [2008, 9, 30], [2008, 9, 30], 0.035, 12, 0], 0.0], ['normal control 2', [[2052, 10, 28], [2053, 4, 28], [2052, 10, 28], 0.0275, 2, 3], 0.0]], [['regression ex-coupon rebate sign 1', [[2050, 8, 31], [2050, 11, 30], [2050, 11, 25], 0.05, 4, 5], -0.068681], ['regression ex-coupon rebate sign 2', [[2036, 10, 18], [2037, 4, 18], [2037, 4, 15], 0.0625, 2, 10], -0.051511], ['partial repair probe 1', [[2029, 9, 30], [2029, 12, 30], [2029, 12, 26], 0.07, 4, 7], -0.076923], ['partial repair probe 2', [[2038, 9, 17], [2038, 10, 17], [2038, 10, 8], 0.04125, 12, 10], -0.103125], ['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, 7], 0.05, 2, 7], 2.39011], ['normal control 1', [[2024, 7, 28], [2025, 1, 28], [2024, 12, 17], 0.07, 2, 3], 2.701087], ['normal control 2', [[2015, 5, 1], [2015, 8, 1], [2015, 7, 17], 0.04125, 4, 0], 0.863111]], [['regression ex-coupon rebate sign 1', [[2029, 1, 29], [2029, 2, 28], [2029, 2, 26], 0.035, 12, 10], -0.019444], ['regression ex-coupon rebate sign 2', [[2047, 8, 29], [2047, 11, 29], [2047, 11, 27], 0.035, 4, 7], -0.019022], ['partial repair probe 1', [[2032, 8, 8], [2032, 9, 8], [2032, 9, 7], 0.0125, 12, 10], -0.00336], ['partial repair probe 2', [[2013, 4, 27], [2013, 10, 27], [2013, 10, 26], 0.04125, 2, 3], -0.01127], ['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', [[2028, 1, 31], [2028, 2, 29], [2028, 1, 31], 0.0125, 12, 7], 0.0], ['normal control 2', [[2044, 7, 23], [2044, 10, 23], [2044, 9, 12], 0.04125, 4, 0], 0.571671]], [['regression ex-coupon rebate sign 1', [[2042, 8, 31], [2043, 2, 28], [2043, 2, 25], 0.0125, 2, 10], -0.010359], ['regression ex-coupon rebate sign 2', [[2022, 7, 31], [2022, 10, 31], [2022, 10, 25], 0.04125, 4, 7], -0.067255], ['partial repair probe 1', [[2012, 4, 27], [2012, 7, 27], [2012, 7, 23], 0.0275, 4, 5], -0.03022], ['partial repair probe 2', [[2032, 10, 31], [2032, 11, 30], [2032, 11, 24], 0.05, 12, 10], -0.083333], ['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', [[2003, 4, 12], [2003, 10, 12], [2003, 10, 7], 0.0275, 2, 0], 1.337432], ['normal control 2', [[2056, 10, 21], [2056, 11, 21], [2056, 11, 12], 0.04125, 12, 7], 0.243952]], [['regression ex-coupon rebate sign 1', [[2060, 1, 31], [2060, 2, 29], [2060, 2, 22], 0.0625, 12, 10], -0.125718], ['regression ex-coupon rebate sign 2', [[2016, 11, 2], [2017, 2, 2], [2017, 1, 29], 0.05, 4, 5], -0.054348], ['partial repair probe 1', [[2052, 11, 21], [2052, 12, 21], [2052, 12, 14], 0.04125, 12, 10], -0.080208], ['partial repair probe 2', [[2020, 1, 31], [2020, 4, 30], [2020, 4, 29], 0.05, 4, 7], -0.013889], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 7], 0.05, 2, 7], 2.39011], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2036, 10, 10], [2037, 4, 10], [2037, 3, 30], 0.035, 2, 10], 1.644231], ['normal control 2', [[2044, 2, 25], [2044, 5, 25], [2044, 5, 16], 0.0125, 4, 0], 0.28125]]]
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 ex-coupon rebate sign 1-0.409226-0.111607Failed
regression ex-coupon rebate sign 2-3.452055-0.047945Failed
partial repair probe 1-0.288723-0.023777Failed
partial repair probe 2-2.430939-0.069061Failed
boundary control 1settlement outside periodsettlement outside periodPassed
boundary control 22.390112.39011Passed
normal control 10.00.0Passed
normal control 20.00.0Passed

SHA-256 / 63fcc50823dbc0ccfb7305ec3e8a5d5f1690dd1ee926c4a1d3f633be0c1ad04f

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 ex-coupon rebate sign 1', [[2031, 2, 28], [2031, 3, 28], [2031, 3, 22], 0.0625, 12, 7], -0.111607], ['regression ex-coupon rebate sign 2', [[2033, 7, 30], [2034, 7, 30], [2034, 7, 25], 0.035, 1, 10], -0.047945], ['partial repair probe 1', [[2008, 5, 27], [2008, 8, 27], [2008, 8, 20], 0.0125, 4, 7], -0.023777], ['partial repair probe 2', [[2059, 1, 29], [2059, 7, 29], [2059, 7, 24], 0.05, 2, 7], -0.069061], ['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, 7], 0.05, 2, 7], 2.39011], ['normal control 1', [[2008, 8, 31], [2008, 9, 30], [2008, 9, 30], 0.035, 12, 0], 0.0], ['normal control 2', [[2052, 10, 28], [2053, 4, 28], [2052, 10, 28], 0.0275, 2, 3], 0.0]], [['regression ex-coupon rebate sign 1', [[2050, 8, 31], [2050, 11, 30], [2050, 11, 25], 0.05, 4, 5], -0.068681], ['regression ex-coupon rebate sign 2', [[2036, 10, 18], [2037, 4, 18], [2037, 4, 15], 0.0625, 2, 10], -0.051511], ['partial repair probe 1', [[2029, 9, 30], [2029, 12, 30], [2029, 12, 26], 0.07, 4, 7], -0.076923], ['partial repair probe 2', [[2038, 9, 17], [2038, 10, 17], [2038, 10, 8], 0.04125, 12, 10], -0.103125], ['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, 7], 0.05, 2, 7], 2.39011], ['normal control 1', [[2024, 7, 28], [2025, 1, 28], [2024, 12, 17], 0.07, 2, 3], 2.701087], ['normal control 2', [[2015, 5, 1], [2015, 8, 1], [2015, 7, 17], 0.04125, 4, 0], 0.863111]], [['regression ex-coupon rebate sign 1', [[2029, 1, 29], [2029, 2, 28], [2029, 2, 26], 0.035, 12, 10], -0.019444], ['regression ex-coupon rebate sign 2', [[2047, 8, 29], [2047, 11, 29], [2047, 11, 27], 0.035, 4, 7], -0.019022], ['partial repair probe 1', [[2032, 8, 8], [2032, 9, 8], [2032, 9, 7], 0.0125, 12, 10], -0.00336], ['partial repair probe 2', [[2013, 4, 27], [2013, 10, 27], [2013, 10, 26], 0.04125, 2, 3], -0.01127], ['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', [[2028, 1, 31], [2028, 2, 29], [2028, 1, 31], 0.0125, 12, 7], 0.0], ['normal control 2', [[2044, 7, 23], [2044, 10, 23], [2044, 9, 12], 0.04125, 4, 0], 0.571671]], [['regression ex-coupon rebate sign 1', [[2042, 8, 31], [2043, 2, 28], [2043, 2, 25], 0.0125, 2, 10], -0.010359], ['regression ex-coupon rebate sign 2', [[2022, 7, 31], [2022, 10, 31], [2022, 10, 25], 0.04125, 4, 7], -0.067255], ['partial repair probe 1', [[2012, 4, 27], [2012, 7, 27], [2012, 7, 23], 0.0275, 4, 5], -0.03022], ['partial repair probe 2', [[2032, 10, 31], [2032, 11, 30], [2032, 11, 24], 0.05, 12, 10], -0.083333], ['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', [[2003, 4, 12], [2003, 10, 12], [2003, 10, 7], 0.0275, 2, 0], 1.337432], ['normal control 2', [[2056, 10, 21], [2056, 11, 21], [2056, 11, 12], 0.04125, 12, 7], 0.243952]], [['regression ex-coupon rebate sign 1', [[2060, 1, 31], [2060, 2, 29], [2060, 2, 22], 0.0625, 12, 10], -0.125718], ['regression ex-coupon rebate sign 2', [[2016, 11, 2], [2017, 2, 2], [2017, 1, 29], 0.05, 4, 5], -0.054348], ['partial repair probe 1', [[2052, 11, 21], [2052, 12, 21], [2052, 12, 14], 0.04125, 12, 10], -0.080208], ['partial repair probe 2', [[2020, 1, 31], [2020, 4, 30], [2020, 4, 29], 0.05, 4, 7], -0.013889], ['boundary control 1', [[2024, 1, 15], [2024, 7, 15], [2024, 7, 7], 0.05, 2, 7], 2.39011], ['boundary control 2', [[2024, 1, 15], [2024, 7, 15], [2024, 1, 15], 0.05, 2, 7], 0.0], ['normal control 1', [[2036, 10, 10], [2037, 4, 10], [2037, 3, 30], 0.035, 2, 10], 1.644231], ['normal control 2', [[2044, 2, 25], [2044, 5, 25], [2044, 5, 16], 0.0125, 4, 0], 0.28125]]]
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 ex-coupon rebate sign 1-0.111607-0.111607Passed
regression ex-coupon rebate sign 2-0.047945-0.047945Passed
partial repair probe 1-0.023777-0.023777Passed
partial repair probe 2-0.069061-0.069061Passed
boundary control 1settlement outside periodsettlement outside periodPassed
boundary control 22.390112.39011Passed
normal control 10.00.0Passed
normal control 20.00.0Passed

SHA-256 / bf2ffdca38861348f924d58c0b087b54623599aa49c3dbab97104cc4675f98f1

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

Case digest / a0a7f7757152bd7fa15dd61beed4572b14dad1f7d2de1a395e023b7c9d95c596