The Proppr EV Bot monitors odds-api.io for value betting opportunities by comparing soft bookmaker odds against sharp bookmaker (pinnacle-like) odds across 100+ sports and markets worldwide.
EV Bot identifies positive expected value (+EV) bets by finding discrepancies between recreational bookmakers (Bet365, William Hill, etc.) and sharp bookmakers (M88, FB Sports). When soft books offer better odds than sharp books, there’s an opportunity for profit.
# From ev_bot.py:461-500def is_line_movement_favorable(market_name, bet_side, previous_hdp, current_hdp): """ Check if line movement is favorable for the bet. Totals: - Line increases (3.5 -> 3.75): Favorable for Over - Line decreases (3.5 -> 3.25): Favorable for Under Spreads: - Positive handicap increases (+1.0 -> +1.5): Unfavorable - Negative handicap increases (-0.5 -> -0.75): Favorable """ market_lower = market_name.lower() side_lower = bet_side.lower() # Totals markets if 'total' in market_lower or 'goals' in market_lower: line_increased = current_hdp > previous_hdp if side_lower == 'over': return line_increased # Higher line = harder to hit = better value elif side_lower == 'under': return not line_increased # Lower line = easier to hit = better value # Spread markets elif 'spread' in market_lower or 'handicap' in market_lower: if previous_hdp >= 0: # Positive handicap # Decrease is favorable (team getting more points) return current_hdp < previous_hdp else: # Negative handicap # Increase is favorable (team giving fewer points) return current_hdp > previous_hdp return True # Unknown market, allow by default
# From ev_bot.py:700-800def process_value_bets(): """ Main processing loop: 1. Fetch odds from API 2. Calculate EV for each market/bookmaker combination 3. Filter by user settings 4. Check for duplicates 5. Format and send alerts """ try: # Fetch current odds from odds-api.io response = requests.get( ODDS_API_BASE_URL, params={ "apiKey": API_KEY, "bookmaker": "Bet365", # Target bookmaker "includeEventDetails": "true" }, timeout=30 ) odds_data = response.json() value_bets = [] for event in odds_data.get('data', []): event_id = event['id'] # Check if sharp odds available sharp_odds = get_sharp_odds(event_id) if not sharp_odds: continue # Calculate EV for each market for market in event.get('markets', []): market_name = market['name'] for bookmaker in market.get('bookmakers', []): if is_excluded_bookmaker(bookmaker): continue for outcome in bookmaker.get('odds', []): soft_odds = outcome['price'] sharp_price = sharp_odds.get(market_name, {}).get(outcome['name']) if not sharp_price: continue ev_pct = calculate_expected_value(soft_odds, sharp_price) if ev_pct and ev_pct >= MIN_EV_THRESHOLD: value_bets.append({ 'event_id': event_id, 'event_name': event['name'], 'market_name': market_name, 'bookmaker': bookmaker['name'], 'bet_side': outcome['name'], 'soft_odds': soft_odds, 'sharp_odds': sharp_price, 'ev_percent': ev_pct, 'match_date': event['commence_time'] }) # Send alerts to users send_value_bet_alerts(value_bets) except Exception as e: logger.error(f"Error processing value bets: {e}")