FA-61216 / Bond day-count conventions / Open access
Irregular final coupon amount: a short final period pays a full regular coupon · case 01
Bonds with a short last period overpay the final coupon.
ROOT CAUSE
The final coupon is always treated as a regular period coupon.
VERIFIED REPAIR
Pro-rate a short final coupon by days(prev, maturity) over the notional period length.
Unsuccessful approach: Adding one day to the notional period length pro-rates against the wrong denominator.
Case contract
Inputs the last regular coupon date prev, maturity (prev < maturity <= prev + 2 periods), months per period and annual rate. Quasi dates are prev shifted forward k*months with the prev day clamped to month length. c = 100*rate/freq. Short final period (maturity <= q1): c*days(prev,mat)/days(prev,q1). Long final period: c*(1 + days(q1,mat)/days(q1,q2)). 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
from fractions import Fraction
N = 1
observations = []
def solve(prev, maturity, months, rate):
def mlen(y, m):
if m == 2:
return 29 if (y % 4 == 0 and y % 100 != 0) or y % 400 == 0 else 28
return 30 if m in (4, 6, 9, 11) else 31
P = datetime.date(*prev)
M = datetime.date(*maturity)
freq = 12 // months
def fwd(k):
t = P.year * 12 + P.month - 1 + k * months
y, m = t // 12, t % 12 + 1
return datetime.date(y, m, min(P.day, mlen(y, m)))
c = 100 * Fraction(str(rate)) / freq
q1 = fwd(1)
if M <= q1:
frac = Fraction(1)
else:
q2 = fwd(2)
frac = 1 + Fraction((M - q1).days, (q2 - q1).days)
return round(float(c * frac), 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression short final stub 1', [[2013, 9, 23], [2014, 3, 12], 6, 0.03], 1.40884], ['regression short final stub 2', [[2042, 5, 10], [2042, 5, 27], 6, 0.0725], 0.334918], ['partial repair probe 1', [[2015, 10, 31], [2016, 1, 31], 3, 0.045], 1.125], ['partial repair probe 2', [[2020, 5, 23], [2020, 11, 23], 6, 0.0725], 3.625], ['normal control 1', [[2018, 2, 28], [2018, 7, 19], 3, 0.03], 1.173913], ['normal control 2', [[2028, 2, 21], [2029, 5, 25], 12, 0.045], 5.646575], ['normal control 3', [[2014, 3, 1], [2014, 10, 2], 6, 0.05], 2.928177], ['normal control 4', [[2007, 7, 17], [2007, 8, 28], 1, 0.05], 0.564516]], [['regression short final stub 1', [[2045, 11, 25], [2045, 11, 26], 1, 0.05], 0.013889], ['regression short final stub 2', [[2044, 5, 31], [2044, 6, 29], 3, 0.03], 0.236413], ['partial repair probe 1', [[2014, 2, 28], [2014, 8, 28], 6, 0.05], 2.5], ['partial repair probe 2', [[2024, 8, 24], [2025, 8, 24], 12, 0.0725], 7.25], ['normal control 1', [[2007, 2, 22], [2007, 3, 31], 1, 0.045], 0.483871], ['normal control 2', [[2017, 10, 31], [2017, 12, 21], 1, 0.05], 0.698925], ['normal control 3', [[2038, 1, 22], [2038, 12, 2], 6, 0.03], 2.584239], ['normal control 4', [[2025, 9, 29], [2025, 11, 22], 1, 0.05], 0.739247]], [['regression short final stub 1', [[2041, 3, 31], [2041, 4, 17], 3, 0.0725], 0.338599], ['regression short final stub 2', [[2024, 10, 27], [2024, 11, 1], 1, 0.0725], 0.097446], ['partial repair probe 1', [[2013, 12, 31], [2014, 6, 30], 6, 0.03], 1.5], ['partial repair probe 2', [[2045, 1, 31], [2045, 2, 28], 1, 0.0725], 0.604167], ['normal control 1', [[2018, 6, 17], [2018, 7, 28], 1, 0.03], 0.33871], ['normal control 2', [[2027, 1, 3], [2027, 10, 30], 6, 0.045], 3.705163], ['normal control 3', [[2041, 2, 11], [2041, 4, 5], 1, 0.0725], 1.091398], ['normal control 4', [[2033, 1, 31], [2034, 1, 1], 6, 0.0725], 6.658967]], [['regression short final stub 1', [[2006, 7, 31], [2006, 9, 19], 6, 0.0725], 0.985054], ['regression short final stub 2', [[2013, 12, 16], [2014, 1, 7], 6, 0.03], 0.181319], ['partial repair probe 1', [[2018, 3, 2], [2019, 3, 2], 12, 0.05], 5.0], ['partial repair probe 2', [[2026, 3, 6], [2026, 4, 6], 1, 0.03], 0.25], ['normal control 1', [[2006, 6, 22], [2007, 1, 17], 6, 0.045], 2.571429], ['normal control 2', [[2034, 12, 27], [2035, 2, 24], 1, 0.0725], 1.149866], ['normal control 3', [[2045, 11, 21], [2046, 5, 8], 3, 0.05], 2.317416], ['normal control 4', [[2043, 1, 17], [2043, 3, 16], 1, 0.05], 0.818452]], [['regression short final stub 1', [[2012, 10, 28], [2012, 11, 25], 6, 0.0725], 0.557692], ['regression short final stub 2', [[2045, 8, 30], [2045, 9, 13], 3, 0.03], 0.11413], ['partial repair probe 1', [[2035, 1, 30], [2035, 7, 30], 6, 0.045], 2.25], ['partial repair probe 2', [[2015, 6, 30], [2016, 6, 30], 12, 0.03], 3.0], ['normal control 1', [[2018, 2, 28], [2018, 7, 27], 3, 0.045], 1.858696], ['normal control 2', [[2030, 12, 29], [2032, 6, 14], 12, 0.0725], 10.577869], ['normal control 3', [[2026, 3, 30], [2027, 6, 18], 12, 0.05], 6.092896], ['normal control 4', [[2038, 12, 13], [2039, 5, 4], 3, 0.05], 1.956522]]]
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 short final stub 1 | 1.5 | 1.40884 | Failed |
| regression short final stub 2 | 3.625 | 0.334918 | Failed |
| partial repair probe 1 | 1.125 | 1.125 | Passed |
| partial repair probe 2 | 3.625 | 3.625 | Passed |
| normal control 1 | 1.173913 | 1.173913 | Passed |
| normal control 2 | 5.646575 | 5.646575 | Passed |
| normal control 3 | 2.928177 | 2.928177 | Passed |
| normal control 4 | 0.564516 | 0.564516 | Passed |
SHA-256 / c705a878ee217134fad2a7c75ba2a101ba492e3dcf19f8967b5edf0bcd7de1ea
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, maturity, months, rate):
def mlen(y, m):
if m == 2:
return 29 if (y % 4 == 0 and y % 100 != 0) or y % 400 == 0 else 28
return 30 if m in (4, 6, 9, 11) else 31
P = datetime.date(*prev)
M = datetime.date(*maturity)
freq = 12 // months
def fwd(k):
t = P.year * 12 + P.month - 1 + k * months
y, m = t // 12, t % 12 + 1
return datetime.date(y, m, min(P.day, mlen(y, m)))
c = 100 * Fraction(str(rate)) / freq
q1 = fwd(1)
if M <= q1:
frac = Fraction((M - P).days, (q1 - P).days + 1)
else:
q2 = fwd(2)
frac = 1 + Fraction((M - q1).days, (q2 - q1).days)
return round(float(c * frac), 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression short final stub 1', [[2013, 9, 23], [2014, 3, 12], 6, 0.03], 1.40884], ['regression short final stub 2', [[2042, 5, 10], [2042, 5, 27], 6, 0.0725], 0.334918], ['partial repair probe 1', [[2015, 10, 31], [2016, 1, 31], 3, 0.045], 1.125], ['partial repair probe 2', [[2020, 5, 23], [2020, 11, 23], 6, 0.0725], 3.625], ['normal control 1', [[2018, 2, 28], [2018, 7, 19], 3, 0.03], 1.173913], ['normal control 2', [[2028, 2, 21], [2029, 5, 25], 12, 0.045], 5.646575], ['normal control 3', [[2014, 3, 1], [2014, 10, 2], 6, 0.05], 2.928177], ['normal control 4', [[2007, 7, 17], [2007, 8, 28], 1, 0.05], 0.564516]], [['regression short final stub 1', [[2045, 11, 25], [2045, 11, 26], 1, 0.05], 0.013889], ['regression short final stub 2', [[2044, 5, 31], [2044, 6, 29], 3, 0.03], 0.236413], ['partial repair probe 1', [[2014, 2, 28], [2014, 8, 28], 6, 0.05], 2.5], ['partial repair probe 2', [[2024, 8, 24], [2025, 8, 24], 12, 0.0725], 7.25], ['normal control 1', [[2007, 2, 22], [2007, 3, 31], 1, 0.045], 0.483871], ['normal control 2', [[2017, 10, 31], [2017, 12, 21], 1, 0.05], 0.698925], ['normal control 3', [[2038, 1, 22], [2038, 12, 2], 6, 0.03], 2.584239], ['normal control 4', [[2025, 9, 29], [2025, 11, 22], 1, 0.05], 0.739247]], [['regression short final stub 1', [[2041, 3, 31], [2041, 4, 17], 3, 0.0725], 0.338599], ['regression short final stub 2', [[2024, 10, 27], [2024, 11, 1], 1, 0.0725], 0.097446], ['partial repair probe 1', [[2013, 12, 31], [2014, 6, 30], 6, 0.03], 1.5], ['partial repair probe 2', [[2045, 1, 31], [2045, 2, 28], 1, 0.0725], 0.604167], ['normal control 1', [[2018, 6, 17], [2018, 7, 28], 1, 0.03], 0.33871], ['normal control 2', [[2027, 1, 3], [2027, 10, 30], 6, 0.045], 3.705163], ['normal control 3', [[2041, 2, 11], [2041, 4, 5], 1, 0.0725], 1.091398], ['normal control 4', [[2033, 1, 31], [2034, 1, 1], 6, 0.0725], 6.658967]], [['regression short final stub 1', [[2006, 7, 31], [2006, 9, 19], 6, 0.0725], 0.985054], ['regression short final stub 2', [[2013, 12, 16], [2014, 1, 7], 6, 0.03], 0.181319], ['partial repair probe 1', [[2018, 3, 2], [2019, 3, 2], 12, 0.05], 5.0], ['partial repair probe 2', [[2026, 3, 6], [2026, 4, 6], 1, 0.03], 0.25], ['normal control 1', [[2006, 6, 22], [2007, 1, 17], 6, 0.045], 2.571429], ['normal control 2', [[2034, 12, 27], [2035, 2, 24], 1, 0.0725], 1.149866], ['normal control 3', [[2045, 11, 21], [2046, 5, 8], 3, 0.05], 2.317416], ['normal control 4', [[2043, 1, 17], [2043, 3, 16], 1, 0.05], 0.818452]], [['regression short final stub 1', [[2012, 10, 28], [2012, 11, 25], 6, 0.0725], 0.557692], ['regression short final stub 2', [[2045, 8, 30], [2045, 9, 13], 3, 0.03], 0.11413], ['partial repair probe 1', [[2035, 1, 30], [2035, 7, 30], 6, 0.045], 2.25], ['partial repair probe 2', [[2015, 6, 30], [2016, 6, 30], 12, 0.03], 3.0], ['normal control 1', [[2018, 2, 28], [2018, 7, 27], 3, 0.045], 1.858696], ['normal control 2', [[2030, 12, 29], [2032, 6, 14], 12, 0.0725], 10.577869], ['normal control 3', [[2026, 3, 30], [2027, 6, 18], 12, 0.05], 6.092896], ['normal control 4', [[2038, 12, 13], [2039, 5, 4], 3, 0.05], 1.956522]]]
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 short final stub 1 | 1.401099 | 1.40884 | Failed |
| regression short final stub 2 | 0.333108 | 0.334918 | Failed |
| partial repair probe 1 | 1.112903 | 1.125 | Failed |
| partial repair probe 2 | 3.605405 | 3.625 | Failed |
| normal control 1 | 1.173913 | 1.173913 | Passed |
| normal control 2 | 5.646575 | 5.646575 | Passed |
| normal control 3 | 2.928177 | 2.928177 | Passed |
| normal control 4 | 0.564516 | 0.564516 | Passed |
SHA-256 / d6a18180664763457d19e6a4b4be10834bc9ec97a47a80757170804b3955f4d9
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, maturity, months, rate):
def mlen(y, m):
if m == 2:
return 29 if (y % 4 == 0 and y % 100 != 0) or y % 400 == 0 else 28
return 30 if m in (4, 6, 9, 11) else 31
P = datetime.date(*prev)
M = datetime.date(*maturity)
freq = 12 // months
def fwd(k):
t = P.year * 12 + P.month - 1 + k * months
y, m = t // 12, t % 12 + 1
return datetime.date(y, m, min(P.day, mlen(y, m)))
c = 100 * Fraction(str(rate)) / freq
q1 = fwd(1)
if M <= q1:
frac = Fraction((M - P).days, (q1 - P).days)
else:
q2 = fwd(2)
frac = 1 + Fraction((M - q1).days, (q2 - q1).days)
return round(float(c * frac), 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression short final stub 1', [[2013, 9, 23], [2014, 3, 12], 6, 0.03], 1.40884], ['regression short final stub 2', [[2042, 5, 10], [2042, 5, 27], 6, 0.0725], 0.334918], ['partial repair probe 1', [[2015, 10, 31], [2016, 1, 31], 3, 0.045], 1.125], ['partial repair probe 2', [[2020, 5, 23], [2020, 11, 23], 6, 0.0725], 3.625], ['normal control 1', [[2018, 2, 28], [2018, 7, 19], 3, 0.03], 1.173913], ['normal control 2', [[2028, 2, 21], [2029, 5, 25], 12, 0.045], 5.646575], ['normal control 3', [[2014, 3, 1], [2014, 10, 2], 6, 0.05], 2.928177], ['normal control 4', [[2007, 7, 17], [2007, 8, 28], 1, 0.05], 0.564516]], [['regression short final stub 1', [[2045, 11, 25], [2045, 11, 26], 1, 0.05], 0.013889], ['regression short final stub 2', [[2044, 5, 31], [2044, 6, 29], 3, 0.03], 0.236413], ['partial repair probe 1', [[2014, 2, 28], [2014, 8, 28], 6, 0.05], 2.5], ['partial repair probe 2', [[2024, 8, 24], [2025, 8, 24], 12, 0.0725], 7.25], ['normal control 1', [[2007, 2, 22], [2007, 3, 31], 1, 0.045], 0.483871], ['normal control 2', [[2017, 10, 31], [2017, 12, 21], 1, 0.05], 0.698925], ['normal control 3', [[2038, 1, 22], [2038, 12, 2], 6, 0.03], 2.584239], ['normal control 4', [[2025, 9, 29], [2025, 11, 22], 1, 0.05], 0.739247]], [['regression short final stub 1', [[2041, 3, 31], [2041, 4, 17], 3, 0.0725], 0.338599], ['regression short final stub 2', [[2024, 10, 27], [2024, 11, 1], 1, 0.0725], 0.097446], ['partial repair probe 1', [[2013, 12, 31], [2014, 6, 30], 6, 0.03], 1.5], ['partial repair probe 2', [[2045, 1, 31], [2045, 2, 28], 1, 0.0725], 0.604167], ['normal control 1', [[2018, 6, 17], [2018, 7, 28], 1, 0.03], 0.33871], ['normal control 2', [[2027, 1, 3], [2027, 10, 30], 6, 0.045], 3.705163], ['normal control 3', [[2041, 2, 11], [2041, 4, 5], 1, 0.0725], 1.091398], ['normal control 4', [[2033, 1, 31], [2034, 1, 1], 6, 0.0725], 6.658967]], [['regression short final stub 1', [[2006, 7, 31], [2006, 9, 19], 6, 0.0725], 0.985054], ['regression short final stub 2', [[2013, 12, 16], [2014, 1, 7], 6, 0.03], 0.181319], ['partial repair probe 1', [[2018, 3, 2], [2019, 3, 2], 12, 0.05], 5.0], ['partial repair probe 2', [[2026, 3, 6], [2026, 4, 6], 1, 0.03], 0.25], ['normal control 1', [[2006, 6, 22], [2007, 1, 17], 6, 0.045], 2.571429], ['normal control 2', [[2034, 12, 27], [2035, 2, 24], 1, 0.0725], 1.149866], ['normal control 3', [[2045, 11, 21], [2046, 5, 8], 3, 0.05], 2.317416], ['normal control 4', [[2043, 1, 17], [2043, 3, 16], 1, 0.05], 0.818452]], [['regression short final stub 1', [[2012, 10, 28], [2012, 11, 25], 6, 0.0725], 0.557692], ['regression short final stub 2', [[2045, 8, 30], [2045, 9, 13], 3, 0.03], 0.11413], ['partial repair probe 1', [[2035, 1, 30], [2035, 7, 30], 6, 0.045], 2.25], ['partial repair probe 2', [[2015, 6, 30], [2016, 6, 30], 12, 0.03], 3.0], ['normal control 1', [[2018, 2, 28], [2018, 7, 27], 3, 0.045], 1.858696], ['normal control 2', [[2030, 12, 29], [2032, 6, 14], 12, 0.0725], 10.577869], ['normal control 3', [[2026, 3, 30], [2027, 6, 18], 12, 0.05], 6.092896], ['normal control 4', [[2038, 12, 13], [2039, 5, 4], 3, 0.05], 1.956522]]]
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 short final stub 1 | 1.40884 | 1.40884 | Passed |
| regression short final stub 2 | 0.334918 | 0.334918 | Passed |
| partial repair probe 1 | 1.125 | 1.125 | Passed |
| partial repair probe 2 | 3.625 | 3.625 | Passed |
| normal control 1 | 1.173913 | 1.173913 | Passed |
| normal control 2 | 5.646575 | 5.646575 | Passed |
| normal control 3 | 2.928177 | 2.928177 | Passed |
| normal control 4 | 0.564516 | 0.564516 | Passed |
SHA-256 / 9489994585ae0f24281890250d3a61651d2469b6346d1faca86f68b62f051082
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:53.115691+00:00.
Case digest / 1f52637ebb6f504393923f414c26c9effd64a80c9ec39bf84ae0731123a41c1a