Bettor

How the “best lines” are chosen

What this app optimizes for

Bettor does not predict winners. It line-shops across a fixed set of sportsbooks and ranks outcomes where (1) the price looks “sharp” in a simple sense and (2) you get a better number at one book than the average of the books that post that line.

Where the numbers come from

Live odds and schedules come from The Odds API. Soccer uses moneyline (h2h) markets in bulk; NBA, NFL, and MLB use player prop markets (and MLB home-run markets) per game, within API limits.

Which books count

Only these books are included: DraftKings, FanDuel, and Fanatics. Lines from other books are ignored.

When a line becomes a candidate

For each outcome (a team on the moneyline, or a specific player prop side), the app requires at least two of those books to post a price. If only one book has it, that outcome is skipped so you can actually compare prices. The next section explains exactly how best_decimal_odds is chosen from the books that do qualify.

How best_decimal_odds is calculated

The Odds API returns each line as a numeric price on each outcome. Bettor requests decimal odds, so that field is treated as a decimal odds quote (e.g. 2.10 means you win 2.10× your stake including stake return — higher is a “better” price for the bettor on that side).

The backend walks the event payload book by book, but only for the allowed bookmakers listed above. For each bet type:

For every posting that survives grouping, the raw price is parsed as a float. Entries are dropped if the value is missing, not a number, or ≤ 1.0 (invalid as decimal odds for a bet).

For one logical bet (one row in the UI), let the surviving decimal quotes from different books be d1, d2, … dn with at least two books (n ≥ 2). Then:

In words: best_decimal_odds is the largest decimal odds among the allowed books that posted that exact outcome — i.e. the longest price you can get without leaving those three books. The field best_book is whichever book offered that maximum (if two books tie on the same number, the implementation picks one deterministically).

The value shown in the API/JSON is best_decimal_odds rounded to three decimal places. Implied probability and rank score use the same underlying best price before that display rounding.

Separately, average_decimal_odds (used for “Edge %”) is the arithmetic mean of those same per-book decimals, not the best price — so edge measures how far above the mean your best book sits.

Implied probability

“Implied %” is the naive conversion from your best decimal price:

This is not a de-vigged or market-wide fair probability — it is just the break-even rate implied by the single best price you can bet.

Edge % (line shopping)

“Edge %” measures how much better the best price is than the average of your books on that exact outcome:

Rank score (how lines are ordered)

Each candidate line gets a rank score that blends favoring a higher implied probability (tighter favorites / shorter prices in that naive sense) and a higher edge % (more disagreement between books on your side):

Within each game, lines are sorted by rank_score descending. The app keeps the top three per game by default.

Minimum implied filter (and fallback)

First, only lines with implied probability (from the best decimal) above a league-specific floor are kept: ≥ 52% for most sports, ≥ 30% for MLB. If that would show nothing useful for the day, the app relaxes once and recomputes — non-MLB lines use ≥ 50%, MLB lines ≥ 28%. You’ll see a banner when that fallback runs.

Disclaimer

This is a tool to compare posted prices, not betting advice. Odds change; past results don’t guarantee future outcomes. Gamble only what you can afford to lose.