FA-61966 / Currency rounding and FX conversion / Open access
A rate published at the trade instant is not eligible · case 01
A trade at t=7 ignores the tick published at t=7 and reports a stale or older rate.
ROOT CAUSE
Eligibility uses ts < trade_ts instead of ts <= trade_ts.
VERIFIED REPAIR
Ticks at or before the trade timestamp are eligible.
Unsuccessful approach: Allowing one unit of clock skew admits a tick published after the trade.
Case contract
solve(ticks, trade_ts, max_age): ticks is the published rate feed [[ts, rate], ...] in publication order. Use the tick with the greatest ts <= trade_ts; among ticks with equal ts the later-published one is a correction and wins. No eligible tick returns 'ERR:no-rate'. If max_age is not None and trade_ts - ts > max_age the rate is 'ERR:stale' (max_age 0 accepts only a same-instant tick). Return [ts, rate].
Why this case matters
Currency amounts must be rounded at the right stage and in the right unit, or ledgers, quotes and settlements drift by minor units.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_FLOOR, ROUND_CEILING, ROUND_DOWN, ROUND_UP, ROUND_HALF_DOWN
N = 1
observations = []
def solve(ticks, trade_ts, max_age):
best = None
for ts, r in ticks:
if ts < trade_ts and (best is None or ts >= best[0]):
best = [ts, r]
if best is None: return 'ERR:no-rate'
if max_age is not None and trade_ts - best[0] > max_age: return 'ERR:stale'
return best
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression same-instant-tick 1', ([[7, '1.40']], 7, 0), [7, '1.40']),
('regression same-instant-tick 2', ([[6, '1.26'], [7, '1.27'], [8, '1.28']], 7, None), [7, '1.27']),
('partial repair guard 2', ([[16, '1.0577'], [3, '1.1605'], [9, '1.0153'], [16, '1.0347'], [5, '1.1882']], 4, 1),
[3, '1.1605']),
('control: correction wins', ([[1, '1.10'], [5, '1.20'], [5, '1.21'], [9, '1.30']], 7, 3), [5, '1.21']),
('control: stale', ([[1, '1.10'], [9, '1.30']], 7, 3), 'ERR:stale'),
('control: only future', ([[9, '1.30']], 7, None), 'ERR:no-rate'),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05'])],
[('regression same-instant-tick 1', ([[6, '1.26'], [2, '1.22'], [6, '1.19']], 6, 1), [6, '1.19']),
('regression same-instant-tick 2',
([[15, '1.2872'], [8, '1.1171'], [6, '1.0302'], [2, '1.1042'], [17, '1.1364']], 8, 1), [8, '1.1171']),
('partial repair guard 1', ([[10, '1.2328'], [1, '1.0928'], [3, '1.1922'], [19, '1.2521'], [19, '1.2864']], 2, 0),
'ERR:stale'),
('partial repair guard 2', ([[10, '1.1254'], [1, '1.0516'], [3, '1.1640']], 2, 1), [1, '1.0516']),
('control: same instant zero age', ([[7, '1.40']], 7, 0), [7, '1.40']),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05']),
('control: age boundary', ([[4, '1.11'], [12, '1.50']], 8, 4), [4, '1.11']),
('control: empty feed', ([], 3, 5), 'ERR:no-rate')],
[('regression same-instant-tick 1', ([[1, '1.2376'], [19, '1.2134'], [14, '1.2465']], 1, 0), [1, '1.2376']),
('regression same-instant-tick 2',
([[7, '1.2163'], [19, '1.2277'], [7, '1.0913'], [11, '1.0442'], [9, '1.2833']], 9, 5), [9, '1.2833']),
('partial repair guard 1',
([[7, '1.1076'], [3, '1.0453'], [4, '1.1000'], [9, '1.2258'], [18, '1.1773'], [19, '1.1059']], 8, 3),
[7, '1.1076']),
('partial repair guard 2', ([[5, '1.1041'], [16, '1.2924'], [15, '1.0087'], [9, '1.2750'], [15, '1.0502']], 15, 3),
[15, '1.0502']),
('control: empty feed', ([], 3, 5), 'ERR:no-rate'),
('control: out of order feed', ([[6, '1.26'], [2, '1.22'], [6, '1.19']], 6, 1), [6, '1.19']),
('control: correction wins', ([[1, '1.10'], [5, '1.20'], [5, '1.21'], [9, '1.30']], 7, 3), [5, '1.21']),
('control: stale', ([[1, '1.10'], [9, '1.30']], 7, 3), 'ERR:stale')],
[('regression same-instant-tick 1',
([[5, '1.1041'], [16, '1.2924'], [15, '1.0087'], [9, '1.2750'], [15, '1.0502']], 15, 3), [15, '1.0502']),
('regression same-instant-tick 2', ([[13, '1.2039'], [1, '1.2085']], 1, 3), [1, '1.2085']),
('partial repair guard 1',
([[1, '1.0230'], [14, '1.0591'], [3, '1.2010'], [13, '1.0135'], [3, '1.1001'], [13, '1.1421']], 12, 3),
'ERR:stale'),
('partial repair guard 2',
([[5, '1.0556'], [2, '1.2743'], [17, '1.2317'], [7, '1.0015'], [4, '1.2457'], [7, '1.1125']], 16, 5), 'ERR:stale'),
('control: stale', ([[1, '1.10'], [9, '1.30']], 7, 3), 'ERR:stale'),
('control: only future', ([[9, '1.30']], 7, None), 'ERR:no-rate'),
('control: same instant zero age', ([[7, '1.40']], 7, 0), [7, '1.40']),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05'])],
[('regression same-instant-tick 1',
([[15, '1.2834'], [0, '1.1993'], [2, '1.2750'], [8, '1.0747'], [5, '1.1148'], [18, '1.2971']], 15, 3),
[15, '1.2834']),
('regression same-instant-tick 2',
([[3, '1.1214'], [20, '1.2326'], [12, '1.0063'], [14, '1.1176'], [6, '1.2748'], [7, '1.1316']], 3, 3),
[3, '1.1214']),
('partial repair guard 1',
([[16, '1.2403'], [10, '1.2789'], [13, '1.0473'], [14, '1.2294'], [1, '1.0595'], [14, '1.2951']], 9, 1),
'ERR:stale'),
('partial repair guard 2',
([[0, '1.2844'], [3, '1.2356'], [8, '1.0943'], [8, '1.1814'], [16, '1.0916'], [15, '1.0591']], 7, 0), 'ERR:stale'),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05']),
('control: age boundary', ([[4, '1.11'], [12, '1.50']], 8, 4), [4, '1.11']),
('control: empty feed', ([], 3, 5), 'ERR:no-rate'),
('control: out of order feed', ([[6, '1.26'], [2, '1.22'], [6, '1.19']], 6, 1), [6, '1.19'])]]
for label, args, expected in cases[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 same-instant-tick 1 | ERR:no-rate | [7, '1.40'] | Failed |
| regression same-instant-tick 2 | [6, '1.26'] | [7, '1.27'] | Failed |
| partial repair guard 2 | [3, '1.1605'] | [3, '1.1605'] | Passed |
| control: correction wins | [5, '1.21'] | [5, '1.21'] | Passed |
| control: stale | ERR:stale | ERR:stale | Passed |
| control: only future | ERR:no-rate | ERR:no-rate | Passed |
| control: unlimited age | [0, '1.05'] | [0, '1.05'] | Passed |
SHA-256 / cbfacb4da96b2eb1478f9371a70ebc866c0ecd0db015ec236469f29d8ca5fe1a
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_FLOOR, ROUND_CEILING, ROUND_DOWN, ROUND_UP, ROUND_HALF_DOWN
N = 1
observations = []
def solve(ticks, trade_ts, max_age):
best = None
for ts, r in ticks:
if ts <= trade_ts + 1 and (best is None or ts >= best[0]):
best = [ts, r]
if best is None: return 'ERR:no-rate'
if max_age is not None and trade_ts - best[0] > max_age: return 'ERR:stale'
return best
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression same-instant-tick 1', ([[7, '1.40']], 7, 0), [7, '1.40']),
('regression same-instant-tick 2', ([[6, '1.26'], [7, '1.27'], [8, '1.28']], 7, None), [7, '1.27']),
('partial repair guard 2', ([[16, '1.0577'], [3, '1.1605'], [9, '1.0153'], [16, '1.0347'], [5, '1.1882']], 4, 1),
[3, '1.1605']),
('control: correction wins', ([[1, '1.10'], [5, '1.20'], [5, '1.21'], [9, '1.30']], 7, 3), [5, '1.21']),
('control: stale', ([[1, '1.10'], [9, '1.30']], 7, 3), 'ERR:stale'),
('control: only future', ([[9, '1.30']], 7, None), 'ERR:no-rate'),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05'])],
[('regression same-instant-tick 1', ([[6, '1.26'], [2, '1.22'], [6, '1.19']], 6, 1), [6, '1.19']),
('regression same-instant-tick 2',
([[15, '1.2872'], [8, '1.1171'], [6, '1.0302'], [2, '1.1042'], [17, '1.1364']], 8, 1), [8, '1.1171']),
('partial repair guard 1', ([[10, '1.2328'], [1, '1.0928'], [3, '1.1922'], [19, '1.2521'], [19, '1.2864']], 2, 0),
'ERR:stale'),
('partial repair guard 2', ([[10, '1.1254'], [1, '1.0516'], [3, '1.1640']], 2, 1), [1, '1.0516']),
('control: same instant zero age', ([[7, '1.40']], 7, 0), [7, '1.40']),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05']),
('control: age boundary', ([[4, '1.11'], [12, '1.50']], 8, 4), [4, '1.11']),
('control: empty feed', ([], 3, 5), 'ERR:no-rate')],
[('regression same-instant-tick 1', ([[1, '1.2376'], [19, '1.2134'], [14, '1.2465']], 1, 0), [1, '1.2376']),
('regression same-instant-tick 2',
([[7, '1.2163'], [19, '1.2277'], [7, '1.0913'], [11, '1.0442'], [9, '1.2833']], 9, 5), [9, '1.2833']),
('partial repair guard 1',
([[7, '1.1076'], [3, '1.0453'], [4, '1.1000'], [9, '1.2258'], [18, '1.1773'], [19, '1.1059']], 8, 3),
[7, '1.1076']),
('partial repair guard 2', ([[5, '1.1041'], [16, '1.2924'], [15, '1.0087'], [9, '1.2750'], [15, '1.0502']], 15, 3),
[15, '1.0502']),
('control: empty feed', ([], 3, 5), 'ERR:no-rate'),
('control: out of order feed', ([[6, '1.26'], [2, '1.22'], [6, '1.19']], 6, 1), [6, '1.19']),
('control: correction wins', ([[1, '1.10'], [5, '1.20'], [5, '1.21'], [9, '1.30']], 7, 3), [5, '1.21']),
('control: stale', ([[1, '1.10'], [9, '1.30']], 7, 3), 'ERR:stale')],
[('regression same-instant-tick 1',
([[5, '1.1041'], [16, '1.2924'], [15, '1.0087'], [9, '1.2750'], [15, '1.0502']], 15, 3), [15, '1.0502']),
('regression same-instant-tick 2', ([[13, '1.2039'], [1, '1.2085']], 1, 3), [1, '1.2085']),
('partial repair guard 1',
([[1, '1.0230'], [14, '1.0591'], [3, '1.2010'], [13, '1.0135'], [3, '1.1001'], [13, '1.1421']], 12, 3),
'ERR:stale'),
('partial repair guard 2',
([[5, '1.0556'], [2, '1.2743'], [17, '1.2317'], [7, '1.0015'], [4, '1.2457'], [7, '1.1125']], 16, 5), 'ERR:stale'),
('control: stale', ([[1, '1.10'], [9, '1.30']], 7, 3), 'ERR:stale'),
('control: only future', ([[9, '1.30']], 7, None), 'ERR:no-rate'),
('control: same instant zero age', ([[7, '1.40']], 7, 0), [7, '1.40']),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05'])],
[('regression same-instant-tick 1',
([[15, '1.2834'], [0, '1.1993'], [2, '1.2750'], [8, '1.0747'], [5, '1.1148'], [18, '1.2971']], 15, 3),
[15, '1.2834']),
('regression same-instant-tick 2',
([[3, '1.1214'], [20, '1.2326'], [12, '1.0063'], [14, '1.1176'], [6, '1.2748'], [7, '1.1316']], 3, 3),
[3, '1.1214']),
('partial repair guard 1',
([[16, '1.2403'], [10, '1.2789'], [13, '1.0473'], [14, '1.2294'], [1, '1.0595'], [14, '1.2951']], 9, 1),
'ERR:stale'),
('partial repair guard 2',
([[0, '1.2844'], [3, '1.2356'], [8, '1.0943'], [8, '1.1814'], [16, '1.0916'], [15, '1.0591']], 7, 0), 'ERR:stale'),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05']),
('control: age boundary', ([[4, '1.11'], [12, '1.50']], 8, 4), [4, '1.11']),
('control: empty feed', ([], 3, 5), 'ERR:no-rate'),
('control: out of order feed', ([[6, '1.26'], [2, '1.22'], [6, '1.19']], 6, 1), [6, '1.19'])]]
for label, args, expected in cases[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 same-instant-tick 1 | [7, '1.40'] | [7, '1.40'] | Passed |
| regression same-instant-tick 2 | [8, '1.28'] | [7, '1.27'] | Failed |
| partial repair guard 2 | [5, '1.1882'] | [3, '1.1605'] | Failed |
| control: correction wins | [5, '1.21'] | [5, '1.21'] | Passed |
| control: stale | ERR:stale | ERR:stale | Passed |
| control: only future | ERR:no-rate | ERR:no-rate | Passed |
| control: unlimited age | [0, '1.05'] | [0, '1.05'] | Passed |
SHA-256 / 8df8b589574486dbd399938e974d490ff746b2be743a88fab96b66cf8635db5e
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_FLOOR, ROUND_CEILING, ROUND_DOWN, ROUND_UP, ROUND_HALF_DOWN
N = 1
observations = []
def solve(ticks, trade_ts, max_age):
best = None
for ts, r in ticks:
if ts <= trade_ts and (best is None or ts >= best[0]):
best = [ts, r]
if best is None: return 'ERR:no-rate'
if max_age is not None and trade_ts - best[0] > max_age: return 'ERR:stale'
return best
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression same-instant-tick 1', ([[7, '1.40']], 7, 0), [7, '1.40']),
('regression same-instant-tick 2', ([[6, '1.26'], [7, '1.27'], [8, '1.28']], 7, None), [7, '1.27']),
('partial repair guard 2', ([[16, '1.0577'], [3, '1.1605'], [9, '1.0153'], [16, '1.0347'], [5, '1.1882']], 4, 1),
[3, '1.1605']),
('control: correction wins', ([[1, '1.10'], [5, '1.20'], [5, '1.21'], [9, '1.30']], 7, 3), [5, '1.21']),
('control: stale', ([[1, '1.10'], [9, '1.30']], 7, 3), 'ERR:stale'),
('control: only future', ([[9, '1.30']], 7, None), 'ERR:no-rate'),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05'])],
[('regression same-instant-tick 1', ([[6, '1.26'], [2, '1.22'], [6, '1.19']], 6, 1), [6, '1.19']),
('regression same-instant-tick 2',
([[15, '1.2872'], [8, '1.1171'], [6, '1.0302'], [2, '1.1042'], [17, '1.1364']], 8, 1), [8, '1.1171']),
('partial repair guard 1', ([[10, '1.2328'], [1, '1.0928'], [3, '1.1922'], [19, '1.2521'], [19, '1.2864']], 2, 0),
'ERR:stale'),
('partial repair guard 2', ([[10, '1.1254'], [1, '1.0516'], [3, '1.1640']], 2, 1), [1, '1.0516']),
('control: same instant zero age', ([[7, '1.40']], 7, 0), [7, '1.40']),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05']),
('control: age boundary', ([[4, '1.11'], [12, '1.50']], 8, 4), [4, '1.11']),
('control: empty feed', ([], 3, 5), 'ERR:no-rate')],
[('regression same-instant-tick 1', ([[1, '1.2376'], [19, '1.2134'], [14, '1.2465']], 1, 0), [1, '1.2376']),
('regression same-instant-tick 2',
([[7, '1.2163'], [19, '1.2277'], [7, '1.0913'], [11, '1.0442'], [9, '1.2833']], 9, 5), [9, '1.2833']),
('partial repair guard 1',
([[7, '1.1076'], [3, '1.0453'], [4, '1.1000'], [9, '1.2258'], [18, '1.1773'], [19, '1.1059']], 8, 3),
[7, '1.1076']),
('partial repair guard 2', ([[5, '1.1041'], [16, '1.2924'], [15, '1.0087'], [9, '1.2750'], [15, '1.0502']], 15, 3),
[15, '1.0502']),
('control: empty feed', ([], 3, 5), 'ERR:no-rate'),
('control: out of order feed', ([[6, '1.26'], [2, '1.22'], [6, '1.19']], 6, 1), [6, '1.19']),
('control: correction wins', ([[1, '1.10'], [5, '1.20'], [5, '1.21'], [9, '1.30']], 7, 3), [5, '1.21']),
('control: stale', ([[1, '1.10'], [9, '1.30']], 7, 3), 'ERR:stale')],
[('regression same-instant-tick 1',
([[5, '1.1041'], [16, '1.2924'], [15, '1.0087'], [9, '1.2750'], [15, '1.0502']], 15, 3), [15, '1.0502']),
('regression same-instant-tick 2', ([[13, '1.2039'], [1, '1.2085']], 1, 3), [1, '1.2085']),
('partial repair guard 1',
([[1, '1.0230'], [14, '1.0591'], [3, '1.2010'], [13, '1.0135'], [3, '1.1001'], [13, '1.1421']], 12, 3),
'ERR:stale'),
('partial repair guard 2',
([[5, '1.0556'], [2, '1.2743'], [17, '1.2317'], [7, '1.0015'], [4, '1.2457'], [7, '1.1125']], 16, 5), 'ERR:stale'),
('control: stale', ([[1, '1.10'], [9, '1.30']], 7, 3), 'ERR:stale'),
('control: only future', ([[9, '1.30']], 7, None), 'ERR:no-rate'),
('control: same instant zero age', ([[7, '1.40']], 7, 0), [7, '1.40']),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05'])],
[('regression same-instant-tick 1',
([[15, '1.2834'], [0, '1.1993'], [2, '1.2750'], [8, '1.0747'], [5, '1.1148'], [18, '1.2971']], 15, 3),
[15, '1.2834']),
('regression same-instant-tick 2',
([[3, '1.1214'], [20, '1.2326'], [12, '1.0063'], [14, '1.1176'], [6, '1.2748'], [7, '1.1316']], 3, 3),
[3, '1.1214']),
('partial repair guard 1',
([[16, '1.2403'], [10, '1.2789'], [13, '1.0473'], [14, '1.2294'], [1, '1.0595'], [14, '1.2951']], 9, 1),
'ERR:stale'),
('partial repair guard 2',
([[0, '1.2844'], [3, '1.2356'], [8, '1.0943'], [8, '1.1814'], [16, '1.0916'], [15, '1.0591']], 7, 0), 'ERR:stale'),
('control: unlimited age', ([[0, '1.05']], 20, None), [0, '1.05']),
('control: age boundary', ([[4, '1.11'], [12, '1.50']], 8, 4), [4, '1.11']),
('control: empty feed', ([], 3, 5), 'ERR:no-rate'),
('control: out of order feed', ([[6, '1.26'], [2, '1.22'], [6, '1.19']], 6, 1), [6, '1.19'])]]
for label, args, expected in cases[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 same-instant-tick 1 | [7, '1.40'] | [7, '1.40'] | Passed |
| regression same-instant-tick 2 | [7, '1.27'] | [7, '1.27'] | Passed |
| partial repair guard 2 | [3, '1.1605'] | [3, '1.1605'] | Passed |
| control: correction wins | [5, '1.21'] | [5, '1.21'] | Passed |
| control: stale | ERR:stale | ERR:stale | Passed |
| control: only future | ERR:no-rate | ERR:no-rate | Passed |
| control: unlimited age | [0, '1.05'] | [0, '1.05'] | Passed |
SHA-256 / 12a09ea34073b64ba3c4c66a513ec815904df63dac22613d4cfc6a0b6caf722e
Verification & scope
A deterministic, bounded teaching model with a stipulated toy contract; it makes no claim of conformance to any real regulation, standard, or institution's rules. 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:47:00.116045+00:00.
Case digest / 0ccbd4e95a281e2530e1e2b0efd3a3898a6213b4c1ff8e2427dbb0db411f226a