Busabase
Busabase is an open-source (MIT) database and workspace for AI agents — data, docs, skills, and apps in one place. Run it locally, self-host it inside your network, or use Busabase Cloud, and turn age
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證2026 U.S. House, Senate and governor forecast - daily Monte Carlo model, run in GitHub Actions (scorvec.com/midterms)
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗A Monte Carlo forecast of the 2026 U.S. House, Senate and governor elections, with an experimental forecast of 22 state
legislative chambers. It is published at scorvec.com/midterms; the reader-facing
explanation is How the forecast works (web/about.html).
The election was held on 3 November 2026. The forecast was frozen at 17:04 UTC that day, before the first polls closed
(data/final/, with its SHA-256 and commit; see The daily run), and the site now
opens on the results and how the frozen forecast did. results.yml reads the calls and counts, scores the forecast,
writes the results page's data and projects the races still out (live/remaining.js: the frozen forecast's own simulations,
from live/engine.js and data/final/whatif_2026.json at the frozen commit, reweighted by every call and count); the daily
forecast no longer runs.
Each day the model re-reads the polls, refits a national generic-ballot trend from individual polls, blends every race's
polls with a fundamentals prior and simulates the elections 20,000 times with correlated errors: a national miss, a
statewide miss, regional and demographic swings, and each race's own error. The whole run happens in GitHub Actions and
commits its outputs to web/; the pages are static, and every position of the "national mood" slider is precomputed.
| page | what it shows |
|---|---|
web/index.html | the races still out (the front page): each chamber's median outcome and seat range, and every uncalled race's chance from the calls and counts so far |
web/results.html | the results: each chamber as called, the national House vote against every election since 1946, every race next to its frozen forecast, and the scorecard |
web/forecast.html | the final forecast, as frozen: chamber and governor odds, seat distributions, every race and how each moved |
web/about.html | how the forecast works, the track record and every source |
web/legislatures.html | state legislative chambers (experimental), with district maps |
National environment (generic.py, national_mood.py). The generic-ballot trend is fitted to individual polls, not to
other people's averages: a penalised piecewise-linear trend (knots every 14 days), one house effect per pollster
(ridge-shrunk, centred), likely / registered / adult offsets and a partisan-sponsor offset. Each poll sits at the
midpoint of its field period, and the versions of one survey (for example likely and registered voters) count as one
poll. A poll's weight is the square root of its sample size (capped at 1,500), lowered for a pollster whose polls scatter
about the trend more than sampling explains, times the pollster's quality rating squared (score out of 3; a C+ pollster keeps
0.36 of its weight); its precision halves every 60 days of age, and no pollster's polls may carry more than 15 % of today's
value. The approval trend is fitted the same way. The trend is taken as it stands: there is no correction for the direction
of a polling miss.
The size of past misses, in either direction, sets the national error s(L), which grows with the days L to the election
(fitted on the 15 House elections 1996-2024; data/static/national_mood_fit.json). The part of that error that is late
opinion movement is scaled state by state by how much of the vote is already banked (early_vote.py), and so is the
part of each race's own error that is late movement in that race (0.25 pts² a day, measured on 538's raw_polls from how
polls of the same race by different pollsters drift apart over the final two months; data/static/race_move_fit.json).
A state banking at the historical pace keeps its total error; one banking faster gets tighter, one banking slower wider.
House (model.py, run2026.py). Each seat's expected Democratic two-party margin starts from a fundamentals prior:
the district's partisan lean (our own, from precinct returns: 0.75 x its 2024 presidential margin against the nation's +
0.25 x the same for 2020, on the 2026 maps; cd_lean_build.py), incumbency and the nominees' fundraising through June
30 (money.py), fitted on the contested seats of 2018 and 2022 with the same lean on those elections' maps, plus the
national environment. Demographic terms carry the Hispanic and Asian swing since 2024, with Texas, Florida and Western
Hispanic factors, white non-college and urbanization factors; the district polls update those factors
(model.factor_update). Race polls are corrected before they are blended, with the corrections measured on 538's
archive of rated polls (race_poll_calibration.py): sponsored polls are shifted and down-weighted, each pollster's
measured lean is removed, polls with many undecided or third-party respondents and polls from inexperienced pollsters
get more variance, older polls fade (63-day half-life, 150-day window), and several versions of one survey count once.
The blend is a Student-t (robust) update against the fit's own prior sd, so a poll that disagrees with everything else
counts for less until other polls confirm it; the measured poll errors enter as standard deviations (the t scale is
adjusted for the degrees of freedom). A poll by a pollster rated A or better with no sponsor is taken at face value instead:
its error is normal with the t's scale (model.TOP_RATED_SD; ratings as known before each election, neutral in the
backtest).
Senate (senate2026.py). The same pipeline with a prior refitted on every Democrat-v-Republican Senate race
1982-2024, with linear time trends in the lean and incumbency slopes (the Senate has nationalized). Independent
challengers (a wider prior; 12 points more for one with no Democrat on the ballot and at least a quarter of the Republican's
money, see Independent challengers), appointed incumbents and split anti-Republican fields have
their own rules. A common trend estimated across
all of this year's Senate polls brings older polls up to date. Control is counted the way it is settled: 50-50 goes to
the Republicans while the vice president is a Republican, and independent winners are assumed to abstain. The page gives
the chance that each party controls the Senate, the share of simulations in which the independents' choice decides it,
and the answer if they side with either party.
Governors (gov2026.py). A prior fitted on every governor race 1998-2024 - the lean slope with a time trend, elected
and successor incumbents, and the nominees' prior office (gov_quality.py) - with the Senate's poll corrections, poll
errors and blend (polls from VoteHub and the Wikipedia race pages).
Ranked-choice races (rcv.py). In Maine (Senate, both House seats) and Alaska (Senate, House, governor) a poll's own
final round is used where it reports one; a first-round-only poll is converted to an expected final-round margin with
transfer rates measured on the official round-by-round tabulations, and the spread of those rates adds to the race's
polling uncertainty.
Simulation. 20,000 draws with Student-t (5 df) errors: a national draw for the House, a statewide draw shared by the Senate and governor races (correlated 0.3 with the national draw), a state draw, urbanization and demographic-group factors, and each race's own error. A slider position is a shift of the same draws (House margins one for one, Senate and governor races by their fitted national slopes), so moving the slider changes the answer only through the assumption. A forward heating-oil adjustment (below) is added after the poll blend.
State legislatures (stateleg.py, experimental). Seat-by-seat simulations of 22 chambers on the same national draw:
the district's 2024 presidential lean, incumbency and the national environment, a state error shared by both chambers
and a seat error, with multi-member districts, held-over senate seats and tie rules handled per chamber.
Diagnostics that do not feed the forecast. Early and mail ballots by race, party or composition in North Carolina, Florida, Georgia, Pennsylvania and Nevada's Clark and Washoe counties; Texas early-vote turnout (off by default); voter registration in North Carolina and Pennsylvania; same-pollster poll-to-poll changes. Details below.
Changes to the model are scored on leak-free, walk-forward harnesses that run the live code as of past dates with only the information available then. Model notes records the scores, including what was tested and left off.
build_web.BACKTEST_NOTE; computed before the Senate error
re-split below, with a shared statewide miss of 4.2, and with the 538 / Cook district leans the House prior used
before its own lean - on the live-path harness that switch moved the September 15 calls by about a seat, see District
lean). House, each year forecast from the other years' data: on September 15 the
model gave Democrats 227 seats (10-90 % range 202-257) in 2018 against 235 won, and 213 (191-234) in 2022 against 213;
over 12 dates from July to November the result fell inside the 10-90 % range every time. Senate, 2018-2024 at 6 dates
each: the seat count fell inside the 10-90 % range at all 24; in all four cycles the Senate polls missed toward the
Republicans - a direction the model deliberately does not assume.gov_backtest.py): every race 2006-2024 at up to six dates (234 races, 1,086 race-dates, 10 cycles);
see Governor model review for the scores.stateleg_backtest.py): 2018 and 2022 on October 1; see State legislatures.python -m midterms.rcv --backtest): each count predicted from its official first round with
rates fitted on the other counts (web/about.html#rcv).backfill.py): the history lines on the forecast page are the current model run as of
every past date with only the polls released by then, the fundraising reports received, the pollster experience, the
heating-oil price and the early-vote snapshot of that date. python -m midterms.backfill --check reruns today's
reconstruction and compares it with web/data/model.json.Where cycles are compared, significance is judged per election cycle, since a cycle shares one national miss: an exact sign-flip permutation of the cycle means and a race-cluster bootstrap stratified by cycle.
.github/workflows/daily.yml is scheduled for 02:30 UTC (22:30 New York time, after the day's poll releases; GitHub
can start scheduled runs late). It runs python -m midterms.bootstrap, then python -m midterms.weekly --daily:
data/raw/ if they are missing (bootstrap.py, checksums verified).generic.py refits the generic-ballot trend.early_vote.py) and EIA's weekly residential heating-oil price (heating_oil.py).refresh.py).build_web.py: the House, Senate, governor and state-legislature simulations -> web/data/model.json, polls.json,
whatif.json and stateleg_seats.json.web/data/history/model_<date>.json; the reconstructed history (backfill.py, rebuilt only when the
methodology files change), web/data/race_history.json and web/data/summary.json (the site's homepage panel).reports/weekly_<date>.md: which races moved in the past week and why.build_web.py also writes data/cache/forecast_detail.json, the scorable forecast (final.py): every race's chance and the
percentiles 1-99 of its simulated margin, the chambers' seat distributions and the national vote.
Freeze. Once, before the polls close, dispatch daily.yml with freeze: python -m midterms.final freeze copies that
run's detail and model.json into data/final/ with a SHA-256 and the commit, and python -m midterms.scorecard template
writes a row per race into data/results/results_2026.csv. From then on scheduled and dispatcher runs stop
(after_freeze runs one anyway).
Publish gate (weekly.publish_gate). The outputs are held when a race that had polls suddenly has none, the race-poll
count falls by more than 10 %, or the generic-ballot poll count falls - usually a Wikipedia table that stopped parsing. The
workflow then commits only the poll records in data/state/ and fails; force_publish on a manual run overrides it.
Commit. The outputs are snapshotted, then on each push attempt the job resyncs to the newest commit of the branch,
restores the snapshot and commits (no rebase, no merge of stale trees). data/raw/ and data/cache/ persist between runs
through actions/cache. A run takes a few minutes on a normal day and about 20 minutes when the reconstructed history is
rebuilt or the download cache is cold.
Publishing. The website copies the pages and web/data/ from this repository at the commit SHA of main
(site_integration/midterms-sync.yml is a reference copy of that workflow) and refuses a forecast that is broken or
older than the one it has.
Other workflows:
| workflow | what it does |
|---|---|
results.yml | hourly, on demand and on every hand edit of data/results/results_2026.csv or outside_calls.csv (calls a news organisation made first, entered by hand with their source): the calls, counts and national House vote from Wikipedia (results_wiki.py; rows from official returns are kept, a count entered from a news tally keeps its numbers and takes Wikipedia's call), the projection of the races still out (live_obs.py, then node live/remaining.js -> web/data/live.json, live_history.json), the scorecard (scorecard.py, rules in its docstring, -> data/results/scorecard_2026.json) and the results page's data (results_page.py -> web/data/results.json, and the homepage's results_summary.json with the median outcome) |
stateleg-geo.yml | builds the district maps web/data/stateleg_geo/ from Census TIGER/Line when the chamber list or the district leans change |
stateleg-data.yml | one-time builds of the state-legislature tables in data/static/stateleg/ (triggered from a feature branch) |
cd-lean.yml | manual: the congressional-district leans (cd_lean_build.py) on the 2026 maps or on the 2018 and 2022 maps |
district-inputs.yml | manual: the urbanization index, the House map shapes and (for states back on their old lines) the demographic tables on the maps in force for 2026 (demog.cd_blocks); dispatch after a court ruling changes a state's map |
Python 3.11 and network access:
pip install -r requirements.txt
python -m midterms.bootstrap # fixed public inputs into data/raw/ (once)
python -m midterms.weekly --daily # the daily run: fetch, fit, simulate; writes web/data/
python -m http.server -d web # serve the pages, then open the printed address
The first run downloads every source page and takes longer than a daily run. Individual pieces:
python -m midterms.generic # generic-ballot and approval trends -> data/cache/generic_model.json
python -m midterms.build_web # simulate from the cached inputs -> web/data/
python -m midterms.backtest 2022 2022-09-15 # the live House path as of a date in a past cycle, scored
python -m midterms.gov_backtest all # governor harness: every variant, then the report
python -m midterms.stateleg_backtest # state-legislature backtest
python -m midterms.backfill --check # today's reconstruction against web/data/model.json
Tests (tests/) need only the committed files and no network, and take a few seconds; CI runs them with the ruff checks
on every code change (.github/workflows/ci.yml):
pip install pytest
pytest
Switches read from the environment: MIDTERMS_STATELEG (state legislatures, default on), MIDTERMS_STATELEG_KAPPA
(the tested-and-rejected state-poll signal, default off), MIDTERMS_RCV (ranked-choice handling, default on),
TX_EARLY (Texas tracker, default off) and MIDTERMS_FORCE_PUBLISH. Model switches that were tested and left off are
module constants, each documented where it is defined.
| path | what |
|---|---|
midterms/ | the model (Python 3.11; requirements.txt); paths.py defines the data layout |
web/ | the results page, the final forecast, how it works and the state legislatures; web/data/ holds the published outputs |
tests/ | the test suite: every module imports, the early-vote profile and exposures, release dates, simulation pieces |
data/static/ | small inputs derived once from public sources (below); committed |
data/state/ | poll records the daily run accumulates (the feeds only reach back a few weeks); committed |
data/manual/ | hand-entered polls (releases not yet in any feed) and checked corrections to scraped polls, with source URLs |
data/early/ | early-vote diagnostics, written nightly until election night; not used by the forecast or the website |
data/final/, data/results/ | the frozen forecast (final.py), the results as they come in and the scorecard (scorecard.py) |
data/raw/, data/cache/ | downloads and intermediate files; git-ignored, kept between runs by actions/cache |
reports/ | weekly change reports |
site_integration/ | reference copy of the website's sync workflow |
Committed inputs (data/static/):
| file | contents | source / licence |
|---|---|---|
district_groups_2026.csv, state_groups.csv, district_wnc_2026.csv | citizen voting-age population by Hispanic / Asian origin group, white non-college share, per 2026 district and state | U.S. Census Bureau ACS 2020-2024 5-year tables + PUMS, the block equivalency file of the map in force (demog.cd_blocks: the 120th Congress, Missouri on its 119th-Congress lines after the Supreme Court blocked its 2025 map); built by demog.py, the Missouri rows from the Census API's district tables (demog.patch_cd119) |
district_heat_oil_2026.csv, state_heat_oil.csv | share of households heating with fuel oil / kerosene | ACS B25040 (public domain); demog.heat_oil_shares, Missouri as above |
urbanization_2026.csv | urbanization index on the 2026 maps | 538 urbanization index 2022 (CC BY 4.0) carried to the maps in force with Census block-group populations; urban.py |
early_share_2022.json | each state's share of 2022 ballots cast before Election Day | U.S. EAC 2022 EAVS (public domain); early_vote.py |
early_profile.json | the timing profile of pre-Election-Day ballots (W, P of h(tau) = (1 - tau/W)^P) with the derived shares it was fitted to | UF Election Lab national early-vote tables (2024 via Wayback Machine captures; shares only, the tables are not redistributed); python -m midterms.early_vote profile |
fec_june30_{2018,2022,2026}.csv, fec_senate_june30_2026.csv, fec_reports_2026.csv, fec_nominees_2026.csv | candidate fundraising through June 30 (House nominees; Senate nominees for the funded-independent rule) | FEC / OpenFEC (public domain); the House files are built by fec_money.py |
house_national_vote_1946.csv | national House vote by party since 1946 | Wikipedia election pages (CC BY-SA 4.0) |
national_mood_fit.json | fitted coefficients only: the national-vote error s(L) (and the unused directional and approval variants), the cycles used and the late-movement slope | fit of the generic-ballot average to the House vote, 15 cycles 1996-2024 (538 / ABC News averages and poll archives, HuffPost Pollster archives, Gallup approval). The underlying table is not distributed; rebuilds the fit from it |
web/data/districts_2026.topo.json (district shapes) is built by district_shapes.py from Census 2020 cartographic block
groups and the block equivalency file of the map in force (demog.cd_blocks; public domain).
data/state/ holds poll toplines as posted by the feeds (Polling USA, pollresults.org - poll data from The New York Times,
CC BY 4.0), race_poll_seen.csv (the date each race poll first appeared on the forecast page), the Center Square generic
ballot series used to confirm held polls (source_watch.py) and pollster_scores.csv (a numeric score per pollster from
pollresults.org's letter grades; it weights the national generic-ballot and approval polls, not race polls; the grades
themselves are not committed).
Downloaded at run time (never committed): the 538 poll archives and data repository files (CC BY 4.0), FEC results workbooks, MIT Election Lab presidential returns (CC0), VoteHub, Wikipedia (CC BY-SA 4.0), Bluesky posts, pollresults.org, UF Election Lab early-vote counts (CC BY-NC-ND 4.0 - used only to compute each state's scaling, never republished), EIA heating-oil prices (public domain), Florida Division of Elections and Pennsylvania Department of State files (and, when switched on, the Texas Secretary of State's 2026 early-voting county files). Race ratings from Cook, Sabato and Inside Elections are read from Wikipedia's ratings tables and shown for comparison only. Every poll belongs to its pollster.
fetch.py sends one identifying User-Agent, scorvec.com midterm model (+https://scorvec.com/midterms/about.html), which
the bootstrap, registration and early-vote downloads also use. Pages are fetched one at a time with pauses, nothing already
held is downloaded again, and fetch.py prints its request and byte totals per host at the end of the run.
| source | per daily run |
|---|---|
| Wikipedia (~130 race and table pages) | the MediaWiki API's current revision ids, 50 pages per request; only pages edited since the cached copy are downloaded; historical results pages are fetched once |
| pollresults.org race pages (~220) | the sitemap (1 request); a page is downloaded only when its lastmod is on or after the cached copy |
| Bluesky (Polling USA, Political Poll Bot) | posts are cached; each feed is read only back to the first post already held |
| VoteHub API (5 poll types) | 5 requests, a few MB: the API has no date filter or ETag |
| EIA heating oil, UF early vote, Florida statistics files | conditional requests (ETag / Last-Modified): unchanged files come back as 304 |
| Texas SOS early-voting turnout | off unless TX_EARLY=on; then the election index and one county file per early-voting day, each at most once a day (conditional requests); per-voter rosters are never requested |
| Pennsylvania mail ballots (data.pa.gov) | 2026 aggregates only; 2022 and 2024 are queried once and kept |
| NC / PA registration (Mondays) | new weekly NC snapshots only; past years' files are kept |
| 538, FEC, MIT inputs (bootstrap) | once, then kept |
| OpenFEC | never in the daily run; a manual rebuild only (cached per candidate) |
The one-time builders (demog.py, urban.py, district_shapes.py, cd_lean_build.py, stateleg_build.py,
fec_money.py) need large downloads (ACS summary files and PUMS, Census geography, precinct returns; several GB) or an
API key and are not part of the daily run; their docstrings list the exact inputs. The geographic builders also need
GeoPandas (the state-legislature and district-inputs.yml workflows install it), and district_shapes.py runs mapshaper
through npx. Every 2026 district input reads the map through demog.cd_blocks, which places the states whose adopted map a
court blocked (demog.MAP_FROM_CD119: Missouri) on their 119th-Congress lines; cd_lean_build.MAP_STATUS holds the dated
sources and a test keeps the two in step.
| secret | used by | needed for |
|---|---|---|
OPENFEC_API_KEY | fec_money.py, only on a manual run with fec_refresh | re-pulling June 30 fundraising (an api.data.gov key) |
CENSUS_API_KEY | demog.patch_cd119, only in district-inputs.yml | the demographic tables of a state back on its old lines, from the Census API (a free api.census.gov key) |
The daily run needs no secret; it commits with the workflow's own GITHUB_TOKEN.
The sections below record what was measured behind the main choices, including what was tested and left off. The switches named here are module constants or the environment variables listed above; the comment at each definition carries the same evidence.
No directional correction (national_mood.DIRECTION = False). Generic-ballot averages overstated Democrats in 13 of
the 15 House elections 1996-2024, but most of that record comes from older averages built differently. Rebuilt with this
estimator for the seven elections with poll-level data (2012-2024), the average overstated Democrats in 5 of 7 a month
out, by anything from nothing to five points, so no direction is assumed. An approval-adjusted variant
(national_mood.APPROVAL) is built and off.
House effects shrunk lightly (generic.LAMBDA_HOUSE = 1 rather than 4): predicting the next 24 days' polls on 538's
generic archive 2018-2024 at 7 dates, weighted RMSE 2.623 -> 2.551 (23 of 28 cases, 4 of 4 years; not significant on its
own, year-block t 1.95).
Poll weights: quality, age and a cap per pollster (generic.QUALITY_GAMMA = 2, AGE_HALF_LIFE = 60, SHARE_CAP = 0.15). Same harness (538's generic archive 2018-2024, 7 dates a cycle, 3-day release lag): weighted RMSE of the next 24
days' polls and the trend's miss of the national House margin, 2.576 / 3.523 without the three. None of the differences
below is significant (year-block t on four elections, 3 df).
| change | next 24 days' polls (better in: dates, years; p) | margin vs House vote (better in: dates, years; p) |
|---|---|---|
| quality weight (score/3)^2 | 2.574 (12/28, 2/4; 0.78) | 3.388 (14/28, 2/4; 0.52) |
| age weight, 60-day precision half-life | 2.568 (14/28, 1/4; 0.77) | 3.492 (18/28, 3/4; 0.12) |
| 15 % share cap | 2.600 (12/28, 0/4; 0.07) | 3.437 (16/28, 3/4; 0.64) |
| all three | 2.565 (15/28, 2/4; 0.72) | 3.436 (14/28, 2/4; 0.78) |
The quality weight scales each pollster's house-effect ridge by the same factor squared; without that, a down-weighted
pollster's house effect is also pulled toward zero and the next polls get worse at every exponent (2.70 at 2, worse in
4/4 years). Exponents 1, 3 and 4 score 2.572 / 2.580 / 2.596 and 3.452 / 3.337 / 3.286; 2 takes most of the margin gain
before the next polls start to slip, and scores best on the next polls with the other two on. The age weight discounts the
trend's smoothness penalty by the same factor, so the trend through past months keeps its shape (with a fixed penalty,
year-old polls at 1 % of their precision flattened the 2026 trend by up to 2 points, approval by 6). Half-lives of 30, 45
and 90 days score 2.583 / 2.568 / 2.568 with the other two on; a looser trend (LAMBDA_TREND 10 instead of 30: 2.564 /
3.466 alone, 2.600 / 3.373 with the other two) or weekly knots (2.566 / 3.417, 2.611 / 3.368) lean on recent polls by
extrapolating their slope instead. The margin gain of the three together comes from 2018 (3.09 -> 1.94) and 2020 (4.00 ->
3.80); 2024 is worse on both counts (next polls 2.19 -> 2.50, margin 4.10 -> 4.51). The cap is a judgement rule, not a
backtest result. A pollster's share is the summed positive influence of its polls on today's value (the poll page's
"influence on today"), and over the cap all its polls' precision is scaled down until it sits at the cap. Morning
Consult's weekly tracker carried 0.36 of the generic trend on 17 October 2026, and with the quality and age weights on,
the leading pollster of a backtest date carries 0.19-0.83. The cap binds at all 28 backtest dates, on 2-7 pollsters at a
time, and moves the trend by -0.9 to +1.2 points. On 17 October 2026 the three moved the generic trend from D+8.6 to
about D+9.6 and approval from -21.1 to about -22.9, and Morning Consult's share from 0.36 to 0.15 (generic) and from 0.25
to 0.10 (approval).
Governor races are the least nationalized of the three offices, so the fundamentals prior matters most there. The harness
(python -m midterms.gov_backtest all) scores every even-year cycle 2006-2024 at September 1, September 15,
October 1, October 15, November 1 and the day before the election (2006-2016 from October 1 only: 538's raw_polls, the poll
source for those years, holds the last ~60 days; 2018-2024 use 538's full governor poll archive). It runs the live path -
gov2026.Prior, prepare (sponsor shifts and pollster leans fitted on earlier cycles only), collapse_versions, the robust
blend and gov2026.simulate on the shared national draw - with only what was known on the date: polls three days after
their last field day, the prior fitted on the governor races of earlier cycles (1998 onward), and the generic-ballot
average known at that lead (538's average to 2016, this estimator from 2018; no directional correction). The 2026-only
Hispanic / Asian loadings and the heating-oil term are left out. n = 234 races (every D-v-R race of each cycle, polled or
not), 1,086 race-dates, 10 cycles; 984 race-dates had polls. Scores: Brier and log loss of P(D wins), error of the race
mean, the CRPS of the margin, 80 % / 50 % interval coverage. Each entry reads difference (cycles better; permutation p /
bootstrap p), with an exact sign-flip permutation of the cycle means (2^10 assignments) and a race-cluster bootstrap
stratified by cycle (4,000 draws).
Baseline (the prior before these terms): log loss 0.1944, Brier 0.0573, margin MAE 6.11 and RMSE 8.59 points, CRPS 4.39, 80 % intervals cover 76.1 % and 50 % intervals 45.1 % (too narrow), PIT KS 0.121.
| variant | log loss | Brier | CRPS | margin MAE | 80 % cov. |
|---|---|---|---|---|---|
| baseline | 0.1944 | 0.0573 | 4.390 | 6.11 | 0.761 |
| successor incumbents coded as incumbents (parser fix) | -0.0044 (7/10; 0.129 / 0.059) | -0.0017 (7/10; 0.152 / 0.062) | +0.035 (3/10; 0.248 / 0.183) | +0.03 (0.469 / 0.372) | 0.758 |
| lean slope with a time trend | -0.0050 (6/10; 0.061 / 0.018) | -0.0015 (7/10; 0.070 / 0.070) | -0.230 (7/10; 0.025 / 0.000) | -0.32 (0.023 / 0.001) | 0.789 |
| recency weights, half-life 12 years | -0.0006 (6/10; 0.484 / 0.266) | -0.0001 (5/10; 0.676 / 0.578) | -0.039 (9/10; 0.064 / 0.017) | -0.06 (0.041 / 0.004) | 0.762 |
| recency weights, half-life 8 years | -0.0007 (5/10; 0.553 / 0.377) | -0.0001 (5/10; 0.871 / 0.757) | -0.053 (9/10; 0.072 / 0.028) | -0.08 (0.037 / 0.006) | 0.762 |
| fit without races with a 15 %+ third candidate | -0.0007 (3/10; 0.666 / 0.354) | -0.0003 (3/10; 0.484 / 0.249) | -0.021 (6/10; 0.184 / 0.103) | -0.02 (0.328 / 0.228) | 0.766 |
| previous governor race residual | +0.0007 (7/10; 0.965 / 0.569) | +0.0002 (7/10; 0.938 / 0.729) | -0.049 (8/10; 0.160 / 0.086) | -0.07 (0.096 / 0.092) | 0.770 |
| previous race residual, own v inherited | -0.0009 (7/10; 0.383 / 0.468) | -0.0004 (7/10; 0.312 / 0.467) | -0.033 (7/10; 0.441 / 0.338) | -0.04 (0.459 / 0.406) | 0.761 |
| incumbent's own previous residual | -0.0006 (7/10; 0.518 / 0.286) | -0.0003 (8/10; 0.314 / 0.196) | -0.023 (7/10; 0.562 / 0.469) | -0.03 (0.555 / 0.482) | 0.761 |
| prior sd: open v incumbent | +0.0007 (5/10; 0.268 / 0.059) | +0.0002 (6/10; 0.471 / 0.254) | +0.015 (1/10; 0.018 / 0.106) | +0.02 (0.021 / 0.152) | 0.748 |
| prior sd grows with distance from 0 | +0.0021 (3/10; 0.105 / 0.033) | +0.0005 (4/10; 0.439 / 0.221) | +0.055 (2/10; 0.029 / 0.005) | +0.07 (0.035 / 0.009) |
Adopted (gov2026.PRIOR_SPEC = {"lean_t": True, "succ": True, "qual": "all"}; {} restores the baseline prior):
INC_FIX) scored the same within noise and is
not used.gov_quality.py); a sitting governor counts as experienced, and a nominee without an article as not. Symmetric in party
by construction. Fitted: 6.1 points.gov2026.USE_2024, data/static/gov_results_2024.csv) and is the harness's tenth test cycle.About thirty variants were scored, and with ten cycles the smallest attainable permutation p is 0.002, so no single result survives a strict multiple-comparison correction. The adopted terms are the ones that pass on both tests, help in most cycles and have a reason (a measured trend, a coding error, a long-established predictor); everything else stays off.
Measured, not used:
senate2026.GOV_SHARED_MISS 4.2, scaled by the governor national slope) is left as it is: x0.5 and x1.5 each lose on log
loss or coverage.senate2026.SEN_CONST, B_LEAN, C_INC, NAT_SLOPE, PRIOR_SD). Per-cycle fits show the
incumbency coefficient falling about 3.8 points per decade, the lean slope rising about 0.11 and the residual sd falling
about 1.35. The prior has linear time trends in the lean and incumbency slopes, fitted on every D-v-R race 1982-2024, the
national slope of the last 10 cycles and a trend-extrapolated residual sd; at 2026: constant 0.66, lean 0.837,
incumbency 6.7, national 0.795, sd 8.8. Against the pooled 10-cycle fit on the walk-forward harness (Senate 2006-2024, 10
cycles, 1,615 race-dates): log loss -0.0079 (7/10 cycles; sign-flip p 0.061, race bootstrap p < 0.001), Brier -0.0025,
CRPS -0.23; 2014-2024 better in 6/6 cycles, worse in 2006, 2008 and 2012. The gain is largest in cycles whose polls
overstated Democrats; unpolled races show the same time pattern.STATE_SHARED_MISS 3.1, RACE_EXTRA 2.83, SEN_S_NAT). Two settings tuned separately on
the harness, which do not reconcile arithmetically and are documented as such in senate2026.py: each race's blend nets
a lead-dependent shared part (SEN_S_NAT x the national slope, about 2.3 points three weeks out) out of the measured
midterm race-average miss of 4.0, keeping about 3.3 as its own systematic error; the simulation then draws a fixed
3.1-point shared shock (the RMS cycle-wide statewide miss 1998-2022, about zero) and adds 2.83 points of race-level
spread, so a race's systematic spread is about 5.3 points. Against a single 4.2 shared shock (which implied a
correlation between race errors of 0.34 against 0.13 measured) race scores were unchanged and the seat-count PIT sd
moved 0.265 -> 0.299 (uniform 0.289); the deep review had found race-level errors larger than modelled. Governors
keep GOV_SHARED_MISS 4.2 with no extra.MIDTERM_POLL_SYS_K 0.8): the shared per-race Senate polling miss is smaller in midterms (4.1
v 4.7 in presidential years); scaling it by the measured ratio improved log loss in all five midterms 2006-2022 and is
applied in midterm years only.SEN_COMMON_TREND, on): how all of the year's Senate polls have moved together, estimated with each
race's own level and each pollster's lean, shifts every older poll by the shared movement since it was taken.
Walk-forward 2006-2024: log loss -0.0035 (8/10 cycles). The same adjustment taken from the generic-ballot average, or
applied to governor races, made the forecasts worse, so it is used for the Senate only.Four 2026 Senate races have an independent as the main challenger: Nebraska, Idaho and South Dakota with no Democrat on
the ballot, Montana with one (senate2026.IND_SHIFT, IND_SHIFT_SD, IND_MIN_RATIO, SPLIT_PRIOR_MULT). Measured on every
Senate race since 1988 and every governor race since 1998 with no Democrat on the ballot (MIT and Wikipedia results; Sanders
and Mississippi's 2008 special, Democrats in all but label, aside), against the D-v-R prior fitted without the race's cycle
(the Senate form on the cycles before it; the governor prior on the other cycles), at the actual national vote:
| race | June 30 money (x the Republican's) | independent minus Republican | prior | above the prior |
|---|---|---|---|---|
| Orman, Kansas 2014 | $0.63M (0.45x) | -11.1 | -38.5 | +27.4 |
| McMullin, Utah 2022 | $3.25M (0.85x) | -10.9 | -31.6 | +20.8 |
| Osborn, Nebraska 2024 | $1.58M (1.00x) | -6.7 | -31.0 | +24.3 |
| Walker, Alaska governor 2014 | (state filings) | +2.3 | -30.7 | +33.0 |
| Harrington, Arkansas 2020 | $0 | -33.1 | -31.7 | -1.4 |
| O'Hara, Mississippi 2002 | none | -69.2 | -32.3 | -36.8 |
| Spannaus, Virginia 1990 | none | -63.3 | -21.9 | -41.5 |
| Steve Osborn, Indiana 2006 | none | -74.8 | -25.7 | -49.1 |
Same-state Senate and governor errors (senate2026.JOINT_ON = False). Within a cycle, a state's Senate and governor
polling errors correlate 0.58 (95 % 0.46-0.68; 149 state-years, 13 cycles). With the switch on, each race's own error is
split into c x a draw shared by the state's statewide races plus sqrt(sd^2 - c^2) x its own draw (c = 3.68 points on
1998-2022, capped at 0.9 sd), so only the covariance moves. Joint test: the governor harness paired with the Senate
harness, state by state, one joint simulation per pair on the shared national draw; n = 682 pair-dates, 142 state-years,
10 cycles:
| off | on | difference (cycles better; permutation p / bootstrap p) | |
|---|---|---|---|
| log loss, 4 outcomes | 0.3325 | 0.3347 | +0.0023 (5/10; 0.73 / 0.76) |
| Brier, 4 outcomes | 0.1793 | 0.1813 | +0.0020 (4/10; 0.47 / 0.40) |
| Brier of P(split ticket) | 0.0775 | 0.0792 | +0.0016 (5/10; 0.53 / 0.50) |
| mean P(split) (actual 24.9 %) | 27.4 % | 26.1 % | |
| simulated correlation of the pair | 0.31 | 0.56 | (backtest residuals: 0.32) |
No gain: the shared national draw already gives a state's two races a correlation of 0.31, and the backtest residuals show 0.32. The simulated national shock is larger than the realized cycle-wide miss, so it already carries the state part. Each office's seat distribution is a sum over one office's races, so a cross-office covariance cannot move the headlines.
Undecideds toward the trailing candidate (model.UND_TRAIL_K = None). On, each race poll's margin moves by
k x margin x (100 - D - R) / (D + R) before the sponsor lean is removed, with k from model.und_trail_fit(before=cycle)
(538 raw_polls, Senate and governor, non-partisan, last 60 days, cycle fixed effects; -0.22 when fitted on every cycle). Against the
model as it is:
| harness | n | log loss | Brier | CRPS | margin MAE | 80 % cov. |
|---|---|---|---|---|---|---|
| governors | 234 races, 1,086 race-dates | -0.0003 (2/10; 0.99 / 1.00) | +0.0001 (3/10; 0.51 / 0.72) | +0.18 (2/10; 0.008 / <0.001) | +0.24 (0.014 / <0.001) | 79.6 -> 76.0 % |
| Senate (walk-forward prior) | 326 races, 1,528 race-dates | +0.0031 (1/10; 0.006 / <0.001) | +0.0006 (2/10; 0.031 / 0.002) | +0.26 (0/10; 0.002 / <0.001) | +0.34 (0/10; 0.002 / <0.001) | 79.6 -> 77.1 % |
It makes the race means worse in both offices: they are already too close to 50-50, and the switch pulls them further in. It is not a party lean (mean shift of the race means +0.03 points for governors, +0.05 for the Senate).
Pollster shared error (model.POLLSTER_SHARED_ERROR = None). A pollster's house error is shared by all of its polls
in a race, so four polls from one firm are not four independent reads. With tau set, each poll's error in the robust blend
splits into its own part and a pollster-race part common to that firm's polls, integrated out exactly; each poll's
marginal variance is unchanged. tau is fitted on 538's raw_polls (race_poll_calibration.pollster_shared_sd: same-pollster
v different-pollster pairs at the same time gap, lean-corrected, net of sampling): tau = 2.8 points. On the leak-free
backtests (Senate 2018-2024, governors 2018-2022, House 2018 and 2022; six dates from September 1 to November 1; tau from
earlier cycles) it changed nothing measurable: log loss Senate +0.0002 (95 % interval -0.0014 to +0.0016), governors
-0.0006 (-0.0024 to +0.0008), House +0.0001 (-0.0001 to +0.0003).
Directional state corrections (model.STATE_DIRECTION = False). State polling misses persist net of the national miss
(next-cycle slope 0.24, 538 raw_polls 1998-2022) and statewide polls ran too Democratic in states with more white
non-college voters (1.17 points per state sd). Both are measured and implemented; like the national rule, the live model
assumes no direction and widens the state-level error instead.
A judgment term, not backtested (no past cycle had a comparable price shock): the change in retail heating oil over the
past year, times each district's (state's) share of oil-heated homes, times a typical household's 650 gallons, at 0.78
generic-ballot points per dollar of gasoline spread over 750 gallons, times the part of the change not yet in the race's
polls, against the president's party (model.heating_oil_shift; a fall enters with the opposite sign). The unpriced part is
race-specific (model.unpriced_change): the change since the race's newest poll was fielded counts in full, the change before
it at half (HEAT_UNPRICED); a race without polls takes half of the whole change. The price is EIA's weekly Maine No. 2
heating oil residential price; EIA's survey runs from October to March, and outside the season the last published week is
held. The reconstructed history evaluates it at each past date.
stateleg.py forecasts control of 22 chambers - both chambers in Michigan, Minnesota, Wisconsin, Arizona, Pennsylvania,
New Hampshire, North Carolina, California and Kentucky; the Georgia, Iowa and Texas Houses; and the Texas Senate - inside
the daily run (MIDTERMS_STATELEG, default on; workflow input stateleg). It writes state_legislatures into
web/data/model.json and the per-seat file web/data/stateleg_seats.json; web/legislatures.html shows them.
Seat model. Each seat's expected Democratic two-party margin is a + beta * lean + c * inc + g * E: the district's
2024 presidential margin minus the national one (lean), incumbency (+1 / -1 / 0) and the model's national environment E.
The simulation adds g x the House's national draw, a state error shared by both chambers (partly chamber-specific) and a
t5 seat error. Multi-member districts (AZ House, NH House) elect their top-k candidates (party share +- the candidate's
own deviation from its slate, sd fitted, plus an incumbency bonus); a party with fewer nominees than seats can win at most
that many. Seats with only one major party on the ballot are fixed. Staggered senates (PA and WI: odd districts up; CA,
KY and TX: the seats elected in 2024 are held over) add the held-over seats by their current holders. Ties: the MI and PA senates go to the party of the 2026 governor winner (the lieutenant governor), the
NC Senate to the Democrats (Lt Gov Hunt), elsewhere counted as shared.
Fitted parameters (data/static/stateleg/params.json; python -m midterms.stateleg fit): incumbency c = 6.0 points,
national slope g = 0.77, 2-year state swing error 4.8 (state error 3.4), between-chamber correlation 0.62 - Klarner returns
1972-2022, 42,851 consecutive same-map district pairs; lean slope beta = 0.91, intercept a = 2.9, seat error 5.8 - 2018
(2016 lean) and 2022 (2020 lean) results; slate sd 8.6 % of the party mean (11,276 multi-member slates).
Backtest (python -m midterms.stateleg_backtest, data/static/stateleg/backtest.json): 2018 and 2022 on October 1,
Klarner parameters from earlier elections only, the lean slope from the other cycle, E = the 538 generic-ballot average with
no correction, who is on the ballot from the candidate records. The original 14 chambers (24 chamber-elections with full
district data): control Brier 0.115 against 0.250 for "the current majority holds", called right in 20 of 24 (the
current-majority rule: 18), seat count inside the 80 % range 23 of 24. The Georgia, Iowa and Texas Houses (5 chamber-elections
with complete district data): all called right, as the current-majority rule also was; Brier 0.007; seat count inside the
80 % range 5/5. The California and Kentucky chambers and the Texas Senate (10 chamber-elections): all called right, as the
current-majority rule; seat count inside the 80 % range 8 of 10. The daily run publishes these per-group scores from
backtest.json (stateleg_backtest.published, state_legislatures.backtest in model.json), and about.html and
legislatures.html quote them from there. No published forecaster's chamber calls were scored (none available under a
usable licence).
State-poll signal (tested, not adopted; MIDTERMS_STATELEG_KAPPA=on restores it). The legislative state swing residual
tracks the state's statewide polling miss (slope -0.31, se 0.05; 360 state-cycles 1998-2020), and a fitted link kappa x
the state's Senate / governor surprise (kappa 0.074, se 0.024) changed seat log loss by +0.0005 (2018) and +0.0003 (2022)
and the original 14 chambers' control Brier 0.115 -> 0.119 (arm "state" of backtest.json): no measurable gain.
Coverage. stateleg_scan.py reads every 2026 chamber page (data/static/stateleg/coverage_scan.csv) and checks the
candidates (stateleg_scan.check -> pending_check.csv): a chamber is added only when the page has a general-election
slate for every seat up, the seats up match the 2024 rule, every district has a lean on the current map, and the
one-party seat count is plausible against 2024 (MEDSL) and 2022 (Klarner). Held back: CT Senate and House (2024 MEDSL party
coding under fusion voting gives no usable comparator), UT and WV Senates (one-party seats far below 2022 / 2024), IA Senate
(a district missing from the page), WA and OR Senates (lean check failed: rms 4.0 and 8.9).
Approximations. NH House: candidates are not listed on Wikipedia, so every seat is contested by full slates, floterial
districts are simulated as ordinary multi-member districts, and the 2018 backtest has no NH House. MI Senate: no 2024
Senate election, and the 2026 court-ordered redraw of the Detroit-area districts is not in the Census TIGER files yet;
leans are VEST 2020 precincts moved to 2024 by each House district's measured swing on the 2022 map (the same method
reproduces known 2024 Senate leans with rms 0.3-1.8 in PA, GA, IA, TX, NC). PA odd Senate seats use the same method on the
current map. MN Senate candidates are not on Wikipedia: incumbents are assumed to run unless listed as retiring. Georgia
House nominees come from the Georgia Secretary of State's official primary and runoff results (ga_sos.py), cross-checked
against Wikipedia and Open States (ga_house_crosscheck.csv).
Data (stateleg_build.py, run in GitHub Actions by stateleg-data.yml; downloads cached, never committed):
| file | contents | source |
|---|---|---|
stateleg/lean_2026.csv | 2024 president by 2026 district (MEDSL precinct rows joined to the same precinct's legislative labels; MN / WI senates from nested House districts; AZ House = LDs; MI Senate / PA odd seats as above) + a spatial cross-check | MEDSL 2024 precinct returns (CC0), VEST 2020 (CC BY 4.0), TIGER (public domain) |
stateleg/lean_hist.csv | 2016 president on the 2018 maps, 2020 president on the 2022 maps | VEST 2016 / 2020 x TIGER 2018 / 2022; NH House by town names x MEDSL 2022 labels |
stateleg/medsl_results.csv.gz | candidate-level legislative results 2022, 2024 | MEDSL (CC0) |
stateleg/klarner.csv.gz | Klarner returns aggregated to district-elections, all states 1972-2022 | Klarner, doi:10.7910/DVN/FJOGJB (CC0) |
stateleg/openstates_current.csv | sitting legislators | Open States people (CC0) |
stateleg/qc.json, stateleg/geo_audit.json | coverage, cross-checks, map-change audit; the district-map join audit (the map build fails on a gap) | - |
web/data/stateleg_geo/{ST}.topo.json, index.json | district maps, one TopoJSON per state (stateleg_geo.py, stateleg-geo.yml) | U.S. Census Bureau TIGER/Line 2025 state legislative districts (public domain); large water cut with the Census 2024 cartographic state outline; NH floterials from MEDSL 2024 precinct labels |
Candidates and retirements are read each day from the Wikipedia 2026 chamber pages (stateleg_wiki.py, cached like every
page). On the maps, districts are shaded by the chance a Democrat wins the seat (multi-member: the expected Democratic
share), hatched where only one party is on the ballot and dotted where the seat is not up in 2026. Boundaries are the
TIGER/Line 2025 districts (the maps in force for 2026), except the Michigan Senate's court-ordered Detroit-area redraw,
which is flagged on its map; each New Hampshire floterial is drawn as the outline of the base districts whose towns vote
in it.
cd_lean_build.py builds the House prior's district lean from public precinct returns: 0.75 x the most recent presidential
two-party margin minus the nation's + 0.25 x the same for the election before, using only elections held before the cycle
cd_lean_2026.csv), the maps of the 2022 election from 2020 and 2016 and of the 2018
election from 2016 and 2012 (cd_lean_hist.csv, on which the prior is fitted). The national margins are the 50-state sums
of the same tables. Every state is calibrated to its official totals, each nominee's votes on every ballot line (MIT's
party code leaves out New York's fusion lines through 2016).2026 maps. Redrawn states are found from the Census 119th v 120th Congress block equivalency files. Unchanged states
use MEDSL 2024 precinct rows by their U.S. House label, where the file matches the official state totals and the
presidential vote sits on labelled precinct rows. Redrawn states (and files that fail those checks): VEST 2020 precincts
-> 2020 blocks -> 120th-Congress districts, carried to 2024 by the county-by-district swing on the labels (qc_cd.json
lists every check and shift). The Census 120th-Congress block file does not follow court orders, so
cd_lean_build.MAP_STATUS records the map in force per state with a dated source; Missouri uses its 2022 lines (the U.S.
Supreme Court blocked the 2025 map on 2026-09-25). For comparison only, against 2 x Cook PVI: rms 1.09 points over all 435
(0.93 on unchanged maps, 1.29 on redrawn ones), mean +0.25, r 0.9994.
2018 and 2022 maps. VEST 2016 and 2020 precincts -> 2020 blocks -> the 118th-Congress block equivalency file (2022) or the TIGER/Line 116th-Congress districts by each block's internal point (2018). VEST has no 2012 files and MEDSL's county returns sit behind a Dataverse guestbook, so 2012 is the 2016 block votes moved by each county's 2012-16 swing and turnout change, with the county results from Wikipedia's state pages (every state's table matches its official totals within 1 %). Checked against The Downballot's published presidential results by district (a check only; their figures are not used or redistributed): 2016 on the 2018 maps rms 0.16 points (largest 1.7, MO-5), 2020 on the 2022 maps rms 0.23 (3.4, LA-6), 2012 on the 2018 maps rms 2.5 (10, TX-7: the county step cannot see swings inside the large metro counties), so the 2018 lean is within rms 0.63 (2.5) of the same lean from their figures. The 2016 vote alone would be off by rms 2.45 (7.6). The county step measured where both elections are known (2016 rebuilt from the 2020 blocks): rms 2.7 by district. 538's published leans for the same maps differ by rms 3.3 (2018 partisan lean, which also weights state legislative results; largest in West Virginia and Kentucky) and 2.4 (the PVI column of its 2022 urbanization index).
Fit (model.fit_prior, contested seats 2018 and 2022, n 795; the 538 / Cook leans it replaced in brackets): lean slope
0.918 (0.951), incumbency 4.47 (4.58), residual sd 7.35 (7.41); with fundraising (money.coefficients) 0.855 (0.889), 3.34
(3.51) and 1.31 (1.25) per unit of the log money ratio. Nothing about the prior is a stored constant: every run refits it,
and the intercept k0 (the contested-margin intercept over the national House vote, data_prep.national_house_vote) and the
prior sd come out of the same fit - 0.42 (2018 -0.79, 2022 +1.63) and 7.35 today. Until October 2026 the blend used a
hand-copied prior sd of 6.7 and k0 of 0.4 from the earlier 538 / Cook fit, so the switch of lean had left the polls weighed
against a prior 20 % tighter than the fit said. Fundraising still helps out of sample on the new lean (all-seat RMSE fit on
one cycle and scored on the other: -5.6 % in 2018, -2.6 % in 2022).
Backtest. The live House path (model.house_blend, factor_update, simulate) on 2018 and 2022, each year forecast
from the other's fit (slopes, k0, fundraising), 538's House polls as released, the generic-ballot average at the lead, 7
dates from August 1 to the day before the election (5,565 seat-dates):
| lean | log loss | Brier | CRPS | margin MAE | 80 % cov. | Sep 15, 2018 (235 won) | Sep 15, 2022 (213 won) |
|---|---|---|---|---|---|---|---|
| 538 / Cook | 0.1153 | 0.0353 | 3.642 | 4.98 | 0.854 | 221 (197-247) | 212 (190-233) |
| ours | 0.1113 | 0.0338 | 3.593 | 4.93 | 0.866 | 222 (198-249) | 212 (189-233) |
Ours is better in both cycles on all three scores (log loss 2018 -0.0070, 2022 -0.0009; race bootstrap p 0.13, Brier 0.055, CRPS 0.20): with two cycles not a significant gain, and not a loss. The seat count is inside the 10-90 % range at all 14 dates with either lean. On the inputs of 16 October 2026 the switch moved the House from 241.7 to 241.2 Democratic seats (P(majority) 0.894 -> 0.888): +0.7 seats from the lean with k0 refitted on both leans, -1.1 from replacing the stale k0.
These ran until election night and were retired with the early-vote page on 4 November 2026. The trackers behind the tests of the early vote's skill (North Carolina, Florida, Pennsylvania, Clark County) keep their code and committed aggregates; the Georgia, Texas and Washoe County trackers, the turnout projection and North Carolina's election-night tracker were removed with their data. None of these fed the forecast. Votes cast before Election Day enter the model only through the early-vote movement split described above (national and race-level).
North Carolina, the tracker and the gate (nc_tracker.py): accepted mail and one-stop ballots of 2026 against the 2018 and 2022
midterms and the 2020 and 2024 presidential years on the same number of days out, by party, age, race, county (against
each county's 2024 presidential margin) and whether the voters voted in the previous two generals; rates are per
registered voter of the same group on that year's rolls (2018-2024 committed in data/static/nc_tracker/; 2026 from the
morning's file, this week's registration file reduced again only when the board replaces it, and nc_early's cached
voter history). The gate: the party mix of the early vote (D - R share of ballots banked by L days out, minus the
same on the rolls, change from the previous cycle of the same kind) against North Carolina's statewide poll miss, 2012-2022:
5 scoreable cycles, r -0.72 at 17 days (p 0.17), -0.27 at 10, -0.22 at 5, the sign opposite to the usual reading. No skill,
so it stays out of the forecast (data/static/early_gate.json); rerun after 2026.
North Carolina by race and ethnicity (nc_early.py; race and ethnicity are self-reported on the NC voter record): accepted
mail and one-stop ballots by days before Election Day, 2026 against 2022 and 2024, for white, Black, other, undesignated
and Hispanic voters - each group's share of ballots, that share against its share of registered voters on the same days
out, ballots per 1,000 registered, the share who skipped at least two of the three previous generals, and what the same
voters did in the previous general of the same kind. Inputs: the NCSBE absentee file of 2026 (one conditional request per
run), the committed 2022 / 2024 baseline, NCSBE weekly registration statistics, and the 2016-2024 generals reduced from
NCSBE's voter-history file (voter-level, so it lives only in the Actions cache, never committed). Output
data/early/nc_early.json and the day's table data/early/nc/2026_<date>.csv.
Florida (fl_early.py; party only): the Division of Elections' daily statistics files are snapshotted into
data/early/fl_stats/ (Florida keeps no history, so a missed day is lost) and compared with the Wayback Machine's 2024
captures at the same days out, as the returns' R-D margin, that margin minus the registration R-D of the latest month end,
and minus the R-D of all mail ballots sent. Output data/early/fl_early.json. Hurricanes Helene (Sep 26, 2024) and Milton
(Oct 9, 2024) fall on the 2024 baseline dates.
Pennsylvania (pa_early.py, daily run): mail-ballot applications and returned ballots by party at matched days out,
2026 against 2022 and 2024, from the Department of State's datasets on data.pa.gov; uploaded as a workflow artifact.
Model and code: Shawn Corvec. Poll data belong to their pollsters. Governor results and nominees' offices, and the 2026
calls and counts: Wikipedia (CC BY-SA 4.0). Ranked-choice transfer rates are derived from the Maine Secretary of State's and the State of Alaska
Division of Elections' official tabulations. See web/about.html ("Sources and credits") for the full list. Not
affiliated with or endorsed by any of the sources.
Code: MIT. Data keep the terms of their sources, listed above. Race polls taken from Wikipedia's race pages are
used under CC BY-SA 4.0; the race poll averages in web/data/model.json
are derived from them and published under the same licence. The 2026 calls and counts in data/results/results_2026.csv and
web/data/results.json are taken from Wikipedia's election pages under the same licence, until official returns replace them.
python -m midterms.national_mood --refitgov_results_2024.csv, gov_nominees.csv, gov_candidate_quality.csv, gov_nominee_offices.csv | 2024 governor results; every governor nominee 1998-2026 with the linked article; whether each had held statewide elected office or a seat in Congress, and the infobox offices read | Wikipedia yearly "United States gubernatorial elections" pages and the nominees' articles (CC BY-SA 4.0); gov_quality.py |
rcv_transfers.csv, rcv_rates.json, rcv_backtest_results.csv, rcv_backtest_polls.csv | ranked-choice transfers per eliminated candidate, the fitted rates, the leave-one-race-out backtests | official round-by-round tabulations of the Maine Secretary of State (2nd district 2018, 2022) and the State of Alaska Division of Elections (August 2022 special general, 2022 and 2024 generals), public records; python -m midterms.rcv --fetch downloads them into data/raw/rcv/ and rebuilds the tables |
cd_lean_2026.csv, qc_cd.json | the House prior's district lean on the 2026 maps: 2024 and 2020 presidential margins for all 435 seats, with every check | MEDSL 2024 precinct returns (CC0), VEST 2020 (CC BY 4.0), Census block equivalency files (public domain); cd_lean_build.py |
cd_lean_hist.csv, qc_cd_hist.json | the same lean on the maps of the 2018 election (2016 and 2012 president) and the 2022 election (2020 and 2016), on which the prior is fitted, with every check | VEST 2016 and 2020 (CC BY 4.0), Census TIGER/Line 116th-Congress districts, 118th-Congress block equivalency file and 2020 blocks (public domain), MIT state totals (CC0); 2012 county results from Wikipedia's state pages (CC BY-SA 4.0, so the 2012 columns are CC BY-SA 4.0); cd_lean_build.py hist |
stateleg/ | state-legislature leans, results, sitting legislators, fitted parameters, backtest and audits | see State legislatures |
nc_early_baseline.csv (+ .qc.json) | North Carolina accepted mail / one-stop ballots of the 2022 and 2024 generals by days out and race / ethnicity group, with propensity counts and the registered voters of each group | NC State Board of Elections absentee files, voter-history file and registration statistics (public records); aggregates only, python -m midterms.nc_early baseline --src DIR |
fl_wayback/, fl_registration.csv | Florida statistics files of the 2024 and 2022 generals; active registered voters by party at month ends | Florida Division of Elections (public records); the 2024 / 2022 files as captured by the Internet Archive's Wayback Machine, the registration figures from the monthly county-and-party reports |
race_move_fit.json | a race's own late movement, pts² of margin a day (the total, its national part, the bootstrap sd) | 538 raw_polls (CC BY 4.0), Senate + governor generals 1998-2022; python -c "from midterms import early_vote; early_vote.race_move_fit()" |
nc_tracker/ | North Carolina accepted mail / one-stop ballots of the 2018-2024 generals by days out, method, party, race, age and county, with each year's registered voters, the 2022 early voters' 2018 / 2020 participation, the party paces of 2008-2024 and each county's 2024 presidential margin | NC State Board of Elections absentee files, registration statistics, voter history and precinct results (public records); aggregates only, python -m midterms.nc_tracker history --src DIR |
early_gate.json | the backtest that keeps the early-vote party mix out of the forecast (North Carolina, 5 scoreable cycles) | the tables above and 538 raw_polls; python -m midterms.nc_tracker gate |
early_skill.json, early_skill/ | the multi-state test of the same question, pre-registered in early_skill.py (North Carolina, Pennsylvania, Clark County; the change from the last cycle of the same kind against the final poll average's miss), with its aggregate inputs: Pennsylvania mail returns by party and days out 2020-2024, the Internet Archive's Florida captures, the source versions | data.pa.gov (PA Department of State, public records, counted server-side); the North Carolina and Clark County tables above; 538 raw_polls and poll archives (CC BY 4.0); MIT Election Data and Science Lab President and Senate 1976-2024 (CC0); early-skill.yml |
nv_clark_baseline.csv | Clark County, Nevada, mail ballots returned by registered party and day in the 2022 and 2024 generals, and 2024 in-person early voters by day (counts only) | Clark County Election Department return files (public records); nv_clark_early.py |
Top-rated pollsters count more respondents (generic.N_CAP_TOP = 3000, TOP_SCORE = 2.8): polls of a pollster rated
A or better count up to 3,000 respondents instead of 1,500; every other pollster keeps the 1,500 cap. Adopted on the rule
set before the test (neither score may get worse): same harness, with each cycle's ratings as 538 published them before
the election, next polls 2.565 -> 2.564 and margin 3.436 -> 3.427 (2024 only; not significant). A higher share cap for the
same pollsters (SHARE_CAP_TOP 0.25) made both scores worse (2.567 / 3.447) and is off: every pollster keeps the 15 %
cap. On 21 October 2026 the rule applies to Siena, Emerson, Cygnal and TIPP.
Gallup party identification (tested, not adopted). Gallup's leaned party-ID gap (adults) was tested as a predictor of the national House vote alongside the generic-ballot average G, leave-one-election-out 1996-2024 (n 15). Gallup's quarterly series is not openly published, so the annual series is the proxy. V ~ G: MAE 1.91 / RMSE 2.29; + the election-year gap 1.69 / 2.14 (better in 11/15, permutation p 0.09); + the previous year's gap 1.60 / 2.08 (12/15, permutation p 0.06); + the two-year change 2.04 / 2.41 (worse). Midterms only (n 7): no variant helps. No significant out-of-sample gain. The per-year series is not distributed (Gallup does not allow republishing its tables); source: Gallup, "Party Affiliation" trend, retrieved October 2026.
Uncontested seats (tested, not adopted). The raw national House vote that the national-mood fit targets is distorted by seats without a D-v-R contest (2018: 29 D-only and 2 R-only seats plus same-party top-two generals; D 6.0M votes there, R 0.6M) and by Florida's unopposed races, which record no vote. A one-off test (October 2026) imputed every such seat (23-97 a cycle) from its nearest contested result on the same lines, else a safe-seat model (masked contested seats: margin RMSE 11.0, safe-seat fallback 18.0), giving an adjusted vote 1.6 points either side of the raw one over 1996-2024 (2018: D+8.7 -> D+7.7). The national error falls (4.27 -> 3.90 at 29 days), but scored against the same target the adjusted vote does not forecast better (log score -0.022 per cycle, 7/15 cycles better, permutation p 0.29). A seat harness's gain (p 0.008) comes entirely from the intercept moving 0.27 points toward the Republicans, the generic ballot's historical Democratic overstatement that the no-direction rule excludes; the live-path House backtest (2018-2022) is a wash. Sources: MIT Election Data and Science Lab, U.S. House 1976-2018 (CC0); FEC "Federal Elections" 2018-2022 and the House Clerk's 2024 election statistics (public domain).
| 0.750 |
| prior sd x0.8 | -0.0063 (9/10; 0.014 / 0.005) | -0.0015 (9/10; 0.143 / 0.085) | +0.140 (0/10; 0.002 / 0.000) | +0.12 (0.006 / 0.007) | 0.728 |
| prior sd x1.25 | +0.0071 (1/10; 0.004 / 0.000) | +0.0020 (1/10; 0.027 / 0.003) | -0.060 (8/10; 0.031 / 0.031) | -0.05 (0.143 / 0.150) | 0.778 |
| shared statewide shock x0.5 | -0.0037 (7/10; 0.258 / 0.226) | -0.0008 (9/10; 0.139 / 0.239) | +0.024 (3/10; 0.119 / 0.018) | +0.00 (1.000 / 1.000) | 0.703 |
| shared statewide shock x1.5 | +0.0085 (2/10; 0.051 / 0.008) | +0.0017 (1/10; 0.037 / 0.021) | +0.004 (6/10; 0.867 / 0.775) | +0.00 (1.000 / 1.000) | 0.822 |
| race sd x1.15 | +0.0063 (3/10; 0.031 / 0.001) | +0.0013 (1/10; 0.016 / 0.002) | -0.019 (7/10; 0.148 / 0.076) | +0.00 (1.000 / 1.000) | 0.811 |
| race sd x0.9 | -0.0037 (7/10; 0.066 / 0.006) | -0.0008 (9/10; 0.023 / 0.006) | +0.024 (2/10; 0.016 / 0.001) | +0.00 (1.000 / 1.000) | 0.729 |
| undecided penalty x2 | +0.0002 (3/10; 0.578 / 0.639) | +0.0000 (4/10; 0.984 / 0.983) | +0.002 (5/10; 0.824 / 0.769) | +0.01 (0.518 / 0.581) | 0.758 |
| undecided penalty from 8 % | +0.0003 (2/10; 0.277 / 0.309) | +0.0000 (4/10; 0.662 / 0.792) | +0.002 (4/10; 0.607 / 0.558) | +0.01 (0.211 / 0.229) | 0.758 |
| no undecided penalty | +0.0001 (5/10; 0.891 / 0.931) | +0.0001 (6/10; 0.629 / 0.643) | +0.001 (5/10; 0.875 / 0.856) | -0.00 (0.758 / 0.838) | 0.757 |
| third-candidate penalty x1.5 | +0.0002 (1/10; 0.250 / 0.026) | +0.0001 (1/10; 0.375 / 0.050) | +0.004 (2/10; 0.875 / 0.513) | +0.00 (0.875 / 0.694) | 0.761 |
| poll staleness half-life 42 d | +0.0000 (3/10; 0.906 / 0.907) | -0.0001 (5/10; 0.646 / 0.750) | +0.012 (3/10; 0.178 / 0.308) | +0.02 (0.191 / 0.345) | 0.765 |
| poll staleness half-life 90 d | +0.0002 (5/10; 0.422 / 0.577) | +0.0001 (5/10; 0.346 / 0.491) | -0.005 (5/10; 0.424 / 0.537) | -0.01 (0.289 / 0.390) | 0.755 |
| governor poll errors from raw_polls (4.4 / 5.4) | +0.0015 (4/10; 0.555 / 0.467) | +0.0001 (2/10; 0.955 / 0.914) | +0.061 (2/10; 0.016 / 0.001) | +0.07 (0.035 / 0.025) | 0.773 |
| successor incumbents, own coefficient | -0.0049 (8/10; 0.020 / 0.005) | -0.0018 (8/10; 0.025 / 0.005) | +0.021 (4/10; 0.340 / 0.330) | +0.02 (0.580 / 0.533) | 0.760 |
| lean trend + parser fix | -0.0083 (9/10; 0.027 / 0.003) | -0.0028 (8/10; 0.018 / 0.009) | -0.167 (7/10; 0.041 / 0.003) | -0.22 (0.055 / 0.006) | 0.782 |
| lean trend + successor term | -0.0090 (9/10; 0.008 / 0.001) | -0.0030 (9/10; 0.004 / 0.003) | -0.185 (7/10; 0.035 / 0.001) | -0.25 (0.045 / 0.004) | 0.781 |
| polls pulled toward the state's fundamentals (k walk-forward) | -0.0046 (7/10; 0.074 / 0.150) | -0.0012 (6/10; 0.676 / 0.339) | -0.091 (7/10; 0.035 / 0.014) | -0.12 (0.066 / 0.037) | 0.786 |
| nominee experience D - R | -0.0049 (5/10; 0.242 / 0.139) | -0.0024 (6/10; 0.107 / 0.075) | -0.094 (8/10; 0.027 / 0.004) | -0.13 (0.025 / 0.004) | 0.771 |
| nominee experience, open seats only | -0.0045 (6/10; 0.107 / 0.106) | -0.0023 (6/10; 0.031 / 0.040) | -0.056 (8/10; 0.178 / 0.057) | -0.09 (0.129 / 0.019) | 0.770 |
| lean trend + successor term + experience (adopted) | -0.0111 (9/10; 0.037 / 0.011) | -0.0043 (8/10; 0.020 / 0.004) | -0.266 (8/10; 0.012 / 0.000) | -0.35 (0.016 / 0.000) | 0.796 |
| adopted + undecideds toward the trailing candidate (k walk-forward) | -0.0114 (8/10; 0.158 / 0.023) | -0.0042 (7/10; 0.045 / 0.011) | -0.084 (5/10; 0.461 / 0.266) | -0.12 (0.451 / 0.267) | 0.760 |
| lean trend + successor + experience (open seats) | -0.0110 (9/10; 0.018 / 0.003) | -0.0042 (8/10; 0.014 / 0.001) | -0.233 (7/10; 0.021 / 0.000) | -0.32 (0.021 / 0.000) | 0.791 |
| lean trend + successor, race sd x1.15 | -0.0029 (4/10; 0.363 / 0.402) | -0.0018 (5/10; 0.125 / 0.124) | -0.192 (6/10; 0.031 / 0.000) | -0.25 (0.045 / 0.004) | 0.824 |
| lean trend + successor, race sd x0.9 | -0.0125 (8/10; 0.016 / 0.000) | -0.0037 (9/10; 0.012 / 0.002) | -0.170 (7/10; 0.064 / 0.004) | -0.25 (0.045 / 0.004) | 0.755 |
| lean trend + successor, shared shock x1.5 | +0.0000 (2/10; 0.988 / 0.946) | -0.0012 (4/10; 0.266 / 0.429) | -0.175 (6/10; 0.041 / 0.002) | -0.25 (0.045 / 0.004) | 0.839 |
| lean trend + successor, prior sd x1.25 | -0.0010 (6/10; 0.805 / 0.707) | -0.0007 (7/10; 0.553 / 0.509) | -0.218 (8/10; 0.008 / 0.000) | -0.28 (0.016 / 0.003) | 0.785 |
wiki_polls.house_rcv_polls): the polls name Hill as the challenger (32.5 % in the August top-four primary,
Hafner 3.8 %), each survey's final round is used or its first round converted with the transfer rates, and the seat keeps the
D-v-R prior widened x SPLIT_PRIOR_MULT without the money term (model.house_blend). A Hill win is not a Democratic seat
(model.house_seat_counts); data/cache/house2026_challengers.csv keeps the named challenger when a page fetch fails.