FA-84551 / Betting odds conversion / Open access
Liability computed from the unrounded lay stake or the full price · case 01
The liability shown differs from what the exchange will reserve for the placed lay.
ROOT CAUSE
Liability is computed as lay_stake * lay_price.
VERIFIED REPAIR
Use the rounded lay stake times lay_price - 1.
Unsuccessful approach: Using the unrounded lay stake reports a liability for a stake that cannot be placed.
Case contract
Green-up a matched back bet by laying at a later price (both > 1, else "invalid"). The equal-profit lay stake is back_stake * back_price / lay_price rounded half up to a cent. Using that rounded lay stake: liability = lay_stake * (lay_price - 1), profit if the selection wins = back_stake * (back_price - 1) - liability, profit if it loses = lay_stake - back_stake. Money values are exact then rounded half up to cents. Return [lay_stake, liability, profit_win, profit_lose].
Why this case matters
Exchange trading tools compute hedge stakes that equalise profit across outcomes.
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(back_stake, back_price, lay_price):
bp = Fraction(back_price)
lp = Fraction(lay_price)
if bp <= 1 or lp <= 1:
return 'invalid'
def cents(x):
return math.floor(x + Fraction(1, 2))
lay = cents(back_stake * bp / lp)
liability = lay * lp
win = back_stake * (bp - 1) - liability
lose = lay - back_stake
return [lay, cents(liability), cents(win), cents(lose)]
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 price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (1000, '2.13', '3.37'), [632, 1498, -368, -368]),
('regression: liability basis', (777, '2.19', '3.53'), [482, 1219, -295, -295]),
('variant scenario 1', (2500, '4.19', '5.37'), [1951, 8526, -551, -549]),
('variant scenario 2', (777, '2.40', '4.13'), [452, 1415, -327, -325])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (333, '3.71', '6.08'), [203, 1031, -129, -130]),
('variant scenario 1', (333, '3.42', '3.87'), [294, 844, -38, -39]),
('variant scenario 2', (777, '6.37', '6.02'), [822, 4126, 46, 45])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (2500, '4.93', '3.75'), [3287, 9039, 786, 787]),
('variant scenario 1', (333, '7.71', '5.28'), [486, 2080, 154, 153]),
('variant scenario 2', (777, '3.75', '6.14'), [475, 2442, -305, -302])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (1000, '4.61', '3.77'), [1223, 3388, 222, 223]),
('variant scenario 1', (1000, '6.97', '1.50'), [4647, 2324, 3647, 3647]),
('variant scenario 2', (333, '6.28', '6.70'), [312, 1778, -20, -21])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (2500, '6.61', '2.17'), [7615, 8910, 5115, 5115]),
('regression: liability basis', (777, '2.82', '6.91'), [317, 1873, -459, -460]),
('variant scenario 1', (777, '7.11', '1.39'), [3974, 1550, 3198, 3197]),
('variant scenario 2', (2500, '2.16', '1.35'), [4000, 1400, 1500, 1500])]]
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 price shortened | [1500, 3000, -1000, 500] | [1500, 1500, 500, 500] | Failed |
| control price drifted | [500, 2000, -1000, -500] | [500, 1500, -500, -500] | Failed |
| boundary no movement | [1000, 2500, -1000, 0] | [1000, 1500, 0, 0] | Failed |
| boundary invalid price | invalid | invalid | Passed |
| regression: liability basis | [632, 2130, -1000, -368] | [632, 1498, -368, -368] | Failed |
| regression: liability basis | [482, 1701, -777, -295] | [482, 1219, -295, -295] | Failed |
| variant scenario 1 | [1951, 10477, -2502, -549] | [1951, 8526, -551, -549] | Failed |
| variant scenario 2 | [452, 1867, -779, -325] | [452, 1415, -327, -325] | Failed |
SHA-256 / 473fe1dd6e407a273201619ffcde9215956e494673f81617dfdb842af82757c5
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(back_stake, back_price, lay_price):
bp = Fraction(back_price)
lp = Fraction(lay_price)
if bp <= 1 or lp <= 1:
return 'invalid'
def cents(x):
return math.floor(x + Fraction(1, 2))
lay = cents(back_stake * bp / lp)
liability = back_stake * bp / lp * (lp - 1)
win = back_stake * (bp - 1) - liability
lose = lay - back_stake
return [lay, cents(liability), cents(win), cents(lose)]
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 price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (1000, '2.13', '3.37'), [632, 1498, -368, -368]),
('regression: liability basis', (777, '2.19', '3.53'), [482, 1219, -295, -295]),
('variant scenario 1', (2500, '4.19', '5.37'), [1951, 8526, -551, -549]),
('variant scenario 2', (777, '2.40', '4.13'), [452, 1415, -327, -325])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (333, '3.71', '6.08'), [203, 1031, -129, -130]),
('variant scenario 1', (333, '3.42', '3.87'), [294, 844, -38, -39]),
('variant scenario 2', (777, '6.37', '6.02'), [822, 4126, 46, 45])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (2500, '4.93', '3.75'), [3287, 9039, 786, 787]),
('variant scenario 1', (333, '7.71', '5.28'), [486, 2080, 154, 153]),
('variant scenario 2', (777, '3.75', '6.14'), [475, 2442, -305, -302])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (1000, '4.61', '3.77'), [1223, 3388, 222, 223]),
('variant scenario 1', (1000, '6.97', '1.50'), [4647, 2324, 3647, 3647]),
('variant scenario 2', (333, '6.28', '6.70'), [312, 1778, -20, -21])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (2500, '6.61', '2.17'), [7615, 8910, 5115, 5115]),
('regression: liability basis', (777, '2.82', '6.91'), [317, 1873, -459, -460]),
('variant scenario 1', (777, '7.11', '1.39'), [3974, 1550, 3198, 3197]),
('variant scenario 2', (2500, '2.16', '1.35'), [4000, 1400, 1500, 1500])]]
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 price shortened | [1500, 1500, 500, 500] | [1500, 1500, 500, 500] | Passed |
| control price drifted | [500, 1500, -500, -500] | [500, 1500, -500, -500] | Passed |
| boundary no movement | [1000, 1500, 0, 0] | [1000, 1500, 0, 0] | Passed |
| boundary invalid price | invalid | invalid | Passed |
| regression: liability basis | [632, 1498, -368, -368] | [632, 1498, -368, -368] | Passed |
| regression: liability basis | [482, 1220, -295, -295] | [482, 1219, -295, -295] | Failed |
| variant scenario 1 | [1951, 8524, -549, -549] | [1951, 8526, -551, -549] | Failed |
| variant scenario 2 | [452, 1413, -325, -325] | [452, 1415, -327, -325] | Failed |
SHA-256 / b1aebc76f2abecef87cdcac0983a65444c33a52bcdbf8b1bec457ca86a7be037
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(back_stake, back_price, lay_price):
bp = Fraction(back_price)
lp = Fraction(lay_price)
if bp <= 1 or lp <= 1:
return 'invalid'
def cents(x):
return math.floor(x + Fraction(1, 2))
lay = cents(back_stake * bp / lp)
liability = lay * (lp - 1)
win = back_stake * (bp - 1) - liability
lose = lay - back_stake
return [lay, cents(liability), cents(win), cents(lose)]
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 price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (1000, '2.13', '3.37'), [632, 1498, -368, -368]),
('regression: liability basis', (777, '2.19', '3.53'), [482, 1219, -295, -295]),
('variant scenario 1', (2500, '4.19', '5.37'), [1951, 8526, -551, -549]),
('variant scenario 2', (777, '2.40', '4.13'), [452, 1415, -327, -325])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (333, '3.71', '6.08'), [203, 1031, -129, -130]),
('variant scenario 1', (333, '3.42', '3.87'), [294, 844, -38, -39]),
('variant scenario 2', (777, '6.37', '6.02'), [822, 4126, 46, 45])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (2500, '4.93', '3.75'), [3287, 9039, 786, 787]),
('variant scenario 1', (333, '7.71', '5.28'), [486, 2080, 154, 153]),
('variant scenario 2', (777, '3.75', '6.14'), [475, 2442, -305, -302])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (1000, '4.61', '3.77'), [1223, 3388, 222, 223]),
('variant scenario 1', (1000, '6.97', '1.50'), [4647, 2324, 3647, 3647]),
('variant scenario 2', (333, '6.28', '6.70'), [312, 1778, -20, -21])],
[('control price shortened', (1000, '3.00', '2.00'), [1500, 1500, 500, 500]),
('control price drifted', (1000, '2.00', '4.00'), [500, 1500, -500, -500]),
('boundary no movement', (1000, '2.50', '2.50'), [1000, 1500, 0, 0]),
('boundary invalid price', (1000, '1.00', '2.00'), 'invalid'),
('regression: liability basis', (2500, '6.61', '2.17'), [7615, 8910, 5115, 5115]),
('regression: liability basis', (777, '2.82', '6.91'), [317, 1873, -459, -460]),
('variant scenario 1', (777, '7.11', '1.39'), [3974, 1550, 3198, 3197]),
('variant scenario 2', (2500, '2.16', '1.35'), [4000, 1400, 1500, 1500])]]
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 price shortened | [1500, 1500, 500, 500] | [1500, 1500, 500, 500] | Passed |
| control price drifted | [500, 1500, -500, -500] | [500, 1500, -500, -500] | Passed |
| boundary no movement | [1000, 1500, 0, 0] | [1000, 1500, 0, 0] | Passed |
| boundary invalid price | invalid | invalid | Passed |
| regression: liability basis | [632, 1498, -368, -368] | [632, 1498, -368, -368] | Passed |
| regression: liability basis | [482, 1219, -295, -295] | [482, 1219, -295, -295] | Passed |
| variant scenario 1 | [1951, 8526, -551, -549] | [1951, 8526, -551, -549] | Passed |
| variant scenario 2 | [452, 1415, -327, -325] | [452, 1415, -327, -325] | Passed |
SHA-256 / f786aeae745ffae750132f68c438dcb4180a8130a34be52412c9c55f30592423
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.891192+00:00.
Case digest / 0647e248e2f777b51d76af7745ada7daa9d3f437f2e3a9d47c999f71a835907f