diff --git a/data/nfl/bs_pod_over_unders.csv b/data/nfl/bs_pod_over_unders.csv new file mode 100644 index 0000000..aa29ee0 --- /dev/null +++ b/data/nfl/bs_pod_over_unders.csv @@ -0,0 +1,33 @@ +season,speaker,team,pick,line,episode_date,episode_url +2026,Bill Simmons,Philadelphia Eagles,over,10.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Philadelphia Eagles,under,10.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Dallas Cowboys,over,9.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Dallas Cowboys,under,9.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,New York Giants,under,7.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,New York Giants,under,7.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Washington Commanders,under,7.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Washington Commanders,over,7.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Detroit Lions,over,10.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Detroit Lions,under,10.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Green Bay Packers,under,9.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Green Bay Packers,over,9.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Chicago Bears,over,9.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Chicago Bears,under,9.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Minnesota Vikings,under,8.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Minnesota Vikings,under,8.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Tampa Bay Buccaneers,over,8.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Tampa Bay Buccaneers,under,8.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,New Orleans Saints,over,7.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,New Orleans Saints,under,7.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Atlanta Falcons,under,7.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Atlanta Falcons,under,7.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Carolina Panthers,over,6.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Carolina Panthers,over,6.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Los Angeles Rams,under,11.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Los Angeles Rams,over,11.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Seattle Seahawks,over,10.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Seattle Seahawks,over,10.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,San Francisco 49ers,over,9.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,San Francisco 49ers,over,9.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Bill Simmons,Arizona Cardinals,under,3.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house +2026,Cousin Sal,Arizona Cardinals,under,3.5,2026-08-28,https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house diff --git a/evidence/pages/nfl/bs-pod-over-unders.md b/evidence/pages/nfl/bs-pod-over-unders.md new file mode 100644 index 0000000..497c8ca --- /dev/null +++ b/evidence/pages/nfl/bs-pod-over-unders.md @@ -0,0 +1,39 @@ +--- +queries: + - pod_picks: nfl/bs_pod_over_unders_summary.sql + - pod_records: nfl/bs_pod_over_unders_records.sql +title: BS Pod Over/Unders +sidebar_position: 4 +--- + +# 2026 NFC BS Pod Over/Unders + +Bill Simmons and Cousin Sal's regular-season win-total picks for the NFC. `Proj wins` is the current simulation average; `Proj` shows whether that projection is over or under the listed win total. Picks remain open until the regular season is complete. `On track` means the current win total is already on the selected side of the line. + +[Episode transcript](https://podscripts.co/podcasts/the-bill-simmons-podcast/the-annual-nfc-overunders-with-bill-simmons-cousin-sal-and-joe-house) + +## Aggregate Records + + + + + + + + + + + +## Team-by-Team Picks + + + + + + + + + + + + diff --git a/evidence/pages/nfl/index.md b/evidence/pages/nfl/index.md index 28f3dc9..95b6392 100644 --- a/evidence/pages/nfl/index.md +++ b/evidence/pages/nfl/index.md @@ -88,7 +88,7 @@ title: NFL Sim
-๐Ÿˆ [Teams](/nfl/teams) ๐Ÿˆ +๐Ÿˆ [Teams](/nfl/teams) ยท [BS Pod Over/Unders](/nfl/bs-pod-over-unders) ๐Ÿˆ
diff --git a/evidence/pages/nfl/predictions.md b/evidence/pages/nfl/predictions.md index 918c246..50128ed 100644 --- a/evidence/pages/nfl/predictions.md +++ b/evidence/pages/nfl/predictions.md @@ -5,6 +5,7 @@ queries: - past_games_summary_by_team: nfl/past_games_summary_by_team.sql - future_games: nfl/future_games.sql - teams: nfl/all_teams.sql +sidebar_position: 3 --- # Predictions diff --git a/evidence/queries/nfl/bs_pod_over_unders_records.sql b/evidence/queries/nfl/bs_pod_over_unders_records.sql new file mode 100644 index 0000000..3a76ab7 --- /dev/null +++ b/evidence/queries/nfl/bs_pod_over_unders_records.sql @@ -0,0 +1,13 @@ +select + speaker, + count(*) filter (where regular_season_complete and final_result = 'Win') as settled_wins, + count(*) filter (where regular_season_complete and final_result = 'Loss') as settled_losses, + count(*) filter (where regular_season_complete and final_result = 'Push') as settled_pushes, + count(*) filter (where not regular_season_complete) as open_picks, + count(*) filter (where not regular_season_complete and live_status = 'On track') as on_track, + count(*) filter (where not regular_season_complete and live_status = 'Behind') as behind +from src_nfl_bs_pod_over_unders +where season = 2026 + and conf = 'NFC' +group by speaker +order by speaker diff --git a/evidence/queries/nfl/bs_pod_over_unders_summary.sql b/evidence/queries/nfl/bs_pod_over_unders_summary.sql new file mode 100644 index 0000000..e675875 --- /dev/null +++ b/evidence/queries/nfl/bs_pod_over_unders_summary.sql @@ -0,0 +1,40 @@ +with team_picks as ( + select + team, + max(division) as division, + max(line) as win_total, + max(pick) filter (where speaker = 'Bill Simmons') as bill_pick, + max(live_status) filter (where speaker = 'Bill Simmons') as bill_status, + max(pick) filter (where speaker = 'Cousin Sal') as sal_pick, + max(live_status) filter (where speaker = 'Cousin Sal') as sal_status + from src_nfl_bs_pod_over_unders + where season = 2026 + and conf = 'NFC' + group by team +) +select + p.team, + p.division, + p.win_total, + round(r.avg_wins, 1) as proj_wins, + case + when r.avg_wins is null then null + when r.avg_wins > p.win_total then 'over' + when r.avg_wins < p.win_total then 'under' + else 'push' + end as proj, + p.bill_pick, + p.bill_status, + case + when p.bill_status = 'On track' then 1 + when p.bill_status = 'Behind' then -1 + end as bill_status_score, + p.sal_pick, + p.sal_status, + case + when p.sal_status = 'On track' then 1 + when p.sal_status = 'Behind' then -1 + end as sal_status_score +from team_picks p +left join src_nfl_reg_season_summary r on r.team = p.team +order by p.division, p.team diff --git a/evidence/sources/mdsbox/nfl/src_nfl_bs_pod_over_unders.sql b/evidence/sources/mdsbox/nfl/src_nfl_bs_pod_over_unders.sql new file mode 100644 index 0000000..bb6b842 --- /dev/null +++ b/evidence/sources/mdsbox/nfl/src_nfl_bs_pod_over_unders.sql @@ -0,0 +1 @@ +select * from nfl_bs_pod_over_unders diff --git a/transform/models/_nfl_docs.yml b/transform/models/_nfl_docs.yml index 0644be9..ee57831 100644 --- a/transform/models/_nfl_docs.yml +++ b/transform/models/_nfl_docs.yml @@ -65,4 +65,69 @@ models: - not_null - name: win_total tests: - - not_null \ No newline at end of file + - not_null + - name: nfl_bs_pod_over_unders + description: Current-season Bill Simmons and Cousin Sal NFC win-total picks with live settlement status. + tests: + - dbt_utils.unique_combination_of_columns: + combination_of_columns: + - season + - speaker + - team + columns: + - name: season + tests: + - not_null + - name: speaker + tests: + - not_null + - accepted_values: + values: ['Bill Simmons', 'Cousin Sal'] + - name: team + tests: + - not_null + - name: conf + tests: + - not_null + - accepted_values: + values: ['NFC'] + - name: division + tests: + - not_null + - name: pick + tests: + - not_null + - accepted_values: + values: ['over', 'under'] + - name: line + tests: + - not_null + - name: episode_date + tests: + - not_null + - name: episode_url + tests: + - not_null + - name: games_played + tests: + - not_null + - name: scheduled_games + tests: + - not_null + - name: current_wins + tests: + - not_null + - name: current_losses + tests: + - not_null + - name: current_ties + tests: + - not_null + - name: regular_season_complete + tests: + - not_null + - name: live_status + tests: + - not_null + - accepted_values: + values: ['On track', 'Behind', 'Win', 'Loss', 'Push'] \ No newline at end of file diff --git a/transform/models/_sources.yml b/transform/models/_sources.yml index 85ab9a8..29c43c5 100644 --- a/transform/models/_sources.yml +++ b/transform/models/_sources.yml @@ -26,6 +26,8 @@ sources: description: nfl schedule - name: nfl_team_ratings description: nfl team ratings from vegas + - name: bs_pod_over_unders + description: Bill Simmons and Cousin Sal NFL win-total picks - name: ncaaf schema: psa meta: diff --git a/transform/models/nfl/analysis/nfl_bs_pod_over_unders.sql b/transform/models/nfl/analysis/nfl_bs_pod_over_unders.sql new file mode 100644 index 0000000..b0abd29 --- /dev/null +++ b/transform/models/nfl/analysis/nfl_bs_pod_over_unders.sql @@ -0,0 +1,124 @@ +with + picks as ( + select + cast(season as integer) as season, + trim(speaker) as speaker, + trim(team) as team, + lower(trim(pick)) as pick, + cast(line as double) as line, + cast(episode_date as date) as episode_date, + trim(episode_url) as episode_url + from {{ source("nfl", "bs_pod_over_unders") }} + ), + + teams as ( + select team, conf, division + from {{ ref("nfl_ratings") }} + ), + + scheduled_games as ( + select team, count(*) as scheduled_games + from + ( + select home_team as team + from {{ ref("nfl_schedules") }} + where type = 'reg_season' + union all + select visiting_team as team + from {{ ref("nfl_schedules") }} + where type = 'reg_season' + ) schedule_by_team + group by team + ), + + completed_games as ( + select + r.home_team, + r.home_team_score, + r.visiting_team, + r.visiting_team_score + from {{ ref("nfl_schedules") }} s + inner join {{ ref("nfl_latest_results") }} r on r.game_id = s.game_id + where s.type = 'reg_season' + ), + + team_game_results as ( + select + home_team as team, + case when home_team_score > visiting_team_score then 1 else 0 end as wins, + case when home_team_score < visiting_team_score then 1 else 0 end as losses, + case when home_team_score = visiting_team_score then 1 else 0 end as ties + from completed_games + union all + select + visiting_team as team, + case when visiting_team_score > home_team_score then 1 else 0 end as wins, + case when visiting_team_score < home_team_score then 1 else 0 end as losses, + case when visiting_team_score = home_team_score then 1 else 0 end as ties + from completed_games + ), + + actuals as ( + select + team, + sum(wins)::integer as current_wins, + sum(losses)::integer as current_losses, + sum(ties)::integer as current_ties, + count(*)::integer as games_played + from team_game_results + group by team + ), + + metrics as ( + select + p.season, + p.speaker, + p.team, + t.conf, + t.division, + p.pick, + p.line, + p.episode_date, + p.episode_url, + coalesce(a.games_played, 0)::integer as games_played, + coalesce(s.scheduled_games, 0)::integer as scheduled_games, + coalesce(a.current_wins, 0)::integer as current_wins, + coalesce(a.current_losses, 0)::integer as current_losses, + coalesce(a.current_ties, 0)::integer as current_ties, + case + when coalesce(s.scheduled_games, 0) > 0 + and coalesce(a.games_played, 0) >= s.scheduled_games + then true + else false + end as regular_season_complete + from picks p + left join teams t on t.team = p.team + left join scheduled_games s on s.team = p.team + left join actuals a on a.team = p.team + ), + + results as ( + select + *, + case + when regular_season_complete then + case + when pick = 'over' and current_wins > line then 'Win' + when pick = 'under' and current_wins < line then 'Win' + when current_wins = line then 'Push' + else 'Loss' + end + end as final_result + from metrics + ) + +select + *, + case + when regular_season_complete then final_result + when (pick = 'over' and current_wins > line) + or (pick = 'under' and current_wins < line) + then 'On track' + else 'Behind' + end as live_status +from results diff --git a/transform/tests/nfl_bs_pod_over_unders_cardinality.sql b/transform/tests/nfl_bs_pod_over_unders_cardinality.sql new file mode 100644 index 0000000..cce10e5 --- /dev/null +++ b/transform/tests/nfl_bs_pod_over_unders_cardinality.sql @@ -0,0 +1,18 @@ +with + stats as ( + select + count(*) as row_count, + count(distinct team) as team_count, + count(distinct speaker) as speaker_count, + min(season) as min_season, + max(season) as max_season + from {{ ref("nfl_bs_pod_over_unders") }} + ) + +select * +from stats +where row_count <> 32 + or team_count <> 16 + or speaker_count <> 2 + or coalesce(min_season, 0) <> 2026 + or coalesce(max_season, 0) <> 2026 diff --git a/transform/tests/nfl_bs_pod_over_unders_team_speakers.sql b/transform/tests/nfl_bs_pod_over_unders_team_speakers.sql new file mode 100644 index 0000000..835f973 --- /dev/null +++ b/transform/tests/nfl_bs_pod_over_unders_team_speakers.sql @@ -0,0 +1,9 @@ +select + season, + team, + count(*) as row_count, + count(distinct speaker) as speaker_count +from {{ ref("nfl_bs_pod_over_unders") }} +group by all +having count(*) <> 2 + or count(distinct speaker) <> 2