"+EV" is shorthand for positive expected value, a bet where the true win probability is higher than the price implies. The standard pattern: pick a sharp book as the source of truth, remove the vig to get the implied fair probability, then compare every soft-book price to that fair line. When a soft book offers a number better than fair, that's the edge.
| Step | What we do |
|---|---|
| 1 | Stream all books for the sport over WebSocket |
| 2 | Cache each (event, market, outcome) per book |
| 3 | On each sharp-book update, no-vig the pair (Over + Under) → fair probability |
| 4 | Compare every soft-book price to the fair probability |
| 5 | If edge ≥ threshold and the line is fresh, fire alert |
| 6 | Dedupe so you only fire once per (event, market, line, side, book) |
The bookmaker's vig means the two-side implied probabilities sum to > 100%. To get the true implied probability, divide each side's implied by the sum:
function americanToDecimal(american) {
return american > 0 ? american / 100 + 1 : 100 / -american + 1;
}
function impliedProb(decimal) {
return 1 / decimal;
}
function noVigPair(decimalOver, decimalUnder) {
const pOver = impliedProb(decimalOver);
const pUnder = impliedProb(decimalUnder);
const total = pOver + pUnder;
return {
fair_p_over: pOver / total,
fair_p_under: pUnder / total,
vig: total - 1, // e.g. 0.045 = 4.5% vig
};
}
Node + the ws library. Handles connect, sharp/soft cache, edge detection, Discord webhook delivery, and dedupe.
The single most common false-positive in +EV alerting is a stale soft-book line. The soft book hasn't moved with the market, so it looks +EV, but by the time you place the bet, the book has updated or limited the line. Two guards we use:
last_update per row. The stream pushes this on every row. If now - last_update > 90s on a soft book, skip, the line is stale, the book isn't quoting actively right now.| EV threshold | Hit rate | What you'll see |
|---|---|---|
| ≥ 1.0% | ~300-600 alerts/day | Noisy. Many alerts get washed by the time you bet. Good for a side-by-side dashboard, bad for push notifications. |
| ≥ 2.5% | ~80-150 alerts/day | Sweet spot for most volume operators. High enough to survive the time it takes to place. Discord-friendly cadence. |
| ≥ 5.0% | ~15-30 alerts/day | Rare, usually requires either a lineup-news edge or a book that's badly mispricing. Bigger edge, bigger limit risk (sharp books will limit you). |
push_mode in the connected envelope. If your tier is coalesced (Business 1 s, Enterprise 0.5 s), realize your timing window is the coalesce interval. Scale tier raw is the only path to ~ 300 ms reaction.If you place a bet from an alert, log the price and call POST /v1/clv after the line closes. It returns the closing-line value as a percentage, which is the industry-standard metric for +EV validation. Over a 500-bet sample, CLV should average > 0; if it doesn't, your alerting logic is leaking edge somewhere.
If you'd rather not roll your own, POST /v1/clv takes a list of bets and returns no-vig fair price + edge + CLV per row. You can also call GET /v1/sports/{sport_key}/ev for a snapshot of every current +EV opportunity computed with the same sharp-fallback chain and no-vig math used above. Tradeoff: REST snapshots are 1-2 s old; WebSocket-driven alerts are 300-800 ms. Either works; choose based on how aggressive your edge needs to be.