Updated: Loading...
Updated: Loading...
Click any district for detailed forecast. Color indicates race rating.
Distribution of seats across 10,000 simulated elections
| District | Incumbent | PVI | Dem % | Rating |
|---|
How the probability of Democratic control has changed over time
Polling data is fetched daily from the VoteHub API. Generic ballot polls and presidential approval polls from the last 180 days are collected. Each poll is weighted by pollster quality (using FiveThirtyEight-style grades: A+ = 3.0, A = 2.7, down to C = 1.0) and recency.
District fundamentals include Partisan Voter Index (PVI) calculated from 2024 presidential results by congressional district (sourced from Wikipedia), and incumbency status for all 435 House seats.
Cook Political ratings are scraped daily from cookpolitical.com. For redistricted states, Cook ratings are used to adjust PVI values where the 2024 presidential baseline may not reflect the new district lines.
The national political environment is inferred from generic ballot polls using PyMC Bayesian inference. The model treats each poll as a noisy observation of the true latent national sentiment:
y_i ~ Normal(μ + house_effect[pollster], σ_i) | Poll observation model |
μ ~ Normal(0, 5) | Latent national environment |
house_effect ~ Normal(0, σ_house) | Pollster bias |
σ_house ~ HalfNormal(2) | Magnitude of house effects |
The prior μ ~ Normal(0, 5) is a weakly informative prior centered at a neutral national environment. The standard deviation of 5 points assigns roughly 95% probability to environments between D+10 and R+10, covering essentially all plausible national environments in modern U.S. elections while letting the polling data drive the posterior.
The posterior is sampled via MCMC with 4 chains × 500 draws each. After sampling, a deterministic adjustment is applied for presidential approval:
μ_adjusted = μ + (-0.3 × 0.3 × net_approval)
This reflects the historical relationship where each point of net presidential approval corresponds to roughly 0.1 points on the generic ballot (with 30% weight given to approval data).
Model parameters were fitted using PyMC on 2018 and 2022 midterm election results (797 total district-races). The training process learned:
The fitted model achieves R² = 0.94 and RMSE = 3.9 points on historical data. Parameters are stored in data/processed/learned_params.json.
For each district, the expected Democratic vote share is:
vote_share = 50 + β_pvi × PVI + β_inc × Inc + μ_region + β_nat × μ_national + ε
Where:
District uncertainty (σ_d) is scaled using a logistic function on |PVI|: safe districts (|PVI| > 15) have less uncertainty (~2 points), while competitive districts have full uncertainty (~4.5 points).
10,000 simulations propagate uncertainty through the entire model. For each simulation:
This produces a distribution of 10,000 possible seat outcomes. The probability of Democratic majority is the fraction of simulations where Dems win ≥218 seats.
The model uses FiveThirtyEight's 10 political regions to capture geographic correlation in election outcomes. During each simulation, a regional effect is sampled for each region, and all districts within that region share the same effect.
| New England | ME, NH, VT, MA |
| Mid-Atlantic | NY, NJ, DE, MD, RI, CT |
| Rust Belt | IL, IN, OH, MI, WI, PA, MN, IA |
| Southeast | FL, GA, NC, VA |
| Deep South | SC, AL, MS, AR, TN, KY, WV, MO |
| Texas Region | TX, OK, LA |
| Plains | ND, SD, NE, KS |
| Mountain | ID, MT, WY, UT, AK |
| Southwest | AZ, NV, NM, CO |
| Pacific | CA, OR, WA, HI |
A GitHub Actions workflow runs daily at 9am ET:
| Generic Ballot Polls | VoteHub API |
| Presidential Approval | VoteHub API |
| District PVI | Wikipedia (2024 results) |
| Race Ratings | Cook Political Report |
| Congressional Maps | U.S. Census TIGER/Line |
This model has several known limitations:
The complete model implementation is open source:
github.com/grantbw4/2026-midterms-forecast
Key files: models/national_environment.py (PyMC inference), models/hierarchical_model.py (Monte Carlo simulation), models/parameter_fitting.py (historical training).