FAILURE MAP
← Case archive

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.

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

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 fixtureActualExpectedOutcome
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 priceinvalidinvalidPassed
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 fixtureActualExpectedOutcome
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 priceinvalidinvalidPassed
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 fixtureActualExpectedOutcome
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 priceinvalidinvalidPassed
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