FA-84536 / Betting odds conversion / Open access
Losing lay bet charged the stake instead of the liability · case 01
A losing lay at 4.00 costs only the backer stake.
ROOT CAUSE
The lay-loss branch subtracts the stake.
VERIFIED REPAIR
Charge the liability stake * (price - 1), rounded up to the cent.
Unsuccessful approach: Charging stake * price includes the backer stake in the liability.
Case contract
Betting exchange settlement. bets rows are [market, side, stake_cents, price, won] where won says whether the bet won. Back: win profit floor(stake * (price - 1)), loss -stake. Lay: win +stake, loss -ceil(stake * (price - 1)) (the liability). Commission is charged per market on the market net profit only when that net is positive, at rate_pct percent rounded half up to a cent. Return [total net after commission, total commission] in cents.
Why this case matters
Exchanges charge commission on net market winnings, not per winning bet.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
from fractions import Fraction
import math
N = 1
observations = []
def solve(bets, rate_pct):
nets = {}
for market, side, stake, price, won in bets:
p = Fraction(price)
if side == 'back':
pl = math.floor(stake * (p - 1)) if won else -stake
else:
pl = stake if won else -stake
nets[market] = nets.get(market, 0) + pl
comm = 0
for m, net in nets.items():
if net > 0:
comm += math.floor(net * Fraction(rate_pct) / 100 + Fraction(1, 2))
return [sum(nets.values()) - comm, comm]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
def run(args):
try:
return solve(*args)
except Exception as exc:
return 'raised ' + type(exc).__name__
cases = [[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m3', 'back', 2000, '3.91', True],
['m3', 'back', 1000, '4.34', True],
['m2', 'back', 1000, '5.86', True],
['m3', 'lay', 2000, '1.49', False]],
'6.5'),
[12192, 848]),
('variant scenario 1',
([['m1', 'back', 2000, '5.34', True],
['m1', 'back', 100, '2.32', True],
['m2', 'lay', 1000, '7.42', False],
['m2', 'lay', 1000, '6.91', False],
['m2', 'lay', 2000, '4.87', True]],
'6.5'),
[-2091, 573]),
('variant scenario 2',
([['m2', 'back', 2000, '1.48', False],
['m2', 'back', 250, '5.52', False],
['m2', 'back', 1000, '7.14', True],
['m1', 'lay', 777, '7.92', False]],
'6.5'),
[-1740, 253])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m2', 'lay', 777, '7.74', False], ['m2', 'lay', 2000, '6.02', True]], '5'),
[-3237, 0]),
('variant scenario 1', ([['m1', 'lay', 1000, '3.82', False]], '5'), [-2820, 0]),
('variant scenario 2',
([['m3', 'back', 250, '2.61', True], ['m2', 'lay', 250, '7.61', True]], '2'),
[639, 13])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m1', 'lay', 2000, '7.65', False],
['m1', 'back', 100, '7.65', True],
['m2', 'lay', 250, '3.87', True],
['m3', 'back', 250, '7.51', True]],
'5'),
[-10852, 94]),
('variant scenario 1',
([['m1', 'back', 777, '2.00', True],
['m3', 'lay', 100, '3.56', True],
['m1', 'back', 100, '7.20', False]],
'2'),
[761, 16]),
('variant scenario 2', ([['m3', 'lay', 2000, '3.18', True]], '2'), [1960, 40])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m3', 'lay', 1000, '5.16', True],
['m3', 'back', 100, '3.04', True],
['m1', 'back', 2000, '5.82', False],
['m3', 'lay', 250, '3.59', False],
['m1', 'lay', 2000, '3.53', True]],
'2'),
[545, 11]),
('variant scenario 1', ([['m1', 'back', 100, '6.47', True]], '2'), [536, 11]),
('variant scenario 2',
([['m2', 'lay', 2000, '7.84', True],
['m1', 'back', 250, '6.18', False],
['m2', 'back', 100, '7.05', True]],
'2'),
[2303, 52])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m2', 'back', 2000, '1.66', False],
['m3', 'lay', 2000, '7.57', True],
['m2', 'back', 250, '5.20', False],
['m3', 'back', 100, '7.50', True],
['m3', 'lay', 250, '2.34', False]],
'2'),
[19, 46]),
('variant scenario 1', ([['m1', 'lay', 100, '7.27', True]], '2'), [98, 2]),
('variant scenario 2', ([['m1', 'back', 777, '6.40', True]], '5'), [3985, 210])]]
for label, args, expected in cases[N - 1]:
check(label, run(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 |
|---|---|---|---|
| control single back winner | [1900, 100] | [1900, 100] | Passed |
| boundary hedged market nets out | [950, 50] | [475, 25] | Failed |
| control lay winner | [950, 50] | [950, 50] | Passed |
| boundary losing market no commission | [-500, 0] | [-500, 0] | Passed |
| regression: lay liability | [11239, 781] | [12192, 848] | Failed |
| variant scenario 1 | [8239, 573] | [-2091, 573] | Failed |
| variant scenario 2 | [2860, 253] | [-1740, 253] | Failed |
SHA-256 / 5b6fa033f5bc89f4f83127d4f73d36e810b1ae30c601f8854905dfd06761a757
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
from fractions import Fraction
import math
N = 1
observations = []
def solve(bets, rate_pct):
nets = {}
for market, side, stake, price, won in bets:
p = Fraction(price)
if side == 'back':
pl = math.floor(stake * (p - 1)) if won else -stake
else:
pl = stake if won else -math.ceil(stake * p)
nets[market] = nets.get(market, 0) + pl
comm = 0
for m, net in nets.items():
if net > 0:
comm += math.floor(net * Fraction(rate_pct) / 100 + Fraction(1, 2))
return [sum(nets.values()) - comm, comm]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
def run(args):
try:
return solve(*args)
except Exception as exc:
return 'raised ' + type(exc).__name__
cases = [[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m3', 'back', 2000, '3.91', True],
['m3', 'back', 1000, '4.34', True],
['m2', 'back', 1000, '5.86', True],
['m3', 'lay', 2000, '1.49', False]],
'6.5'),
[12192, 848]),
('variant scenario 1',
([['m1', 'back', 2000, '5.34', True],
['m1', 'back', 100, '2.32', True],
['m2', 'lay', 1000, '7.42', False],
['m2', 'lay', 1000, '6.91', False],
['m2', 'lay', 2000, '4.87', True]],
'6.5'),
[-2091, 573]),
('variant scenario 2',
([['m2', 'back', 2000, '1.48', False],
['m2', 'back', 250, '5.52', False],
['m2', 'back', 1000, '7.14', True],
['m1', 'lay', 777, '7.92', False]],
'6.5'),
[-1740, 253])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m2', 'lay', 777, '7.74', False], ['m2', 'lay', 2000, '6.02', True]], '5'),
[-3237, 0]),
('variant scenario 1', ([['m1', 'lay', 1000, '3.82', False]], '5'), [-2820, 0]),
('variant scenario 2',
([['m3', 'back', 250, '2.61', True], ['m2', 'lay', 250, '7.61', True]], '2'),
[639, 13])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m1', 'lay', 2000, '7.65', False],
['m1', 'back', 100, '7.65', True],
['m2', 'lay', 250, '3.87', True],
['m3', 'back', 250, '7.51', True]],
'5'),
[-10852, 94]),
('variant scenario 1',
([['m1', 'back', 777, '2.00', True],
['m3', 'lay', 100, '3.56', True],
['m1', 'back', 100, '7.20', False]],
'2'),
[761, 16]),
('variant scenario 2', ([['m3', 'lay', 2000, '3.18', True]], '2'), [1960, 40])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m3', 'lay', 1000, '5.16', True],
['m3', 'back', 100, '3.04', True],
['m1', 'back', 2000, '5.82', False],
['m3', 'lay', 250, '3.59', False],
['m1', 'lay', 2000, '3.53', True]],
'2'),
[545, 11]),
('variant scenario 1', ([['m1', 'back', 100, '6.47', True]], '2'), [536, 11]),
('variant scenario 2',
([['m2', 'lay', 2000, '7.84', True],
['m1', 'back', 250, '6.18', False],
['m2', 'back', 100, '7.05', True]],
'2'),
[2303, 52])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m2', 'back', 2000, '1.66', False],
['m3', 'lay', 2000, '7.57', True],
['m2', 'back', 250, '5.20', False],
['m3', 'back', 100, '7.50', True],
['m3', 'lay', 250, '2.34', False]],
'2'),
[19, 46]),
('variant scenario 1', ([['m1', 'lay', 100, '7.27', True]], '2'), [98, 2]),
('variant scenario 2', ([['m1', 'back', 777, '6.40', True]], '5'), [3985, 210])]]
for label, args, expected in cases[N - 1]:
check(label, run(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 |
|---|---|---|---|
| control single back winner | [1900, 100] | [1900, 100] | Passed |
| boundary hedged market nets out | [-500, 0] | [475, 25] | Failed |
| control lay winner | [950, 50] | [950, 50] | Passed |
| boundary losing market no commission | [-500, 0] | [-500, 0] | Passed |
| regression: lay liability | [10322, 718] | [12192, 848] | Failed |
| variant scenario 1 | [-4091, 573] | [-2091, 573] | Failed |
| variant scenario 2 | [-2517, 253] | [-1740, 253] | Failed |
SHA-256 / 7d16abe7989810b5147fd03ac7b16763065ff5a34a4cac7851434b606f750866
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
from fractions import Fraction
import math
N = 1
observations = []
def solve(bets, rate_pct):
nets = {}
for market, side, stake, price, won in bets:
p = Fraction(price)
if side == 'back':
pl = math.floor(stake * (p - 1)) if won else -stake
else:
pl = stake if won else -math.ceil(stake * (p - 1))
nets[market] = nets.get(market, 0) + pl
comm = 0
for m, net in nets.items():
if net > 0:
comm += math.floor(net * Fraction(rate_pct) / 100 + Fraction(1, 2))
return [sum(nets.values()) - comm, comm]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
def run(args):
try:
return solve(*args)
except Exception as exc:
return 'raised ' + type(exc).__name__
cases = [[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m3', 'back', 2000, '3.91', True],
['m3', 'back', 1000, '4.34', True],
['m2', 'back', 1000, '5.86', True],
['m3', 'lay', 2000, '1.49', False]],
'6.5'),
[12192, 848]),
('variant scenario 1',
([['m1', 'back', 2000, '5.34', True],
['m1', 'back', 100, '2.32', True],
['m2', 'lay', 1000, '7.42', False],
['m2', 'lay', 1000, '6.91', False],
['m2', 'lay', 2000, '4.87', True]],
'6.5'),
[-2091, 573]),
('variant scenario 2',
([['m2', 'back', 2000, '1.48', False],
['m2', 'back', 250, '5.52', False],
['m2', 'back', 1000, '7.14', True],
['m1', 'lay', 777, '7.92', False]],
'6.5'),
[-1740, 253])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m2', 'lay', 777, '7.74', False], ['m2', 'lay', 2000, '6.02', True]], '5'),
[-3237, 0]),
('variant scenario 1', ([['m1', 'lay', 1000, '3.82', False]], '5'), [-2820, 0]),
('variant scenario 2',
([['m3', 'back', 250, '2.61', True], ['m2', 'lay', 250, '7.61', True]], '2'),
[639, 13])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m1', 'lay', 2000, '7.65', False],
['m1', 'back', 100, '7.65', True],
['m2', 'lay', 250, '3.87', True],
['m3', 'back', 250, '7.51', True]],
'5'),
[-10852, 94]),
('variant scenario 1',
([['m1', 'back', 777, '2.00', True],
['m3', 'lay', 100, '3.56', True],
['m1', 'back', 100, '7.20', False]],
'2'),
[761, 16]),
('variant scenario 2', ([['m3', 'lay', 2000, '3.18', True]], '2'), [1960, 40])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m3', 'lay', 1000, '5.16', True],
['m3', 'back', 100, '3.04', True],
['m1', 'back', 2000, '5.82', False],
['m3', 'lay', 250, '3.59', False],
['m1', 'lay', 2000, '3.53', True]],
'2'),
[545, 11]),
('variant scenario 1', ([['m1', 'back', 100, '6.47', True]], '2'), [536, 11]),
('variant scenario 2',
([['m2', 'lay', 2000, '7.84', True],
['m1', 'back', 250, '6.18', False],
['m2', 'back', 100, '7.05', True]],
'2'),
[2303, 52])],
[('control single back winner', ([['m1', 'back', 1000, '3.00', True]], '5'), [1900, 100]),
('boundary hedged market nets out',
([['m1', 'back', 1000, '3.00', True], ['m1', 'lay', 1000, '2.50', False]], '5'),
[475, 25]),
('control lay winner', ([['m2', 'lay', 1000, '4.00', True]], '5'), [950, 50]),
('boundary losing market no commission',
([['m1', 'back', 1000, '3.00', False], ['m1', 'lay', 500, '2.00', True]], '5'),
[-500, 0]),
('regression: lay liability',
([['m2', 'back', 2000, '1.66', False],
['m3', 'lay', 2000, '7.57', True],
['m2', 'back', 250, '5.20', False],
['m3', 'back', 100, '7.50', True],
['m3', 'lay', 250, '2.34', False]],
'2'),
[19, 46]),
('variant scenario 1', ([['m1', 'lay', 100, '7.27', True]], '2'), [98, 2]),
('variant scenario 2', ([['m1', 'back', 777, '6.40', True]], '5'), [3985, 210])]]
for label, args, expected in cases[N - 1]:
check(label, run(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 |
|---|---|---|---|
| control single back winner | [1900, 100] | [1900, 100] | Passed |
| boundary hedged market nets out | [475, 25] | [475, 25] | Passed |
| control lay winner | [950, 50] | [950, 50] | Passed |
| boundary losing market no commission | [-500, 0] | [-500, 0] | Passed |
| regression: lay liability | [12192, 848] | [12192, 848] | Passed |
| variant scenario 1 | [-2091, 573] | [-2091, 573] | Passed |
| variant scenario 2 | [-1740, 253] | [-1740, 253] | Passed |
SHA-256 / a19d911fdb4bfef16695f0908eb0062337f7d954e61184176bb73b00773661b6
Verification & scope
Stipulated, bounded toy contract stated in the contract field; not a claim of conformance with any operator, exchange or regulator rule set. 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:50:31.854377+00:00.
Case digest / 3f58cfc9464aec1dcbe380682193b29a4bd57b01732e8079f2ea2d595238c5be