FAILURE MAP
← Case archive

FA-84371 / Betting odds conversion / Open access

Moneylines below 100 accepted · case 01

A "+50" or "-99" line is expanded instead of rejected.

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

ROOT CAUSE

The guard only rejects non-positive magnitudes.

VERIFIED REPAIR

Reject magnitudes below 100 for both signs.

Unsuccessful approach: Checking the minimum only for positive lines still accepts "-99".

Case contract

Expand an American moneyline ("+150", "150" or "-200"; magnitude at least 100, otherwise "invalid"). Profit per unit is N/100 for positive or unsigned lines and 100/N for negative lines. Return [decimal price with two decimals, reduced fractional profit "a/b", implied probability percent with one decimal], all computed exactly and rounded half up.

Why this case matters

Odds screens show American, decimal, fractional and implied-probability columns for one price.

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(american):
    s = american.strip()
    neg = s.startswith('-')
    n = int(s.lstrip('+-'))
    if n <= 0:
        return 'invalid'
    profit = Fraction(100, n) if neg else Fraction(n, 100)
    dec = 1 + profit
    def fmt(x, places):
        q = 10 ** places
        v = math.floor(x * q + Fraction(1, 2))
        return '%d.%0*d' % (v // q, places, v % q)
    prob = Fraction(n, n + 100) * 100 if neg else Fraction(100, n + 100) * 100
    return [fmt(dec, 2), '%d/%d' % (profit.numerator, profit.denominator), fmt(prob, 1)]
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 underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('+50',), 'invalid'),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('110',), ['2.10', '11/10', '47.6']),
  ('variant scenario 2', ('+1135',), ['12.35', '227/20', '8.1'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('-175',), ['1.57', '4/7', '63.6']),
  ('variant scenario 2', ('846',), ['9.46', '423/50', '10.6'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('+655',), ['7.55', '131/20', '13.2']),
  ('variant scenario 2', ('+800',), ['9.00', '8/1', '11.1'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('+50',), 'invalid'),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('+1106',), ['12.06', '553/50', '8.3']),
  ('variant scenario 2', ('-772',), ['1.13', '25/193', '88.5'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('+50',), 'invalid'),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('-115',), ['1.87', '20/23', '53.5']),
  ('variant scenario 2', ('100',), ['2.00', '1/1', '50.0'])]]
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 underdog['2.50', '3/2', '40.0']['2.50', '3/2', '40.0']Passed
control favourite['1.50', '1/2', '66.7']['1.50', '1/2', '66.7']Passed
boundary even money['2.00', '1/1', '50.0']['2.00', '1/1', '50.0']Passed
boundary unsigned line['3.50', '5/2', '28.6']['3.50', '5/2', '28.6']Passed
boundary below minimum['1.50', '1/2', '66.7']invalidFailed
control half-up decimal['1.13', '1/8', '88.9']['1.13', '1/8', '88.9']Passed
regression: magnitude minimum['1.50', '1/2', '66.7']invalidFailed
regression: magnitude minimum['2.01', '100/99', '49.7']invalidFailed
variant scenario 1['2.10', '11/10', '47.6']['2.10', '11/10', '47.6']Passed
variant scenario 2['12.35', '227/20', '8.1']['12.35', '227/20', '8.1']Passed

SHA-256 / e4ce2f9bf302ac4138410c160bf9a8fe19ea49d13a2ae1373c322ce0b3603b85

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(american):
    s = american.strip()
    neg = s.startswith('-')
    n = int(s.lstrip('+-'))
    if n < 100 and not neg:
        return 'invalid'
    profit = Fraction(100, n) if neg else Fraction(n, 100)
    dec = 1 + profit
    def fmt(x, places):
        q = 10 ** places
        v = math.floor(x * q + Fraction(1, 2))
        return '%d.%0*d' % (v // q, places, v % q)
    prob = Fraction(n, n + 100) * 100 if neg else Fraction(100, n + 100) * 100
    return [fmt(dec, 2), '%d/%d' % (profit.numerator, profit.denominator), fmt(prob, 1)]
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 underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('+50',), 'invalid'),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('110',), ['2.10', '11/10', '47.6']),
  ('variant scenario 2', ('+1135',), ['12.35', '227/20', '8.1'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('-175',), ['1.57', '4/7', '63.6']),
  ('variant scenario 2', ('846',), ['9.46', '423/50', '10.6'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('+655',), ['7.55', '131/20', '13.2']),
  ('variant scenario 2', ('+800',), ['9.00', '8/1', '11.1'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('+50',), 'invalid'),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('+1106',), ['12.06', '553/50', '8.3']),
  ('variant scenario 2', ('-772',), ['1.13', '25/193', '88.5'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('+50',), 'invalid'),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('-115',), ['1.87', '20/23', '53.5']),
  ('variant scenario 2', ('100',), ['2.00', '1/1', '50.0'])]]
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 underdog['2.50', '3/2', '40.0']['2.50', '3/2', '40.0']Passed
control favourite['1.50', '1/2', '66.7']['1.50', '1/2', '66.7']Passed
boundary even money['2.00', '1/1', '50.0']['2.00', '1/1', '50.0']Passed
boundary unsigned line['3.50', '5/2', '28.6']['3.50', '5/2', '28.6']Passed
boundary below minimuminvalidinvalidPassed
control half-up decimal['1.13', '1/8', '88.9']['1.13', '1/8', '88.9']Passed
regression: magnitude minimuminvalidinvalidPassed
regression: magnitude minimum['2.01', '100/99', '49.7']invalidFailed
variant scenario 1['2.10', '11/10', '47.6']['2.10', '11/10', '47.6']Passed
variant scenario 2['12.35', '227/20', '8.1']['12.35', '227/20', '8.1']Passed

SHA-256 / 31a1b4228505199efec473657545286cae3c6e821e38708765d14eb54d0382cb

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(american):
    s = american.strip()
    neg = s.startswith('-')
    n = int(s.lstrip('+-'))
    if n < 100:
        return 'invalid'
    profit = Fraction(100, n) if neg else Fraction(n, 100)
    dec = 1 + profit
    def fmt(x, places):
        q = 10 ** places
        v = math.floor(x * q + Fraction(1, 2))
        return '%d.%0*d' % (v // q, places, v % q)
    prob = Fraction(n, n + 100) * 100 if neg else Fraction(100, n + 100) * 100
    return [fmt(dec, 2), '%d/%d' % (profit.numerator, profit.denominator), fmt(prob, 1)]
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 underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('+50',), 'invalid'),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('110',), ['2.10', '11/10', '47.6']),
  ('variant scenario 2', ('+1135',), ['12.35', '227/20', '8.1'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('-175',), ['1.57', '4/7', '63.6']),
  ('variant scenario 2', ('846',), ['9.46', '423/50', '10.6'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('+655',), ['7.55', '131/20', '13.2']),
  ('variant scenario 2', ('+800',), ['9.00', '8/1', '11.1'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('+50',), 'invalid'),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('+1106',), ['12.06', '553/50', '8.3']),
  ('variant scenario 2', ('-772',), ['1.13', '25/193', '88.5'])],
 [('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
  ('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
  ('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
  ('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
  ('boundary below minimum', ('+50',), 'invalid'),
  ('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
  ('regression: magnitude minimum', ('+50',), 'invalid'),
  ('regression: magnitude minimum', ('-99',), 'invalid'),
  ('variant scenario 1', ('-115',), ['1.87', '20/23', '53.5']),
  ('variant scenario 2', ('100',), ['2.00', '1/1', '50.0'])]]
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 underdog['2.50', '3/2', '40.0']['2.50', '3/2', '40.0']Passed
control favourite['1.50', '1/2', '66.7']['1.50', '1/2', '66.7']Passed
boundary even money['2.00', '1/1', '50.0']['2.00', '1/1', '50.0']Passed
boundary unsigned line['3.50', '5/2', '28.6']['3.50', '5/2', '28.6']Passed
boundary below minimuminvalidinvalidPassed
control half-up decimal['1.13', '1/8', '88.9']['1.13', '1/8', '88.9']Passed
regression: magnitude minimuminvalidinvalidPassed
regression: magnitude minimuminvalidinvalidPassed
variant scenario 1['2.10', '11/10', '47.6']['2.10', '11/10', '47.6']Passed
variant scenario 2['12.35', '227/20', '8.1']['12.35', '227/20', '8.1']Passed

SHA-256 / 6eb3e9f332a76eb3ba39b58cd279d6594bb44c7d7938c77aa364b6d21df18841

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:30.357662+00:00.

Case digest / f32c4c2302d1196933f5ed5cf2bfed22adede4375ee82fdbdb81fa6531bd44ee