You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds Action Network MLB public‑betting sentiment and a sharp line‑drift agent to improve signal quality; also blocks pure same‑team stacks so slips aren’t rejected by platforms.
New Features
New action_network_layer.py: fetches game‑level ticket%/money% from Action Network (no auth), daily‑cached; computes RLM (≥15pp) and direction; exposed as market.an_game_sentiment per team.
_SharpFadeAgent: uses AN game‑level RLM as a fallback when no player‑level sharp report exists; batter props only; scales by divergence; requires +1.5pp extra EV.
Replace _TimeValueAgent with _LineDriftAgent: fires when sb_implied_prob − platform implied ≥ 4pp; +1.5 EV bonus when sb_line_gap < −0.25; skips excluded props and cases with no sportsbook data.
Bug Fixes
_are_legs_correlated: blocks slips where all known legs are from the same team (e.g., LAD+LAD or LAD+LAD+LAD); mixed stacks (e.g., LAD+LAD+NYY) still allowed; duplicate player and same player+prop remain blocked.
Written for commit 4a65484. Summary will update on new commits.
Summary by CodeRabbit
Release Notes
New Features
Added Reverse Line Movement (RLM) detection for MLB games using Action Network data
Introduced same-team parlay stack blocking to prevent correlated bets
Launched drift-based bet evaluation replacing time-value analysis
Improvements
Enhanced market enrichment with game-level sentiment signals
Sharpened RLM signal strength computation for batter-prop decisions with stricter EV thresholds
…orrelated()
PrizePicks and Underdog both reject slips where every leg is on the same team.
Previously _are_legs_correlated() only blocked duplicate players — StackSmithAgent
(which groups teammates by pitching matchup) could produce pure LAD+LAD+LAD stacks
that would be voided on platform.
New Rule 3: if len(unique_teams) == 1 and any team appears 2+ times → blocked.
Mixed stacks (LAD+LAD+NYY) are still allowed — the cross-team leg satisfies both
platforms' correlated parlay policy.
7/7 test cases pass:
LAD+LAD (2-leg) → blocked ✅
LAD+LAD+NYY (3-leg) → allowed ✅
STL+DET+STL (3-leg) → allowed ✅
LAD+LAD+LAD (3-leg) → blocked ✅
all-different teams → allowed ✅
duplicate player → blocked ✅
solo leg → allowed ✅
Applies to ALL agents (EVHunter, BullpenAgent, PropCycle, StackSmith, etc.)
…gnal)
- Removes _TimeValueAgent (duplicate of WeatherAgent — dome/temp boosts only, no actual drift)
- Adds _LineDriftAgent: compares sb_implied_prob (DK/FD/BetMGM vig-stripped, already
stamped by sportsbook_reference_layer) vs platform implied from over_american
- Fires when drift >= 4 percentage points (sharp books ahead of UD/PP)
- Secondary signal: sb_line_gap < -0.25 (DFS line set easier than sportsbook) adds +1.5 EV bonus
- Skips props with no Odds API data (sb_implied_prob=0) — no fabricated signal
- Excludes stolen_bases, home_runs, walks, walks_allowed per prop exclusion directive
- Added to _AGENT_CLASSES (agent 15 of 15 active)
- 8/8 logic tests pass
Reviewing files that changed from the base of the PR and between 8611f52 and 4553072.
📒 Files selected for processing (2)
action_network_layer.py
tasklets.py
📝 Walkthrough
Walkthrough
A new action_network_layer.py module fetches MLB game sentiment from Action Network's betting API, parsing Over/Under ticket and money percentages and computing RLM signals with per-day caching. The tasklets.py file integrates this sentiment data into market enrichment, updates parlay correlation rules to block pure same-team stacks, adds a game-level RLM decision path to the Sharp Fade Agent, and replaces the TimeValueAgent with a new LineDriftAgent for drift-based proposition evaluation.
New module that fetches MLB game sentiment from Action Network's publicbetting endpoint. Parses Over/Under tickets and money percentages, computes RLM signals (checking if abs(Over ticket% − Over money%) ≥ 15pp), assigns rlm_direction based on ticket/money comparison, and returns cached results keyed by PT date.
DataHub Market Enrichment tasklets.py
Integrates Action Network sentiment into _is_pre_match() by importing and calling fetch_mlb_game_sentiment(), storing results in market["an_game_sentiment"] with fallback to {} on errors.
Parlay Correlation Rules tasklets.py
Updates _are_legs_correlated() to block slips when all legs with known team belong to the same team and at least two legs are present (pure same-team stack rule), while retaining existing duplicate player and (player, prop_type) combo blocking.
Agent Logic Overhaul tasklets.py
Removes _TimeValueAgent; adds _LineDriftAgent that gates props by sb_implied_prob > 0, computes drift from line gap, applies optional LINE_GAP_BONUS (1.5), and requires EV above threshold + 1.5pp. Extends _SharpFadeAgent.evaluate() with a second game-level RLM decision path using market["an_game_sentiment"] for batter props when player-level data is unavailable. Updates _AGENT_CLASSES registry.
Sequence Diagram
sequenceDiagram
participant Client as Proposition Evaluator
participant TaskLets as TaskLets Engine
participant ANLayer as Action Network Layer
participant ANApi as Action Network API
participant Cache as Per-Day Cache
participant SharpAgent as Sharp Fade Agent
Client->>TaskLets: Request market pre-match enrichment
TaskLets->>ANLayer: fetch_mlb_game_sentiment(date)
ANLayer->>Cache: Check cached data for date
alt Cache Hit
Cache-->>ANLayer: Return cached sentiment
else Cache Miss
ANLayer->>ANApi: GET publicbetting endpoint (MLB scoreboard)
ANApi-->>ANLayer: Event + market data (Over/Under tickets%, money%)
ANLayer->>ANLayer: Parse OUs, compute RLM signals, build per-team entries
ANLayer->>Cache: Store in cache by PT date
Cache-->>ANLayer: Acknowledged
end
ANLayer-->>TaskLets: game_sentiment dict
TaskLets->>TaskLets: Enrich market["an_game_sentiment"]
TaskLets->>SharpAgent: Evaluate proposition
alt Player-level sharp_report exists
SharpAgent->>SharpAgent: Use player report (existing logic)
else No player-level report
SharpAgent->>SharpAgent: Use game-level RLM from an_game_sentiment
SharpAgent->>SharpAgent: Apply batter-props RLM signal, compute divergence EV
end
SharpAgent-->>Client: Bet decision with strength/EV
Loading
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~50 minutes
Possibly related PRs
#229: Modifies tasklets.py's DataHub pre-match and market enrichment logic, directly affected by the new Action Network sentiment integration added in this PR.
Poem
🐰 Action Networks bloom, sentiment now clear,
RLM signals hop through cache each day,
Drift agents replace old time-value cheer,
While same-team stacks now safely fade away,
Sharp fades leap forward, richer data in play! 🎰✨
✨ Finishing Touches📝 Generate docstrings
Create stacked PR
Commit on current branch
🧪 Generate unit tests (beta)
Create PR with unit tests
Commit unit tests in branch fix/agent-activation-pr250
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
We reviewed changes in 8611f52...4553072 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.
The reason will be displayed to describe this comment to others. Learn more.
Using the global statement
It is recommended not to use global statement unless it is really necessary. Global variables are dangerous because they can be simultaneously accessed from multiple sections of a program. This frequently results in bugs. This also make code difficult to read, because they force you to search through multiple functions or even modules just to understand all the different locations where the global variable is used and modified. Read more about why it should be avoided here.
The reason will be displayed to describe this comment to others. Learn more.
Use lazy % formatting in logging functions
Formatting the message manually before passing it to a logging call does unnecessary work if logging is disabled. Consider using the logging module's built-in formatting features to avoid that.
The reason will be displayed to describe this comment to others. Learn more.
Use lazy % formatting in logging functions
Formatting the message manually before passing it to a logging call does unnecessary work if logging is disabled. Consider using the logging module's built-in formatting features to avoid that.
The reason will be displayed to describe this comment to others. Learn more.
Use lazy % formatting in logging functions
Formatting the message manually before passing it to a logging call does unnecessary work if logging is disabled. Consider using the logging module's built-in formatting features to avoid that.
Micro-Learning Topic: Cross-site scripting (Detected by phrase)
Matched on "XSS"
Cross-site scripting vulnerabilities occur when unescaped input is rendered into a page displayed to the user. When HTML or script is included in the input, it will be processed by a user's browser as HTML or script and can alter the appearance of the page or execute malicious scripts in their user context.
OWASP Cross Site Scripting (XSS) Software Attack - OWASP community page with comprehensive information about cross site scripting, and links to various OWASP resources to help detect or prevent it.
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces the action_network_layer.py module for fetching MLB public betting data and integrates it into the betting agents to provide a game-level Reverse Line Movement (RLM) signal. It also replaces the _TimeValueAgent with a new _LineDriftAgent that leverages sharp sportsbook data and updates parlay correlation logic to prevent pure same-team stacks. The review identified critical scale mismatches in the EV calculations for the new agents, a likely lookup failure due to team name formatting, and a logic bug where doubleheader data would overwrite previous games.
The reason will be displayed to describe this comment to others. Learn more.
There is a critical scale mismatch in the EV calculation for the game-level RLM signal. _american_to_implied returns a percentage (0-100), but the formula at line 3111 incorrectly treats it as a fraction (0-1) in the numerator while keeping it as a percentage in the denominator. This will result in incorrect EV values and prevent the agent from firing correctly.
The reason will be displayed to describe this comment to others. Learn more.
The _LineDriftAgent contains multiple scale mismatches. sharp_implied is a fraction (0-1), but platform_implied is a percentage (0-100) because it's derived directly from _american_to_implied. This causes the drift calculation at line 3164 to be incorrect (always negative). Additionally, the ev_pct calculation at line 3176 suffers from the same scale inconsistency between model_prob and platform_implied.
The reason will be displayed to describe this comment to others. Learn more.
The team lookup in an_sentiment will likely fail because prop.get("team") typically returns team abbreviations (e.g., "NYY"), whereas an_sentiment is keyed by full team names (e.g., "new york yankees"). You should use the _ABBREV_TO_FULL mapping to resolve the full name before performing the lookup.
The reason will be displayed to describe this comment to others. Learn more.
The current implementation uses team names as keys in the result dictionary. In the event of an MLB doubleheader, the data for the second game will overwrite the first game for both teams involved. Consider using a unique game identifier or storing a list of game sentiments per team if doubleheaders need to be supported.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary by cubic
Adds Action Network MLB public‑betting sentiment and a sharp line‑drift agent to improve signal quality; also blocks pure same‑team stacks so slips aren’t rejected by platforms.
New Features
action_network_layer.py: fetches game‑level ticket%/money% from Action Network (no auth), daily‑cached; computes RLM (≥15pp) and direction; exposed asmarket.an_game_sentimentper team._SharpFadeAgent: uses AN game‑level RLM as a fallback when no player‑level sharp report exists; batter props only; scales by divergence; requires +1.5pp extra EV._TimeValueAgentwith_LineDriftAgent: fires whensb_implied_prob− platform implied ≥ 4pp; +1.5 EV bonus whensb_line_gap< −0.25; skips excluded props and cases with no sportsbook data.Bug Fixes
_are_legs_correlated: blocks slips where all known legs are from the same team (e.g., LAD+LAD or LAD+LAD+LAD); mixed stacks (e.g., LAD+LAD+NYY) still allowed; duplicate player and same player+prop remain blocked.Written for commit 4a65484. Summary will update on new commits.
Summary by CodeRabbit
Release Notes
New Features
Improvements