2026-05-19 · betting math positive ev data quality

Reading +EV outputs honestly: why 0.29% edges are realistic and 50% aren't

We shipped a free no-auth +EV demo at /ev earlier this week. The first version surfaced opportunities like "Mets +4900 at Kalshi, edge +55.76%." That's an absurd number on a liquid moneyline. No real-world edge on a US-licensed sportsbook is 55% on a coin-flip-tier market. The number was a data quality bug, not an opportunity.

This post is about the gap between what a naive devig scanner outputs and what's actually a real opportunity. If you're running your own +EV scanner, integrating ours, or just trying to evaluate a public tool, this is the lens.

What edge sizes are realistic

On a liquid US moneyline market (MLB, NFL, NBA, NHL game outcomes), the typical realistic edge against a Pinnacle no-vig anchor is:

Edge sizeRealismWhat it means
0.1% to 1%Very commonSoft book is half a tick off the sharp anchor. Real but small. Throughput-limited by your account size and book limits.
1% to 3%CommonSoft book is reacting slowly to a line move at the sharp, or running a promotion. Real and worth chasing.
3% to 5%UncommonEither a slow-moving market the soft hasn't updated, or you're catching the soft mid-update. Still real; window may close in seconds.
5% to 10%RareAlmost always indicates the soft book has a stale line. Verify before betting. May get voided by the book post-bet if it's egregious.
10% to 20%Very rareGenuine 10-20% edges exist mainly on alt-lines and niche player props where the soft book hasn't built a model. On main moneylines, suspect data error.
Over 20%Data errorAlmost certainly an event mismatch, stale data, or a market-type mismatch (e.g. matching a series-winner contract against a game ML).

If your scanner is showing 50%+ edges on flagship games, the scanner has a bug. Real edge of that size doesn't exist on liquid moneylines.

The five silent traps

Here are the failure modes that produce inflated edges. We fixed all five in our public demo (see the source for the canonical filter set).

1. Exchange/prediction-market books

Kalshi, Novig, Sporttrade, ProphetX, Polymarket price contracts, not traditional moneylines. The contract on a sports event at Kalshi may resolve on a different underlying event than the sportsbook's game ML. Kalshi might have a contract titled "Yankees Win World Series 2026" priced in pennies (1-99¢); your scanner sees that as Yankees +4900 American odds, devigs against Pinnacle's tonight's-game ML at -145, and produces a +55% edge. The two prices are on different events. The edge is fictional.

Filter: exclude kalshi, novig, sporttrade, prophetx, polymarket from naive cross-book comparisons. The full ParlayAPI /v1/sports/{sport}/ev endpoint still includes them but tags every match with exchange_anchor_warning so you can opt into the additional scrutiny.

2. Stale prices from one book

A book's website briefly shows yesterday's closing line because the page CDN cached it. Your scanner ingests it. Devig says you have +12% edge on Yankees. You bet it. The book voids the bet citing "obvious pricing error" and you waste your time. The edge was the data being stale, not real value.

Filter: reject any opportunity whose source row is older than that source's stale_freshness_s threshold from /v1/meta/per-book-sla. The full endpoint does this; the demo doesn't (yet) but caps absurd edges at 20% as a fallback.

3. Alt-line matched against main line

You're scanning baseball totals. Book A has "Game Total Over 7.5 at -110" and Book B has "First-5-Innings Total Over 7.5 at +180." Same teams, same matchup, same line number, completely different market. Your scanner matches them by team + line and produces a fake arbitrage or fake +EV.

Filter: always match on full market key (totals vs alternate_totals vs totals_1st_5_innings), not just line + team. Our parsers emit canonical market keys; trust them.

4. Player-prop event-ID drift

Mike Trout is listed as the starting CF at one book and the DH at another for the same game. Your prop scanner matches "M. Trout Total Bases" across the two books and devig says you have +8% edge. In reality, one book has Trout listed with the night's projected line, the other still shows his previous game's line because their event_id mapping refresh hasn't fired yet. The "edge" is a clock-skew artifact.

Filter: require commence_time agreement within a 5-minute tolerance for cross-book player-prop comparisons. The full /v1/sports/{sport}/ev does this; if you're rolling your own, this is the most common source of fake prop edges.

5. Devig math on partial markets

Devig requires both sides of a two-way market. If a book has only one side priced (e.g. Pinnacle posts Yankees -145 but no Rangers price because the Rangers price is still pending), naive code falls back to using the un-devigged Pinnacle implied prob as "fair." That overstates Yankee fair-prob by the full overround, producing fake +EV on Yankees-side comparisons.

Filter: never devig from a one-sided observation. If the sharp anchor only has one side, skip the event for the comparison cycle.

What 0.29% looks like in practice

Here's a real opportunity from our demo:

Tampa Bay Rays @ FanDuel, price -116
  book_implied: 53.70%
  pinnacle_devig_fair_prob: 53.99%
  edge: +0.29%

That's not exciting on a single bet. But it's a real +0.29% on a liquid market, and if you stake it 1000 times across the season at $100 a stake, you expect $290 net. Long-run +EV. That's the realistic profile.

Now compare to a fake "+55%" edge: that's not betting math, that's a data integrity failure. Treating it as real costs you bankroll when the bet gets voided or hits a phantom event you didn't intend to bet on.

Practical filter set for any +EV scanner

Whatever scanner you're running (ours, a competitor's, or your own), apply these filters before clicking "place bet":

  1. Edge bucket sanity: if displayed edge is above 5% on a flagship moneyline, look at it with eyes-on. Above 20%, treat as data error until proven otherwise.
  2. Source age: the soft book's last update must be within its stale-freshness threshold. Check /v1/meta/source-quality for the live snapshot.
  3. Market key exact match: don't compare across alt-line / main-line. Don't compare game ML to series ML. Don't compare full-game total to first-half total.
  4. Two-sided devig: only devig when the sharp anchor has both sides priced.
  5. Exchange-book exclusion (or warning): by default, exclude Kalshi/Novig/Sporttrade/ProphetX/Polymarket from naive comparisons unless you've explicitly modeled their contract semantics.
  6. Commence-time agreement: cross-book matches must agree on commence_time within 5 minutes (handles event_id drift).
Why we cap our demo at 20%. Even with the five filters above, occasional data errors slip through (a parser falling behind, a partial market we haven't tagged, a new exchange-style book we haven't classified). The 20% cap is a fallback: anything above is suppressed in the public demo regardless of its source. The authenticated /v1/sports/{sport}/ev endpoint returns the raw signal with structured warnings so customers can apply their own thresholds.

The honest pitch

Real +EV is small and accumulates. The scanner's job is to find the 0.5% edges that are real, not the 50% edges that look exciting. A scanner that shows you 0.29% / 0.7% / 1.2% / 2.1% opportunities is healthy. A scanner that shows you double-digit edges on flagship games is either broken or showing you exchange/prediction-market noise.

If you're evaluating ParlayAPI against another vendor, look at the edge-size distribution on a slow Tuesday night. If they're claiming 5%+ edges across the slate, ask where the filters are. If they're claiming 0.3%-3% edges with the occasional 5%+ outlier (which can be real, especially around line moves), they're doing the math right.

Try ours at /ev or the rate-limited try endpoint at /v1/try/{sport}/ev. For full access including spreads, totals, and player props with all six filters enabled, sign up free at /signup.

All posts · No-vig CLV explained · Value-hunting trifecta · Live +EV