US recession oddsWill the US fall into a recession in the next 12 months?
Unlikely. The risk of a recession is low right now.
About 4 in 100 (4.1%) chance that a recession starts within the next 12 months.
A month ago: about 6 in 100 (back-test) · a year ago: about 40 in 100 (back-test)
Ringed dots: a typical year. Since 1980, a recession has started within the following 12 months about 13 times in 100.
What this means for you
- Nothing here points to a downturn soon. Most households don't need to change plans because of recession risk.
- Good habits always help: a few months of expenses saved, and debts you could still pay if your income dipped.
- Odds can change quickly when new data comes out. This page updates every weekday.
General information, not financial advice.
Everyday signs to watch
2 of these 4 signs look calm.
About 4.2% of people who want a job don't have one, down 0.2 points from a year ago. Employers added about 51,000 jobs a month lately.
Prices are about 3.4% higher than a year ago. The Federal Reserve aims for about 2%.
Pay rose 3.1% and prices 3.4% over the year, so paychecks are falling behind prices by 0.2%.
Long-term interest rates are 1.1 points above short-term ones, which is normal. When short-term rates rise above long-term ones, recessions have often followed.
The odds over time
The chance of a recession starting within 12 months, month by month. Shaded bars are past recessions.
In 2022–2024 the odds rose above 90 in 100, but no recession came: forecasts like this one can give false alarms. Before October 2026 the line is a back-test: what the method would have said at the time.
Updated Oct 6, 2026 from US government and Federal Reserve data. A research tool, not investment advice.
LatestNew data released by Oct 6, 2026 (excess bond premium (Fed Board)) updated the 12-block model. The headline uses only the term spread and the bond premium. It moved from 6.1% to 4.1% because the term spread rose from 0.90 to 1.08 points (October's average so far, 3 trading days) and the bond premium fell from −0.28 to −0.31. The headline is 4.1%, down 1.9 pts from the September end-of-month forecast.
Recession odds are low: 4.1%, well below the 25% alarm line.
1 week ago 6.1% (logged forecast for Sep 30, 2026)1 month ago 5.9% (back-test reading for Aug 31, 2026)3 months ago 9.7% (back-test reading for Jun 30, 2026)1 year ago 40% (back-test reading for Sep 30, 2025)
Two market signals set this number, and both read calm: the yield curve is positive (the 10-year Treasury yield is 1.08 points above the 3-month bill); the excess bond premium, the extra yield lenders demand beyond expected defaults, is −0.31, below its long-run average: lenders are relaxed.
80% range 2.8–6.1% from the fit alone; the models we track range from 2.5% to 9.1% · Forecast updated Oct 6, 2026, 11:35 am ET · data last checked Oct 6, 2026, 11:33 am ET (checks run every weekday)
Next scheduled data: Chicago Fed financial conditions, Wed Oct 7 (8:30 am ET) · CPI inflation, Wed Oct 14 (8:30 am ET) Release calendar ›
Compare other models, and why this is the headline
Not on the scale: Chauvet-Piger, 0.6%. It estimates whether a recession has already begun, not whether one starts in the next 12 months.
Why this number: kill rule K4 found that the yield-curve benchmark (term spread + EBP) forecast better than the 12-block model in testing, so the benchmark is the headline (6.1% in the last end-of-month forecast). The 12-block model reads 2.5%, shown as a second opinion.
How reliable is it? In testing over 1980–2024, months this benchmark put below 5% were followed by a recession 0.7% of the time (268 months). Its above 50% readings were less exact: recessions followed 46% of the time against 84% forecast. Each band rests on only a handful of recessions, so read the number as a guide to risk, not a precise frequency.
The Fed Board's 9.1% (September, published about two months behind) answers a different question: the chance the economy is in recession at some point in the next 12 months, so it reads near 100% while a recession is under way. Ours is the chance a recession begins. The Fed's model also barely reacts to the yield curve (it averaged 21% through 2023, during the deep inversion, when our yield-curve benchmark averaged 97%) and is fitted to the whole history with hindsight. They are different models, not one model fitted two ways; over 1980–2024 the two series correlate only 0.35.
What would change the odds?
Move the two signals behind the headline, the yield-curve benchmark (term spread + EBP)
What moved since the last end-of-month forecast
The model rose from 2.5% to 2.5% with excess bond premium (Fed Board). The dollar and commodity prices went from +0.37 to −0.04; labor went from −0.07 to +0.22; the curve block went from −0.37 to −0.23. The moves pulled in different directions. Within the dollar and commodity prices, the strongest signals: agricultural commodity index (+0.44). The weakest: broad trade-weighted dollar index (−0.32); advanced-economy dollar index (−0.23).
What moved the 12-block model
Change in the model's probability, percentage points (+ raises risk; approximate, block by block). 5 of 12 blocks carry no weight.
The twelve blocks today
Standardized against each signal's own history. Below zero is recession-like.
Policy Uncertainty reads −4.00, but the fit gives this block no weight, so it does not move the model: once the other blocks are known, it added nothing in the backtest. With a weight of −0.46, Business, Orders & Surveys is the largest single term holding the model's probability down (−0.15 on the probit scale, more than Labor Market).
The record
Walk-forward: each month is forecast using only recessions the NBER had announced by then. Before about 2010 most inputs come from later-revised data, so the early record is an optimistic ceiling.
Kill rules
1 in force · when to stop trusting the model
Written on Oct 2, 2026, after the backtest had been run, and before any forecast was published. K4 was corrected on Oct 3, after it fired, to compare against the benchmark it names, the yield-curve benchmark (term spread + EBP), in both test windows. When a rule fires, its action is applied automatically.
Limits of these rules
- K4 switches the headline on a small gap: 0.012 / 0.010 in AUROC (1980+ / 1990+), across the five recessions in the test window. A resampling test in the review of Oct 5, 2026 put the 90% range of that gap at about ±0.1, so the two models cannot be told apart statistically. On Brier score (calibration, lower is better) the 12-block model is better in both windows (0.084 / 0.071 against 0.098 / 0.090).
- The benchmark's excess bond premium history is re-estimated by the Fed with hindsight and has no real-time archive, which flatters the benchmark in the K4 comparison. With a real-time credit spread (Baa) in its place, the benchmark leads in 1980–2024 but trails in 1990–2024 on AUROC (review experiments, Oct 5, 2026).
- The benchmark itself stayed above 50% for 25 straight months in 2022–24 with no recession. K2 applies only to the 12-block model, whose longest such run was 7 months.
- K3 needs 36 resolved live forecasts, one a month from Sep 2026, so it cannot fire before Aug 2029.
- K6 uses a gap in percentage points, which says little at low probabilities (today's 2.5% against 4.1% is a 1.6-fold difference but only 1.6 points).
- K7 looks at whole block scores at the ±4 cap, which needs every factor in a block capped, so it rarely can fire.
- K8 refers to text factors, and the model currently has none.
- Any change to these rules will be dated here and applied going forward only; no past forecast is edited.
Forecast log
append-only, hash-chained · last 4 entries
| Month | Model | TS + EBP | Logged (ET) | Note | Hash |
|---|---|---|---|---|---|
| 2026-10-06 in-month | 2.5% | 4.1% | Oct 6, 2026, 11:35 am | in-month update: Excess bond premium (Fed Board) | 145EC8BDE5 |
| 2026-10-02 in-month | 1.6% | 4.4% | Oct 2, 2026, 11:26 pm | in-month update: Jobs report; Jobless claims; Durable goods; 23 other series (30-year fixed mortgage rate, AAA, Atlanta Fed GDPNow, BAA…) | A430D5BC0D |
| 2026-09 month-end | 2.5% | 6.1% | Oct 2, 2026, 11:01 pm | post-audit fixes 2026-10-02 | 84F00E9016 |
| 2026-09 month-end | 2.8% | 6.2% | Oct 2, 2026, 9:46 pm | D7B9A9D8A4 |
"Month-end" is the end-of-month forecast, comparable with the 1980–2024 track record. "In-month" is an interim update logged when new data was published. Scoring rule: when a month has more than one end-of-month entry, the last one logged for that month is the one scored; earlier ones stay in the log, marked superseded. Both September month-end entries stay in the log by design: the 9:46 pm ET entry was superseded at 11:01 pm ET (post-audit fixes 2026-10-02). Any edit to a past entry breaks the hash chain. The maintainer can rebuild each entry from stored data; the code is not public yet and some inputs are licensed, so readers can verify the hashes but not rerun the model.
Cite this
CycleWatch. (2026, October 6). US recession probability: 4.1% chance a recession begins within 12 months (forecast-log entry 145EC8BDE5). https://cyclewatch.org
The forecast history is downloadable as CSV on the Data page.
Signals
The most telling series behind the odds, as last published. The signal is each series' z-score against its own history, signed so that below zero is recession-like. Choose a block to open it, or search; select a row for details.
Listed here: 67 of the 194 signals. In each block, the strongest readings, every recession-like one and the benchmarks the Track record scores. All 194 signals ›
No match among the signals listed here. Browse all 194 signals ›
Block 01 · Real Activity & Output4 of 15 listed · 11 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Core capital goods orders NEWORDER · FRED | 87,640 Millions of Dollars +1,345 | 2026-08-01 pub. 2026-09-25 | +0.73 | In model | |
| Electricity output IPG2211S · FRED | 113.1 Index 2017=100 +2.32 | 2026-08-01 pub. 2026-09-18 | +1.45 | In model | |
| Heavy truck sales HTRUCKSSAAR · FRED | 0.396 Millions of Units −0.017 | 2026-09-01 pub. 2026-10-02 | −1.64 | In model | |
| Real personal consumption expenditures PCEC96 · FRED | 16,955 Billions of Chained 2017 Dollars +92.80 | 2026-08-01 pub. 2026-09-30 | +0.43 | In model |
Block 02 · Labor Market4 of 22 listed · 13 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Average weekly hours, manufacturing AWHMAN · FRED | 42.00 Hours +0.200 | 2026-09-01 pub. 2026-10-02 | +1.01 | In model | |
| Job openings JTSJOL · FRED | 7,079 Level in Thousands −256.0 | 2026-08-01 pub. 2026-09-29 | −1.20 | In model | |
| Prime-age employment-population ratio LNS12300060 · FRED | 80.70 Percent +0.300 | 2026-09-01 pub. 2026-10-02 | +1.35 | In model | |
| Sahm Rule (real-time) SAHMREALTIME · FRED | 0.000 Percentage Points +0.070 | 2026-09-01 pub. 2026-10-02 | — | Benchmark |
Block 03 · Household & Consumer4 of 15 listed · 8 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Credit card delinquency, all banks DRCCLACBS · FRED | 2.85 Percent −0.060 | 2026-04-01 pub. 2026-08-25 | +0.26 | In model | |
| Household net worth TNWBSHNO · FRED | 195,870,496 Millions of U.S. Dollars +12,803,400 | 2026-04-01 pub. 2026-09-11 | +2.76 | In model | |
| Real retail & food services sales RRSFS · FRED | 231,630 Millions of 1982-84 CPI Adjusted Dollars +1,933 | 2026-08-01 pub. 2026-09-16 | +0.42 | In model | |
| Total vehicle sales, SAAR TOTALSA · FRED | 16.38 Millions of Units −0.602 | 2026-09-01 pub. 2026-10-02 | −0.95 | In model |
Block 04 · Business, Orders & Surveys4 of 6 listed · 6 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Business formation applications BAHBATOTALSAUS · FRED | 145,387 Number −6,219 | 2026-08-01 pub. 2026-09-11 | −0.28 | In model | |
| Chicago Fed National Activity Index CFNAI · FRED | -0.040 Index −0.120 | 2026-08-01 pub. 2026-09-21 | −0.15 | In model | |
| Corporate profits, NIPA basis CPATAX · FRED | 3,877 Billions of Dollars +276.9 | 2026-04-01 pub. 2026-08-26 | +0.90 | In model | |
| Philadelphia Fed manufacturing survey GACDFSA066MSFRBPHI · FRED | 37.80 Index −9.60 | 2026-09-01 pub. 2026-09-17 | +1.60 | In model |
Block 05 · Credit & Financial Conditions4 of 23 listed · 14 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Excess bond premium (Gilchrist-Zakrajsek) EBP · FED_BOARD | -0.392 Percentage points −0.166 | 2026-09-01 pub. — | +0.77 | In model | |
| Kansas City Fed financial stress index KCFSI · FRED | -0.946 Index −0.104 | 2026-08-01 pub. 2026-09-05 | +0.87 | In model | |
| SLOOS: CRE standards SUBLPDRCSN · FRED | -11.30 Percent −8.00 | 2026-07-01 pub. 2026-08-03 | +1.05 | In model | |
| St. Louis Fed financial stress index STLFSI4 · FRED | -0.807 Index −0.106 | 2026-09-25 pub. 2026-09-30 | +0.93 | In model |
Block 06 · Monetary Policy, Rates & Liquidity4 of 15 listed · 9 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| 10-year real yield DFII10 · FRED | 2.92 Percent +0.040 | 2026-10-02 pub. 2026-10-05 | −2.01 | In model | |
| 5-year real yield DFII5 · FRED | 2.69 Percent +0.040 | 2026-10-02 pub. 2026-10-05 | −1.90 | In model | |
| Effective fed funds rate DFF · FRED | 3.88 Percent +0.000 | 2026-10-02 pub. 2026-10-05 | −0.65 | In model | |
| M2 money stock M2SL · FRED | 23,343 Billions of Dollars +124.9 | 2026-08-01 pub. 2026-09-22 | −0.33 | In model |
Block 07 · The Yield Curve Complex2 metrics · 2 in the model
Block 08 · Energy Prices4 of 18 listed · 3 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Hamilton net oil price increase OIL_SHOCK · Derived | 0.000 model input (derived) +0.000 | 2026-10-06 pub. — | +0.00 | In model | |
| Natural gas, Henry Hub DHHNGSP · FRED | 3.18 Dollars per Million BTU +0.050 | 2026-09-29 pub. 2026-09-30 | +0.27 | In model | |
| WTI crude oil DCOILWTICO · FRED | 96.16 Dollars per Barrel −3.21 | 2026-09-29 pub. 2026-09-30 | −1.71 | In model | |
| Headline CPI CPIAUCSL · FRED | 334.1 Index 1982-1984=100 +1.32 | 2026-08-01 pub. 2026-09-11 | — | Not in model |
Block 09 · Housing & Construction4 of 12 listed · 9 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Housing starts HOUST · FRED | 1,275 Thousands of Units −34.00 | 2026-08-01 pub. 2026-09-17 | +0.71 | In model | |
| Months' supply of new homes MSACSR · FRED | 8.50 Months' Supply −0.500 | 2026-08-01 pub. 2026-09-24 | +1.05 | In model | |
| Private nonresidential construction PNRESCONS · FRED | 773,010 Millions of Dollars +8,031 | 2026-08-01 pub. 2026-10-01 | +0.93 | In model | |
| Residential fixed investment share of GDP A011RE1Q156NBEA · FRED | 3.60 Percent −0.100 | 2026-04-01 pub. 2026-07-30 | −0.67 | In model |
Block 10 · Equity, Volatility & Cross-Asset4 metrics · 2 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| CBOE volatility index VIXCLS · FRED | licensed: not shown on the public site | 2026-10-05 pub. 2026-10-05 | — | In model | |
| Equity drawdown from trailing 12-month high NASDAQCOM · FRED | licensed: not shown on the public site | 2026-10-05 pub. 2026-10-05 | — | In model | |
| Bond-equity correlation regime BE_CORR · DERIVED | no free source | — | Context | ||
| Equity risk premium versus real yields ERP · DERIVED | no free source | — | Context |
Block 11 · Commodities & Dollar4 of 7 listed · 3 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Advanced-economy dollar index DTWEXAFEGS · FRED | 114.9 Index Jan 2006=100 −0.177 | 2026-10-02 pub. 2026-10-05 | −0.23 | In model | |
| Agricultural commodity index WPU01 · FRED | 230.9 Index 1982=100 −6.97 | 2026-08-01 pub. 2026-09-10 | +0.44 | In model | |
| Broad trade-weighted dollar index DTWEXBGS · FRED | 121.4 Index Jan 2006=100 −0.403 | 2026-10-02 pub. 2026-10-05 | −0.32 | In model | |
| Copper price PCOPPUSDM · FRED | 13,543 U.S. Dollars per Metric Ton −9.22 | 2026-07-01 pub. 2026-08-17 | — | Not in model |
Block 12 · Fiscal & Sovereign4 of 5 listed · 0 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Federal deficit as share of GDP FYFSGDA188S · FRED | -5.77 Percent of GDP +0.427 | 2025-01-01 pub. 2026-02-20 | — | Not in model | |
| Bill versus coupon issuance mix ISSUE_MIX · TREASURY | no free source | — | Context | ||
| Federal debt held by the public GFDEBTN · FRED | 39,462,398 Millions of Dollars +396,977 | 2026-04-01 pub. 2026-09-02 | — | Context | |
| Net interest outlays A091RC1Q027SBEA · FRED | 1,280 Billions of Dollars +26.88 | 2026-04-01 pub. 2026-07-30 | — | Context |
Block 13 · Global & External3 metrics · 0 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| BIS debt service ratios BIS_DSR · BIS | no free source | — | Context | ||
| EM foreign exchange reserves EM_RESERVES · IMF | no free source | — | Context | ||
| OECD composite leading indicator, US USALOLITOAASTSAM · FRED | 101.0 Index +0.064 | 2026-08-01 pub. 2026-09-15 | — | Benchmark |
Block 14 · Policy Uncertainty3 metrics · 1 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Economic Policy Uncertainty index USEPUINDXD · FRED | 421.7 Index +173.5 | 2026-10-05 pub. 2026-10-06 | −4.00 | In model | |
| Global Economic Policy Uncertainty GEPUCURRENT · FRED | 241.7 Index −41.02 | 2026-07-01 pub. 2026-08-10 | — | Not in model | |
| Philadelphia Fed Anxious Index ANXIOUS · FED_REGIONAL | no free source | — | Benchmark |
Block 15 · Composite & Model Benchmarks5 of 19 listed · 0 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| CFNAI three-month average CFNAIMA3 · FRED | 0.010 Index +0.020 | 2026-08-01 pub. 2026-09-21 | — | Benchmark | |
| Chauvet-Piger smoothed probability RECPROUSM156N · FRED | 0.620 Percent +0.380 | 2026-08-01 pub. 2026-10-01 | — | Benchmark | |
| Term spread + EBP probit TS_EBP · CycleWatch | 4.15 % chance of recession within 12 months +0.398 | 2026-10-06 pub. 2026-10-06 | — | Benchmark | |
| Term spread alone (same target and training months as the model) TS_ONLY · CycleWatch | 7.33 % chance of recession within 12 months +0.438 | 2026-10-06 pub. 2026-10-06 | — | Benchmark | |
| Unconditional base rate BASE_RATE · CycleWatch | 13.31 % chance of recession within 12 months +0.000 | 2026-10-06 pub. 2026-10-06 | — | Benchmark |
Block 16 · Structural & Slow-Moving4 of 15 listed · 0 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Net immigration flows IMMIGRATION · SCRAPE | no free source | — | Context | ||
| Prime-age participation trend LNS11300060 · FRED | 83.70 Percent +0.300 | 2026-09-01 pub. 2026-10-02 | — | Context | |
| Trend labor productivity OPHNFB · FRED | 120.0 Index 2017=100 +0.423 | 2026-04-01 pub. 2026-08-06 | — | Context | |
| Working-age population growth LFWA64TTUSM647S · FRED | 210,408,200 Persons −14,200 | 2026-07-01 pub. 2026-08-17 | — | Context |
Block 17 · Alternative & High-Frequency2 metrics · 0 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| Indeed job postings (daily) IHLIDXNSAUS · FRED | licensed: not shown on the public site | 2026-09-18 pub. 2026-09-23 | — | Not in model | |
| Weekly claims nowcast input ICSA · FRED | 197,000 Number −1,000 | 2026-09-26 pub. 2026-10-01 | — | Not in model |
Supporting series4 of 8 listed · 0 in the model
| Indicator | Latest | Updated | Last 24 | Signal | Use |
|---|---|---|---|---|---|
| 3-Month Treasury Bill Secondary Market Rate, Discount Basis TB3MS · FRED | 3.94 Percent +0.220 | 2026-09-01 pub. 2026-10-01 | — | Supporting | |
| Market Yield on U.S. Treasury Securities at 10-Year Constant Maturity, Quoted on an Investment Basis GS10 · FRED | 4.99 Percent +0.310 | 2026-09-01 pub. 2026-10-01 | — | Supporting | |
| Moody's Seasoned Aaa Corporate Bond Yield AAA · FRED | licensed: not shown on the public site | 2026-09-01 pub. 2026-10-01 | — | Supporting | |
| Moody's Seasoned Baa Corporate Bond Yield BAA · FRED | licensed: not shown on the public site | 2026-09-01 pub. 2026-10-01 | — | Supporting |
Licensed sources (ISM, Conference Board, S&P Global PMIs and others) show "no free source". Series from ICE BofA, Moody's, S&P, Nasdaq, UMich, CBOE, Freddie Mac, Indeed and Cass are licensed for personal use only: they feed the model, but their values and standardized signals are withheld here; only their dates are shown.
US Consumer
What American households pay, earn, spend and owe: from the price of a dozen eggs to credit-card delinquencies, with every item in the Consumer Price Index. Shown for context; none of it feeds the recession model.
Everyday prices
Average prices BLS collects across US cities, August 2026
| Item | Now | A year ago | 1 month | 12 months | 5 years |
|---|---|---|---|---|---|
| Eggs, grade A large per dozen | $2.27 | $3.59 | +3.8%° | −36.7% | +32.9% |
| Milk, whole per gallon | $4.23 | $4.17 | −1.9%° | +1.4% | +18.8% |
| Bread, white per lb | $1.82 | $1.84 | +0.2%° | −1.0% | +24.3% |
| Ground beef per lb | $6.92 | $6.32 | +0.6%° | +9.6% | +54.9% |
| Chicken, whole per lb | $2.01 | $2.08 | +0.3%° | −3.1% | +36.7% |
| Chicken breast, boneless per lb | $4.17 | $4.21 | +0.5%° | −0.9% | +18.0% |
| Bacon, sliced per lb | $6.61 | $7.21 | +0.3%° | −8.4% | −6.9% |
| Cheddar cheese per lb | $5.98 | $6.12 | +4.2%° | −2.3% | +12.1% |
| Coffee, ground roast per lb | $9.30 | $8.87 | −0.2%° | +4.8% | +96.8% |
| Bananas per lb | $0.65 | $0.67 | +0.3%° | −2.1% | +10.7% |
| Oranges, navel per lb | $1.71 | $1.79 | — | −4.8% | — |
| Tomatoes per lb | $1.98 | $1.93 | −1.3%° | +2.7% | +6.4% |
| Flour, all purpose per lb | $0.55 | $0.56 | +0.9%° | −1.6% | +47.7% |
| Sugar per lb | $1.02 | $1.04 | +0.0%° | −1.5% | +50.4% |
| Gasoline, regular per gallon | $4.20 | $3.29 | +2.6%° | +27.5% | +29.0% |
| Electricity per kWh | $0.196 | $0.190 | −0.5%° | +3.2% | +36.1% |
| Utility (piped) gas per therm | $1.71 | $1.63 | +0.6%° | +4.9% | +35.3% |
Weekly gasoline price (EIA, regular): $4.35 a gallon on Oct 5, 2026, against $3.12 a year earlier. Average prices are not seasonally adjusted (° on the monthly change), and they track specific products, so they can move differently from the CPI category they sit in.
Paychecks vs prices
Is pay keeping up with prices?
In the year to August 2026, average hourly pay rose 3.1% (to $37.76) and consumer prices 3.4%, so pay lagged prices: real wages fell 0.2%. For production and nonsupervisory workers, real pay was flat.
Real wages = (1 + wage growth) ÷ (1 + inflation) − 1, with average hourly earnings of all private employees (BLS) and the CPI, both seasonally adjusted. Average pay also rises when lower-paid jobs are lost, as in 2020, so real wages can jump in a downturn.
Spending and income
After inflation, from the national accounts
| Measure | Latest | Over 12 months | Month |
|---|---|---|---|
| Real consumer spending BEA | $16.96 tn a year | +2.6% | Aug 2026 |
| Real disposable income BEA | $18.41 tn a year | +1.3% | Aug 2026 |
| Retail and food services sales Census | $738 bn a month | +5.4% | Aug 2026 |
| Personal saving rate BEA | 4.1% | −1.1 pts | Aug 2026 |
Spending growing faster than income for long means households are saving less or borrowing more. Retail sales are in dollars, not adjusted for inflation.
Credit
What households owe and how they are coping
| Measure | Latest | Over 12 months | Month |
|---|---|---|---|
| Revolving credit (mostly credit cards) Federal Reserve | $1.36 tn | +3.6% | Jul 2026 |
| Total consumer credit Federal Reserve | $5.19 tn | +2.6% | Jul 2026 |
| Credit card delinquency rate, banks Federal Reserve | 2.9% | −0.2 pts | Q2 2026 |
| Debt service, share of disposable income Federal Reserve | 11.1% | −0.01 pts | Q2 2026 |
Delinquency and debt-service figures are quarterly and arrive with a lag. Consumer credit excludes mortgages.
Jobs
Hiring, openings, layoffs and claims
Employers added 29,000 jobs in September (3-month average +51,000). Unemployment is 4.2%. In August 2026 there were 1.01 job openings per unemployed person (0.94 a year earlier). Initial jobless claims were 197,000 in the week to Sep 26, 2026 (4-week average 200,000).
| Measure | Latest | Over 12 months | Month |
|---|---|---|---|
| Payroll jobs BLS | +29,000 in the month | +0.3% | Sep 2026 |
| Unemployment rate BLS | 4.2% | −0.2 pts | Sep 2026 |
| Unemployed people BLS | 7.11 million | −6.5% | Sep 2026 |
| Job openings BLS | 7.08 million | +2.3% | Aug 2026 |
| Layoffs and discharges BLS | 1.64 million | −10.4% | Aug 2026 |
| Quits rate BLS | 1.9% | −0.1 pts | Aug 2026 |
| Employment rate, ages 25–54 BLS | 80.7% | +0.00 pts | Sep 2026 |
| Initial jobless claims, weekly Labor Department | 197,000 | −12.4% | Sep 26, 2026 |
| Continuing jobless claims, weekly Labor Department | 1,701,000 | −11.5% | Sep 19, 2026 |
More openings than unemployed people means a tight job market; the gap closing fast has often come before a downturn. Weekly claims are the earliest official sign of layoffs. Sources: BLS employment situation and JOLTS, Labor Department, via FRED.
Unemployment by state
August 2026, BLS
10 of 51 states show a recession-style rise in unemployment: a 3-month average at least 0.5 points above its low of the past year (the Sahm rule, state by state). Since 1980 the typical month has had 6; when the 1990, 2001 and 2008 recessions began, 11–18 states did.
Tap or hover a state for its unemployment rate, recent changes and the last two years.
All statessorted by rise from the 12-month low
| State | Rate | 1 month | 12 months | Rise from low | |
|---|---|---|---|---|---|
| Connecticut | 5.1% | −0.1 | +1.2 | +1.30 | rising |
| Oklahoma | 4.3% | +0.0 | +1.0 | +1.07 | rising |
| Florida | 4.5% | −0.1 | +0.6 | +0.80 | rising |
| New Mexico | 4.7% | −0.1 | +0.7 | +0.77 | rising |
| Alabama | 3.4% | +0.0 | +0.6 | +0.63 | rising |
| Arizona | 4.9% | +0.0 | +0.6 | +0.60 | rising |
| Illinois | 4.7% | −0.2 | +0.4 | +0.60 | rising |
| Minnesota | 4.4% | +0.1 | +0.6 | +0.60 | rising |
| Hawaii | 2.7% | +0.0 | +0.5 | +0.53 | rising |
| Washington | 4.9% | −0.1 | +0.3 | +0.50 | rising |
| Kentucky | 4.7% | +0.0 | +0.1 | +0.47 | |
| Virginia | 3.6% | −0.1 | +0.3 | +0.43 | |
| Texas | 4.4% | −0.1 | +0.2 | +0.30 | |
| Wisconsin | 3.2% | −0.1 | +0.2 | +0.20 | |
| Colorado | 4.0% | +0.1 | +0.1 | +0.13 | |
| Delaware | 4.7% | −0.1 | −0.1 | +0.13 | |
| Louisiana | 4.2% | −0.2 | −0.1 | +0.13 | |
| New York | 4.3% | −0.1 | −0.1 | +0.13 | |
| Kansas | 3.8% | +0.0 | +0.1 | +0.10 | |
| Maryland | 4.1% | −0.1 | −0.1 | +0.10 | |
| Michigan | 5.0% | +0.1 | +0.1 | +0.07 | |
| Utah | 3.5% | −0.1 | +0.0 | +0.07 | |
| Idaho | 3.6% | +0.0 | +0.0 | +0.03 | |
| Maine | 3.2% | +0.1 | −0.1 | +0.03 | |
| West Virginia | 4.0% | −0.1 | −0.2 | +0.03 | |
| Georgia | 3.2% | −0.1 | −0.1 | +0.00 | |
| Indiana | 3.3% | +0.0 | −0.4 | +0.00 | |
| Iowa | 3.2% | +0.0 | −0.3 | +0.00 | |
| Montana | 3.2% | +0.0 | −0.1 | +0.00 | |
| Nebraska | 2.9% | +0.0 | +0.0 | +0.00 | |
| South Dakota | 2.0% | +0.0 | +0.0 | +0.00 | |
| Vermont | 2.6% | +0.0 | +0.0 | +0.00 | |
| Arkansas | 3.9% | −0.1 | −0.2 | −0.03 | |
| Oregon | 5.1% | −0.1 | −0.2 | −0.03 | |
| California | 5.1% | +0.0 | −0.4 | −0.07 | |
| Massachusetts | 4.3% | −0.1 | −0.2 | −0.07 | |
| Mississippi | 3.4% | −0.2 | −0.4 | −0.07 | |
| New Hampshire | 2.8% | +0.0 | −0.3 | −0.07 | |
| North Carolina | 3.5% | −0.1 | −0.4 | −0.07 | |
| North Dakota | 2.2% | +0.0 | −0.4 | −0.07 | |
| Tennessee | 3.4% | +0.0 | −0.1 | −0.07 | |
| Alaska | 4.3% | +0.0 | −0.3 | −0.10 | |
| Missouri | 3.5% | −0.1 | −0.5 | −0.10 | |
| District of Columbia | 5.7% | −0.2 | −0.6 | −0.13 | |
| Nevada | 4.8% | −0.2 | −0.4 | −0.13 | |
| New Jersey | 4.3% | −0.1 | −1.2 | −0.13 | |
| Ohio | 3.3% | −0.1 | −1.2 | −0.13 | |
| South Carolina | 4.1% | −0.1 | −0.3 | −0.13 | |
| Wyoming | 3.0% | +0.0 | −0.3 | −0.13 | |
| Pennsylvania | 3.7% | −0.2 | −0.7 | −0.17 | |
| Rhode Island | 3.7% | −0.2 | −0.5 | −0.20 |
State unemployment rates are seasonally adjusted (BLS Local Area Unemployment Statistics, via FRED) and come out about three weeks after the national figure. The count of rising states is being tested as a candidate model input; it does not move today's odds.
Housing
Building, sales, prices and rents
Builders started homes at a 1.27 million annual pace in August 2026 (−1.2% on a year earlier). Permits, which lead starts by a month or two, are +4.2% over the year. Home prices (FHFA) are +2.6% over 12 months. New homes for sale would last 8.5 months at the current sales pace.
| Measure | Latest | Over 12 months | Month |
|---|---|---|---|
| Housing starts Census | 1.27m a year | −1.2% | Aug 2026 |
| Building permits Census | 1.40m a year | +4.2% | Aug 2026 |
| New home sales Census | 684k a year | −2.0% | Aug 2026 |
| Median new home price Census | $393,700 | −5.8% | Aug 2026 |
| Home prices (FHFA purchase-only index) FHFA | index 443.5 | +2.6% | Jul 2026 |
| New homes for sale, months of supply Census | 8.5 months | +0.00 months | Aug 2026 |
| Rental vacancy rate Census | 7.3% | +0.3 pts | Q2 2026 |
Housing turns early: starts and permits fell well before most past recessions. Mortgage rates, Case-Shiller prices and existing-home sales are licensed and are not shown here. Sources: Census Bureau, FHFA, via FRED.
Inflation, item by item
The Consumer Price Index, every category BLS publishes
Every item in the Consumer Price Index, 305 of them from eggs to airline fares: how much each price changed in August 2026 and over the past year. Prices overall rose 3.4% in the year to August 2026. Next release: Wednesday 14 October.
Biggest rises in August
| Item | 1 month | 12 months |
|---|---|---|
| Fuel oil | +10.1% | +52.0% |
| Other motor fuels | +9.6% | +44.0% |
| Fuel oil and other fuels | +6.8% | +30.2% |
| Salad dressing | +5.0% | −0.7% |
| Women's dresses | +4.9% | −1.0% |
| Motor fuel | +4.1% | +27.9% |
| Gasoline, unleaded regular | +4.0% | +28.1% |
| Gasoline (all types) | +3.9% | +27.4% |
Biggest falls in August
| Item | 1 month | 12 months |
|---|---|---|
| Lettuce | −6.2% | −2.2% |
| Other video equipment | −6.1% | +4.6% |
| Tomatoes | −3.0% | +5.8% |
| Apples | −2.8% | +5.4% |
| Women's underwear, nightwear, swimwear, and accessories | −2.5% | +3.6% |
| Telephone hardware, calculators, and other consumer information items | −2.4% | −14.1% |
| Toys | −2.3% | +2.3% |
| Moving, storage, freight expense | −1.9% | −4.6% |
34 of 305 items shown. See all 305 CPI items › · Download them (CSV)
| Item | 1 month | 12 months | 3 mo ann. | Index | 12-month change, last 2 years |
|---|---|---|---|---|---|
| All items | +0.4% | +3.4% | +0.2% | 335.0 | |
| Food and beverages | +0.1% | +2.6% | +1.6% | 347.2 | |
| Food | +0.1% | +2.7% | +1.6% | 350.4 | |
| Alcoholic beverages | +0.1% | +1.6% | +1.3% | 301.7 | |
| Housing | +0.2% | +3.1% | +1.7% | 360.2 | |
| Shelter | +0.3% | +3.0% | +2.1% | 430.6 | |
| Fuels and utilities | +0.1% | +5.0% | −1.4% | 353.3 | |
| Household furnishings and operations | +0.1% | +2.0% | +1.5% | 156.1 | |
| Apparel | +0.0% | +3.6% | −1.8% | 136.8 | |
| Men's and boys' apparel | +0.6% | +3.7% | +4.8% | 133.1 | |
| Women's and girls' apparel | +0.2% | +3.5% | −2.1% | 113.0 | |
| Footwear | −0.3% | +3.6% | −0.5% | 152.0 | |
| Infants' and toddlers' apparel | −0.7% | −3.4% | −13.5% | 121.6 | |
| Jewelry and watches | −1.6% | +8.2% | −17.6% | 229.7 | |
| Transportation | +1.2% | +6.2% | −7.0% | 290.8 | |
| Private transportation | +1.1% | +5.3% | −9.6% | 289.2 | |
| Public transportation | +2.3% | +15.4% | +21.1% | 294.0 | |
| Medical care | −0.2% | +1.6% | −0.1% | 593.0 | |
| Medical care commodities | −0.2% | −2.7% | −3.9% | 405.5 | |
| Medical care services | −0.2% | +2.5% | +0.7% | 653.6 | |
| Recreation | +0.0% | +2.7% | +3.0% | 145.2 | |
| Video and audio | +0.1% | +3.9% | +6.2% | 124.3 | |
| Pets, pet products and services | +0.2% | +3.2% | +3.6% | 236.8 | |
| Sporting goods | +1.0% | +5.3% | +11.3% | 128.9 | |
| Photography | +1.7% | +2.4% | −7.5% | 87.37 | |
| Other recreational goods | −1.8% | +2.5% | +2.3% | 34.07 | |
| Other recreation services | −0.4% | +0.8% | −1.9% | 211.0 | |
| Recreational reading materials | +2.9% | +4.2% | +22.1% | 307.8 | |
| Education and communication | +1.6% | +2.1% | +5.7% | 150.3 | |
| Education | +0.8% | +3.3% | +6.0% | 322.2 | |
| Communication | +2.3% | +1.1% | +5.4% | 73.82 | |
| Other goods and services | +0.2% | +4.2% | +1.4% | 608.0 | |
| Tobacco and smoking products | +0.6% | +6.3% | +1.8% | 111.3 | |
| Personal care | +0.1% | +3.8% | +1.4% | 303.8 |
Special aggregates
Cross-cuts BLS publishes alongside the tree
| Aggregate | 1 month | 12 months | 3 mo ann. |
|---|---|---|---|
| All items less food and energy (core) | +0.3% | +2.4% | +2.0% |
| Energy | +2.1% | +16.3% | −19.1% |
| All items less shelter | +0.5% | +3.6% | −0.8% |
| All items less energy | +0.3% | +2.5% | +1.9% |
| Commodities | +0.6% | +4.0% | −2.9% |
| Commodities less food and energy commodities (core goods) | +0.1% | +0.7% | +0.9% |
| Services | +0.3% | +3.1% | +2.1% |
| Services less energy services (core services) | +0.3% | +3.0% | +2.4% |
| Services less rent of shelter | +0.3% | +3.1% | +1.3% |
| Durables | +0.0% | −0.3% | +1.2% |
| Nondurables | +0.7% | +5.8% | −4.3% |
Source: U.S. Bureau of Labor Statistics, Consumer Price Index for All Urban Consumers (CPI-U), U.S. city average; index 1982–84 = 100. Monthly changes are seasonally adjusted where BLS adjusts the item (256 of 305); the rest are marked ° and include normal seasonal swings, such as clothing discounts in January. 12-month changes are unadjusted, as BLS reports them. BLS did not collect October 2025 prices (federal shutdown), so changes that span that month are blank. Updated automatically on the day BLS publishes. Shown for context: these figures do not feed the recession model.
Sources: U.S. Bureau of Labor Statistics, Bureau of Economic Analysis, Census Bureau, Federal Reserve Board and Energy Information Administration, via FRED. Updated with the daily data check.
Track record
How the forecasts would have done since 1980, each month made with only what was known at the time (a walk-forward test). Five recessions fall in the test window, so every figure here carries wide uncertainty.
Alarms and recessions since 1980
Shaded: recessions. Bars: months each forecast stood at or above the 25% alarm line. Above each recession: whether the 12-block model warned in the year before it began.
Bottom line
- Kill rule K4 is in force: the yield-curve benchmark (term spread + EBP) ranks pre-recession months better than the 12-block model in both windows (AUROC 0.899 / 0.907 against 0.887 / 0.898), so it carries the headline. The model keeps the better probability accuracy (Brier 0.084 / 0.071 against 0.098 / 0.090; lower is better).
- The AUROC gap between the two is 0.012 / 0.010 (1980+ / 1990+), well inside its uncertainty (roughly ±0.1 in a resampling test in the review of Oct 5, 2026): on ranking, the two cannot be told apart.
- Out of sample the model ranks risky months about as well as the yield curve alone (AUROC 0.887 / 0.898 against 0.894 / 0.876); the difference is not statistically significant.
- At the 25% line the model spent 50 months in false alarm against 89 for the benchmark, and warned of 4 of 5 recessions (8 months ahead on average) against 5 of 5 (9 months ahead on average).
- Before about 2010 most inputs are later-revised data, and several indexes are used only from their publication date. Expect live performance below this record.
Calibration
When the model said X%, how often a recession followed. On the dashed line is perfect; dot size is the number of months.
Scorecard
AUROC ranks pre-recession months (0.5 is no skill, 1 is perfect). AUPRC rewards precision on the rare pre-recession months. Brier is probability error; lower is better.
1980–2024, excluding COVID
| Predictor | AUROC | Before audit | AUPRC | Brier |
|---|---|---|---|---|
| Model v1 (unconstrained) | 0.912 | 0.887 | 0.448 | 0.074 |
| Yield-curve benchmark (term spread + EBP probit) headline | 0.899 | 0.880 | 0.626 | 0.098 |
| Term spread probit | 0.894 | 0.875 | 0.474 | 0.086 |
| Model v2 (sign-constrained) | 0.887 | 0.879 | 0.354 | 0.084 |
| Fed Board EBP model (published; in-recession target) | 0.706 | 0.766 | 0.258 | 0.109 |
| CFNAI 3-month avg (inverted) | 0.699 | 0.698 | 0.200 | |
| Sahm rule (real-time) | 0.692 | 0.685 | 0.158 | |
| Chauvet-Piger (in recession now) | 0.668 | 0.690 | 0.180 | |
| OECD leading indicator (inverted) | 0.430 | 0.432 | 0.092 | |
| Base rate | 0.420 | 0.424 | 0.102 | 0.103 |
1990–2024, excluding COVID
| Predictor | AUROC | Before audit | AUPRC | Brier |
|---|---|---|---|---|
| Model v1 (unconstrained) | 0.917 | 0.909 | 0.432 | 0.061 |
| Yield-curve benchmark (term spread + EBP probit) headline | 0.907 | 0.898 | 0.437 | 0.090 |
| Model v2 (sign-constrained) | 0.898 | 0.899 | 0.343 | 0.071 |
| Term spread probit | 0.876 | 0.863 | 0.269 | 0.092 |
| Fed Board EBP model (published; in-recession target) | 0.687 | 0.742 | 0.245 | 0.098 |
| CFNAI 3-month avg (inverted) | 0.687 | 0.685 | 0.146 | |
| Chauvet-Piger (in recession now) | 0.599 | 0.642 | 0.111 | |
| Sahm rule (real-time) | 0.594 | 0.584 | 0.100 | |
| Base rate | 0.421 | 0.431 | 0.100 | 0.086 |
| OECD leading indicator (inverted) | 0.246 | 0.249 | 0.055 |
The yield-curve benchmark (term spread + EBP) uses the excess bond premium's back-history before it was published in 2012, which flatters it. The Fed Board's published probability is an in-sample fit. The Fed Board and Chauvet-Piger probabilities estimate the chance of being in recession, not of one beginning within 12 months, so those rows are not like-for-like. The OECD leading indicator scores below 0.5 because it is entered with the wrong orientation; treat that row as mis-specified.
Alarm record at 25%
| Recession began | Yield-curve benchmark (headline) | 12-block model |
|---|---|---|
| Feb 1980 | warned 1 month ahead · peak 100% | missed peak 22% |
| Aug 1981 | warned 10 months ahead · peak 100% | warned 11 months ahead · peak 42% |
| Aug 1990 | warned 12 months ahead · peak 99% | warned 6 months ahead · peak 27% |
| Apr 2001 | warned 12 months ahead · peak 100% | warned 4 months ahead · peak 56% |
| Jan 2008 | warned 12 months ahead · peak 60% | warned 11 months ahead · peak 80% |
| Mar 2020 | Excluded: an outside shock (the pandemic) with no economic warning to detect; left out of training and scoring. | |
1980 had only one month of out-of-sample forecasts before it began.
False alarms at 25%
Yield-curve benchmark (headline)
| Period above 25% | Peak | What followed |
|---|---|---|
| Apr 1986, Jun – Jul 1986, Oct 1986 – Mar 1987 | 71% | No recession; 9 months above the line in total |
| Nov 1988 – Jul 1989 | 99% | Fell back below 25%; the recession began 13 months later (Aug 1990) |
| Jun – Jul 1995, Oct 1995 – Feb 1996 | 41% | No recession; 7 months above the line in total |
| Jan – Mar 1998, May 1998 – Mar 1999 | 61% | No recession; 14 months above the line in total |
| Jan – Mar 2006, Jul – Dec 2006 | 66% | Fell back below 25%; the recession began 13 months later (Jan 2008); 9 months above the line in total |
| Jan – Feb 2019 | 50% | No recession |
| Aug 2022 – Dec 2024, Feb – Nov 2025 | 100% | No recession so far; the latest months are not yet resolved (NBER dating lag); 39 months above the line in total |
12-block model
| Period above 25% | Peak | What followed |
|---|---|---|
| Nov 1988 – Jul 1989 | 58% | Fell back below 25%; the recession began 13 months later (Aug 1990) |
| Nov 1995 – Mar 1996, Jun 1996, Aug – Sep 1996 | 35% | No recession; 8 months above the line in total |
| Jul – Oct 2011 | 67% | No recession |
| May 2022 – Jun 2024, Aug 2024, Nov – Dec 2024 | 83% | No recession so far; the latest months are not yet resolved (NBER dating lag); 29 months above the line in total |
From each forecast's walk-forward series: months at or above the alarm line with no recession beginning within 12 months. Runs up to three months apart count as one episode. Left out: single months, months inside recessions and the first 6 months after one ends. Recent months stay open until the NBER could have dated a recession.
Integrity
Two safeguards, and what each one does and does not prove.
Checked before publishing
Checked 2026-10-06: 0 critical, 5 warnings.
The warnings
- AUROC_BELOW_HALF · OECD leading indicator (inverted): OECD leading indicator (inverted) has AUROC 0.430 in 1980–2024, excluding COVID: it ranks risk backwards.
- AUROC_BELOW_HALF · OECD leading indicator (inverted): OECD leading indicator (inverted) has AUROC 0.246 in 1990–2024, excluding COVID: it ranks risk backwards.
- DISCONTINUED · USSLIND: Philadelphia Fed leading index was last published 2020-04-14; it looks discontinued.
- WEEKLY_HEADLINE · weekly: Weekly summary does not show the headline figure.
- BLOCK_JUMP · Policy Uncertainty: Policy Uncertainty moved -2.66 since the last run.
- Every figure is recomputed from the page's own numbers: probit arithmetic, block averages, signs.
- The headline must follow the kill rules, and match the forecast log.
- Stale or discontinued series, and any rewritten log entry, are flagged. A critical finding stops the page from being published.
Verify the forecast log
Each forecast is chained to the one before it by a SHA-256 hash, so editing any past entry breaks the chain from that point on. Check it here: your browser recomputes every hash.
A hash chain alone cannot stop whoever publishes the log from rewriting every entry and recomputing every hash. So each new entry's hash is also stamped with OpenTimestamps and anchored in Bitcoin, where it cannot be changed: the proofs are on the Data page, and anyone can check them at opentimestamps.org. Entries from before Oct 5, 2026 were logged before stamping began; the first stamp covers them all.
Data freshness
When each source last updated successfully. Every source is on schedule.
| Source | Last update | Age | Expected | Status |
|---|---|---|---|---|
| FRED economic data Federal Reserve Bank of St. Louis | Oct 6, 2026 | today | Every weekday | On schedule |
| Excess bond premium Federal Reserve Board | Oct 6, 2026 | today | Every weekday (published monthly) Latest month: September 2026 | On schedule |
| Consumer prices (CPI) Bureau of Labor Statistics | Oct 5, 2026 | 1 day | Monthly, about two weeks after the month Latest month: August 2026 | On schedule |
| US Consumer series FRED | Oct 6, 2026 | today | Every weekday | On schedule |
| State unemployment rates FRED (BLS), 51 states | Sep 18, 2026 | 18 days | Monthly, about three weeks after the month Latest month: August 2026 | On schedule |
| Treasury yield curve (GSW) Federal Reserve Board | Oct 4, 2026 | 2 days | About weekly | On schedule |
| Neutral interest rate (HLW r*) New York Fed | Oct 3, 2026 | 3 days | Checked weekly (changes quarterly) | On schedule |
Ages are counted on the day this page was built. A late source keeps its previous copy: the forecast uses the newest data in hand, and the monitor flags inputs that stop updating.
Automated audit, Oct 2, 2026
Four AI-assisted review passes, run by the maintainer (not an independent third party), checked for leakage of future data, code bugs, data accuracy and robustness. All 28 first-release figures and 32 current values checked matched official sources, and the code was found not to look ahead. Fixed as a result: an inverted stock-market signal, a storage bug, three kill-rule logic errors, outage handling, mislabeled series and license tags, a yield-curve splice, release delays, and use of indexes before they existed.
Changes, Oct 3, 2026
- K4 corrected to compare with the yield-curve benchmark (term spread + EBP) in both windows; the headline now follows it. No logged forecast was edited.
- False-alarm table generated from the forecast series; scorecard footnotes for rows that are not like-for-like.
- Portfolio tool: compared with the S&P in the same recession windows; cash and bond funds handled; broker holdings files and mutual-fund share classes accepted.
- New design: CycleWatch.
Weekly
Written every Friday from the data published that week: the releases, the markets and what moved the model.
Latest week · headline 4.4% (yield-curve benchmark), model 1.6%
- Kill rule K4 is in force: the yield-curve benchmark (term spread + EBP), 4.4% this week, is the headline. The model figures below are the 12-block model's.
- Recession probability 1.6%, down from 2.5% in the September end-of-month forecast (yield curve alone: 7.4%).
- Nonfarm payrolls: +29k in September 2026 (prior +133k).
- Unemployment rate: 4.2% in September 2026 (prior 4.1%).
- Initial jobless claims: 197k (week to 26 Sep; prior 198k).
- Continuing jobless claims: 1,701k (week to 19 Sep; prior 1,712k).
- Core PCE inflation: 3.0% year on year in August 2026 (prior 3.0%).
- Real income (ex transfers): -0.1% on the month in August 2026 (prior +0.2%).
- Biggest model mover: Labor Market (-0.07 to +0.22), reducing risk.
- Next week: Chicago Fed National Financial Conditions Index (Wed); Unemployment Insurance Weekly Claims Report (Thu).
What came out
| Nonfarm payrolls | +29k in September 2026 (prior +133k) | Fri Oct 2 |
| Unemployment rate | 4.2% in September 2026 (prior 4.1%) | Fri Oct 2 |
| Initial jobless claims | 197k (week to 26 Sep; prior 198k) | Thu Oct 1 |
| Continuing jobless claims | 1,701k (week to 19 Sep; prior 1,712k) | Thu Oct 1 |
| Core PCE inflation | 3.0% year on year in August 2026 (prior 3.0%) | Wed Sep 30 |
| Real income (ex transfers) | -0.1% on the month in August 2026 (prior +0.2%) | Wed Sep 30 |
| Real consumer spending | +0.6% on the month in August 2026 (prior +0.1%) | Wed Sep 30 |
| Job openings (JOLTS) | 7.08m in August 2026 (prior 7.33m) | Tue Sep 29 |
| Chicago Fed financial conditions | -0.5 in week to 25 Sep (prior -0.6) | Wed Sep 30 |
stronger weaker than the prior reading, for the economy.
Markets this week
| Market | Level | Week |
|---|---|---|
| Yield curve (10y − 3m) | 1.09% | +16 bp |
| 10-year Treasury yield (latest day; the Signals tab shows the monthly average) | 5.24% | +7 bp |
| WTI oil | 96.16 | +12.8% |
| Broad dollar index | 120.33 | +0.0% |
What moved the model
| Labor Market | -0.07 → +0.22 | reducing risk |
| The Yield Curve Complex | -0.37 → -0.24 | reducing risk |
| Policy Uncertainty | -4.00 → -1.34 | no weight in the model |
| Household & Consumer | +0.54 → +0.42 | no weight in the model |
A block score averages every signal in the block against its own history, so it can improve after a weak headline release (a small payroll gain, say) when its other signals, such as claims, hours and employment rates, strengthen. The Today tab lists the strongest and weakest signals in the block that moved most.
Coming up next week
| Wed Oct 7 | Chicago Fed National Financial Conditions Index |
| Thu Oct 8 | Unemployment Insurance Weekly Claims Report |
Portfolio
See how your holdings have behaved in past recessions, weighted by today's odds. Your files are read on this device only: nothing is uploaded, sent or saved.
Choose one or more CSV exports: Yahoo Finance portfolio files (Symbol, Current Price, Quantity, Transaction Type) or broker holdings downloads such as Vanguard's (Account Number, Symbol, Shares, Share Price, Total Value). Each Yahoo file is one portfolio; each account in a broker file becomes its own portfolio. Identical files are counted once.
Risk library: 1,728 stocks and ETFs, built Oct 3, 2026. Recession probability used: 4.1%, the yield-curve benchmark (term spread + EBP) headline while kill rule K4 stands (the model reads 2.5%).
Method
Every layer of the calculation for this forecast (Oct 6, 2026), with the Python behind each step. The headline is the yield-curve benchmark (term spread + excess bond premium); both are shown in full.
The headline: the yield-curve benchmark (term spread + excess bond premium)
A two-variable probit, the benchmark kill rule K4 names. It carries the headline while K4 stands.
| Term | Input x | Weight β | β × x |
|---|---|---|---|
| Intercept | -0.5252 | ||
| 10-year minus 3-month Treasury spread, percentage points | +1.0833 | -0.8545 | -0.9257 |
| Excess bond premium, percentage points | -0.3097 | +0.9134 | -0.2829 |
| Total (η) | -1.7337 |
Probability = Φ(-1.7337) = 4.15% (matches the logged figure)
Fitted at this forecast on 527 months (1973-04 to 2024-04) whose outcome the NBER had made known, covering the recessions that began after Nov 1973, Jan 1980, Jul 1981, Jul 1990, Mar 2001 and Dec 2007. No penalty and no sign constraint: a steeper curve lowers the probability and a higher excess bond premium raises it, as estimated. The inputs are the month's average spread and the latest published premium; the premium's history before 2012 was not available in real time, which flatters this model's record.
View the Python · recession.live.benchmark_detail 33 lines
def benchmark_detail(T: pd.Timestamp, scores: Optional[pd.DataFrame] = None,
paths: Optional[pd.DataFrame] = None) -> Optional[dict]:
"""
The term spread + EBP probit at decision date T, refitted exactly as
forecast() fits it (same training rows, same penalty), for the Method tab:
coefficients, today's inputs, eta and the probability. None if the stored
features no longer reach T.
"""
from scipy.stats import norm
fd = features.FEATURE_DIR
scores = pd.read_parquet(fd / "block_scores.parquet") if scores is None else scores
paths = pd.read_parquet(fd / "feature_paths.parquet") if paths is None else paths
panel = _panel(scores, paths)
if T not in panel.index:
return None
blocks = model.block_columns(panel)
present = panel[blocks].notna().sum(axis=1) >= model.MIN_BLOCKS_PRESENT
tr = model._training_rows(panel, T) & present
rows = tr & panel["term_spread"].notna() & panel["ebp"].notna()
fit = Probit(1e-6).fit(panel.loc[rows, ["term_spread", "ebp"]].values, panel.loc[rows, "y"].values)
x = panel.loc[T, ["term_spread", "ebp"]]
if x.isna().any():
return None
a, b1, b2 = (float(v) for v in fit.beta)
eta = a + b1 * float(x["term_spread"]) + b2 * float(x["ebp"])
peaks = sorted(str(p)[:7] for p in panel.loc[rows & (panel["y"] == 1), "peak"].dropna().unique())
lo, hi = (float(v) for v in fit.interval(x.values.astype(float)[None, :])[0])
return {"intercept": a, "b_ts": b1, "b_ebp": b2, "ts": float(x["term_spread"]), "ebp": float(x["ebp"]),
"eta": eta, "p": float(norm.cdf(eta)), "interval_80": (lo, hi),
"train_months": int(rows.sum()), "peaks": peaks,
"first": f"{panel.index[rows].min():%Y-%m}", "last": f"{panel.index[rows].max():%Y-%m}"}
View the Python · recession.live.headline 13 lines
def headline(f: dict) -> dict:
"""
The number published as the headline. The model's own forecast is always
logged; when a triggered kill rule says so, the benchmark is published
instead (if it could be computed for the same date).
"""
rules = [k["id"] for k in f.get("kill_criteria", [])
if k["status"] == "TRIGGERED" and k["id"] in BENCHMARK_HEADLINE_RULES]
bench = f["benchmarks"].get(K4_BENCHMARK)
if rules and bench is not None:
return {"series": "benchmark", "probability": bench, "rules": rules}
return {"series": "model", "probability": f["probability"], "rules": rules}
View the Python · recession.live.k4_status 30 lines
def k4_status(scorecard: Optional[pd.DataFrame] = None) -> tuple[str, str]:
"""
K4 from the latest Stage 4 scorecard: TRIGGERED if the term spread + EBP
benchmark (the one the rule's action names) beats the model on AUROC in
EITHER window. The first version compared against the plain term spread
on 1990+ only, which reported "ok" while the named benchmark was ahead in
both windows (page corrections, 3 Oct 2026). AUPRC is reported, not judged.
"""
if scorecard is None:
sc = model.STAGE4_DIR / "scorecard.csv"
if not sc.exists():
return "pending", "no scorecard yet"
scorecard = pd.read_csv(sc)
card = scorecard.set_index(["window", "predictor"])
auroc, auprc, lost = [], [], []
for window, short in K4_WINDOWS:
try:
m, b = card.loc[(window, V2)], card.loc[(window, K4_BENCHMARK)]
except KeyError:
return "pending", f"scorecard has no {short} rows for the model and {K4_BENCHMARK}"
auroc.append(f"{m['AUROC']:.3f} vs {b['AUROC']:.3f} ({short})")
auprc.append(f"{m['AUPRC']:.3f} vs {b['AUPRC']:.3f}")
if b["AUROC"] > m["AUROC"]:
lost.append(short)
detail = (f"AUROC: model vs yield-curve benchmark {', '.join(auroc)}. "
f"AUPRC: {' and '.join(auprc)}.")
if lost:
detail += f" Benchmark ahead on AUROC in {' and '.join(lost)}."
return ("TRIGGERED" if lost else "ok"), detail
The 12-block model, step by step
- Collect the data as it was known. Every series is stored with each version ever published (FRED/ALFRED), so the model only sees figures released by the decision date. Before a series' first archived vintage, its earliest vintage is used with a conservative release delay.
- Transform each series into a change or spread (table below). Monthly changes are averaged over 3 months, because a single month is mostly noise. Daily and weekly series are averaged to months first.
View the Python · recession.features.apply_transform 25 lines
def apply_transform(x: pd.Series, tx: R.Tx, freq: str) -> pd.Series: ppy = PERIODS_PER_YEAR[freq] if tx is R.Tx.NONE: y = x elif tx is R.Tx.DIFF: y = x.diff() elif tx is R.Tx.DIFF2: y = x.diff().diff() elif tx is R.Tx.LOG_DIFF: y = 100 * _log(x).diff() elif tx is R.Tx.LOG_DIFF2: y = 100 * _log(x).diff().diff() elif tx is R.Tx.YOY: y = 100 * (x / x.shift(ppy) - 1) elif tx is R.Tx.PCT_6M_ANN: y = 100 * ((x / x.shift(max(ppy // 2, 1))) ** 2 - 1) elif tx is R.Tx.MAX_3Y_INCREASE: lp = 100 * _log(x) y = (lp - lp.shift(1).rolling(3 * ppy, min_periods=3 * ppy).max()).clip(lower=0) else: raise NotImplementedError(f"transform {tx.value} is not used by any Stage 3 feature") if tx in DIFF_LIKE and freq == "M": y = y.rolling(SMOOTH_MONTHS, min_periods=SMOOTH_MONTHS).mean() return y - Standardize each one against its own history up to that month only: z = (value − median) ÷ scale, where scale = interquartile range ÷ 1.349 (the standard deviation's equivalent for normal data), capped at ±4. Both are shown for every factor below.
View the Python · recession.features.expanding_robust_z 16 lines
def expanding_robust_z(paths: pd.DataFrame) -> pd.DataFrame: """ z(T) from the median and IQR of the feature's own real-time path up to T. Event-type features (the Hamilton oil shock is zero most months; oil was price-controlled before 1973) have an IQR of zero. There the scale falls back to the expanding standard deviation, so a rare non-zero value still registers instead of dividing by zero. """ ex = paths.expanding(MIN_HISTORY_MONTHS) med = ex.median() scale = (ex.quantile(0.75) - ex.quantile(0.25)) / 1.349 scale = scale.where(scale > 0, ex.std()) z = (paths - med) / scale.where(scale > 0) return z.replace([np.inf, -np.inf], np.nan).clip(-Z_CLIP, Z_CLIP) - Orient and average: multiply by −sign so negative always means recession-like, then average within each block. Indexes are left out before the date they were first published.
View the Python · recession.features.block_scores 15 lines
def block_scores(z: pd.DataFrame, specs: list[Spec], included: list[str]) -> tuple[pd.DataFrame, pd.DataFrame]: """Equal-weight mean of signed z within each block. Negative = recessionary.""" sign = {s.key: s.sign for s in specs} z = mask_unpublished(z) signed = pd.DataFrame({k: -sign[k] * z[k] for k in included}) block_of = {s.key: s.block for s in specs} names = {b.number: b.name for b in R.BLOCKS} scores, counts = {}, {} for b in sorted({block_of[k] for k in included}): cols = [k for k in included if block_of[k] == b] label = f"B{b:02d} {names[b]}" scores[label] = signed[cols].mean(axis=1) counts[label] = signed[cols].notna().sum(axis=1) return pd.DataFrame(scores), pd.DataFrame(counts) - Combine with a probit: probability = Φ(intercept + Σ weight × block score), where Φ is the standard normal distribution.
View the Python · recession.model.Probit 75 lines
class Probit: """ Probit with an L2 penalty on slopes. Fisher-information covariance for intervals. sign_constrained=True forces every slope <= 0: inputs are block scores oriented so that NEGATIVE = recessionary (registry sign convention), so a weaker block may only RAISE the probability. The constraint encodes the registry's declared signs; it is not estimated from the data. """ def __init__(self, lam: float, sign_constrained: bool = False): self.lam = lam self.sign_constrained = sign_constrained self.beta: np.ndarray | None = None self.cov: np.ndarray | None = None @staticmethod def _X(x: np.ndarray) -> np.ndarray: return np.column_stack([np.ones(len(x)), x]) def fit(self, x: np.ndarray, y: np.ndarray) -> "Probit": X = self._X(x) pen = np.full(X.shape[1], self.lam) pen[0] = 0.0 def nll(b): eta = X @ b ll = y * norm.logcdf(eta) + (1 - y) * norm.logcdf(-eta) # d/deta of the log-likelihood, computed stably g_pos = np.exp(norm.logpdf(eta) - norm.logcdf(eta)) g_neg = np.exp(norm.logpdf(eta) - norm.logcdf(-eta)) grad = -(X.T @ (y * g_pos - (1 - y) * g_neg)) + pen * b return -ll.sum() + 0.5 * (pen * b * b).sum(), grad b0 = np.zeros(X.shape[1]) b0[0] = norm.ppf(np.clip(y.mean(), 0.01, 0.99)) bounds = ([(None, None)] + [(None, 0.0)] * (X.shape[1] - 1)) if self.sign_constrained else None res = minimize(nll, b0, jac=True, method="L-BFGS-B", bounds=bounds) self.converged = bool(res.success) if not res.success: import warnings warnings.warn(f"Probit did not converge: {res.message}") self.beta = res.x eta = X @ self.beta w = norm.pdf(eta) ** 2 / np.clip(norm.cdf(eta) * norm.cdf(-eta), 1e-12, None) H = X.T @ (X * w[:, None]) + np.diag(pen) self.H = H self.cov = np.linalg.pinv(H) return self def predict(self, x: np.ndarray) -> np.ndarray: return norm.cdf(self._X(x) @ self.beta) def interval(self, x: np.ndarray, q=(0.1, 0.9), draws: int = 4000, seed: int = 0) -> np.ndarray: """ Probability quantiles from parameter uncertainty (Laplace approximation). With the sign constraint, slopes the fit holds at exactly zero (binding) are kept at zero, and the uncertainty of the others is the curvature of the fit in the free directions only. Drawing every slope from the full covariance (as before 5 Oct 2026) gave half the zero-weight blocks positive weights, which the model forbids, and let blocks with no weight move the range (external review). """ rng = np.random.default_rng(seed) if not self.sign_constrained or getattr(self, "H", None) is None: B = rng.multivariate_normal(self.beta, self.cov, size=draws) else: free = np.r_[True, self.beta[1:] < -1e-8] # intercept and the non-zero slopes cov_free = np.linalg.pinv(self.H[np.ix_(free, free)]) B = np.zeros((draws, len(self.beta))) B[:, free] = rng.multivariate_normal(self.beta[free], cov_free, size=draws) B[:, 1:] = np.minimum(B[:, 1:], 0.0) # a weaker block may only raise the probability p = norm.cdf(self._X(x) @ B.T) return np.quantile(p, q, axis=1).TView the Python · recession.live.forecast 91 lines
def forecast(scores: Optional[pd.DataFrame] = None, paths: Optional[pd.DataFrame] = None, counts: Optional[pd.DataFrame] = None, decision: Optional[str] = None, as_of: Optional[str] = None) -> dict: """ decision="YYYY-MM": the official month-end forecast for that month. as_of="YYYY-MM-DD": an in-month update using everything published by that day (requires the Stage 3 paths to include that date: run_stage3(extra_date=...)). """ fd = features.FEATURE_DIR scores = pd.read_parquet(fd / "block_scores.parquet") if scores is None else scores paths = pd.read_parquet(fd / "feature_paths.parquet") if paths is None else paths counts = pd.read_parquet(fd / "block_counts.parquet") if counts is None else counts panel = _panel(scores, paths) blocks = model.block_columns(panel) present = panel[blocks].notna().sum(axis=1) >= model.MIN_BLOCKS_PRESENT if as_of is not None: T = pd.Timestamp(as_of).normalize() # in-month (event) update elif decision: T = pd.Timestamp(decision) + pd.offsets.MonthEnd(0) # official month-end else: T = panel.index[present & panel.index.is_month_end][-1] earlier = panel.index[present & (panel.index < T)] prev = earlier[-1] # An update for a given day is in-month even on the last day of a month; only decision= (or the default) # makes the official month-end forecast. (A 30 Sep update used to become September's official entry.) kind = "in-month" if as_of is not None else "month-end" Xb = panel[blocks].fillna(0.0) tr = model._training_rows(panel, T) & present y = panel.loc[tr, "y"].values v2 = Probit(model.RIDGE_LAMBDA, sign_constrained=True).fit(Xb.loc[tr].values, y) ts_rows = tr & panel["term_spread"].notna() ts = Probit(1e-6).fit(panel.loc[ts_rows, ["term_spread"]].values, panel.loc[ts_rows, "y"].values) te_rows = ts_rows & panel["ebp"].notna() tsebp = Probit(1e-6).fit(panel.loc[te_rows, ["term_spread", "ebp"]].values, panel.loc[te_rows, "y"].values) x, xp = Xb.loc[[T]].values, Xb.loc[[prev]].values p, p_prev = float(v2.predict(x)[0]), float(v2.predict(xp)[0]) lo, hi = (float(v) for v in v2.interval(x)[0]) beta = v2.beta[1:] def bench(fit, cols, when): row = panel.loc[[when], cols] return float(fit.predict(row.values)[0]) if row.notna().all(axis=None) else None ind = model.indicator_benchmarks(pd.DatetimeIndex([T])).iloc[0] block_rows = {} for j, b in enumerate(blocks): now, before = float(Xb.loc[T, b]), float(Xb.loc[prev, b]) recent = counts[b].loc[:T].iloc[-25:-1] block_rows[b] = { "score": round(now, 3), "score_prev": round(before, 3), "weight": round(float(beta[j]), 6), "contribution": round(float(beta[j] * now), 4), # probit index units; + = more risk "change_contribution": round(float(beta[j] * (now - before)), 4), "factors": int(counts.loc[T, b]) if T in counts.index and pd.notna(counts.loc[T, b]) else 0, "usual_factors": int(recent.max()) if len(recent) else 0, } out = { "decision_month": f"{T:%Y-%m}", "kind": kind, "as_of": f"{T:%Y-%m-%d}", "compared_with": f"{prev:%Y-%m-%d}", "probability": round(p, 4), "probability_exact": p, "intercept": round(float(v2.beta[0]), 6), "ridge_lambda": model.RIDGE_LAMBDA, "interval_80": [round(lo, 4), round(hi, 4)], "probability_prev_month_same_model": round(p_prev, 4), "alarm_threshold": DECISIONS.alarm_threshold, "alarm_model": p >= DECISIONS.alarm_threshold, # the 12-block model's own reading "benchmarks": { "Term spread + EBP probit": bench(tsebp, ["term_spread", "ebp"], T), "Term spread probit": bench(ts, ["term_spread"], T), "Fed Board TS+EBP probability (published)": _num(ind["Fed Board TS+EBP probability (published)"]), "Chauvet-Piger probability": _num(ind["Chauvet-Piger probability"]), "Sahm rule (real-time)": _num(ind["Sahm rule (real-time)"]), }, "blocks": block_rows, "features_included": None, # filled by the caller from the Stage 3 report "train_months": int(tr.sum()), "base_rate": round(float(y.mean()), 4), # what "no skill" would have forecast (K3) "recessions_in_training": int(panel.loc[tr & (panel["y"] == 1), "peak"].nunique()), } out["flags"] = flags(out) out["kill_criteria"] = kill_status(out) # The alarm follows the HEADLINE (the benchmark while K3/K4/K5 hold), not the demoted model. out["alarm"] = (headline(out)["probability"] or 0) >= DECISIONS.alarm_threshold out["explanation"] = explain(out) return out
| Transform | What it computes |
|---|---|
| NONE | the level as published |
| DIFF | change from the previous period |
| LOG_DIFF | 100 × change in the logarithm, about the % change from the previous period |
| YOY | % change from a year earlier |
| MAX_3Y_INCREASE | rise above the highest level of the previous 3 years (Hamilton's net oil price increase) |
From raw data to block scores
Open a block to see every factor: its value, the median and scale it is standardized with, its z-score and its signed contribution.
Real Activity & Outputblock score +0.077 = average of 11 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| Real GDP | GDPC1 | LOG_DIFF | 0.549 | 0.632 | 0.548 | -0.15 | -1 | -0.15 |
| Real gross domestic income | A261RX1Q020SBEA | LOG_DIFF | 0.653 | 0.721 | 0.642 | -0.11 | -1 | -0.11 |
| Industrial production | INDPRO | LOG_DIFF | 0.139 | 0.235 | 0.396 | -0.24 | -1 | -0.24 |
| Capacity utilization | TCU | DIFF | 0.037 | 0.037 | 0.321 | +0.00 | -1 | +0.00 |
| Real personal income less transfers | W875RX1 | LOG_DIFF | 0.129 | 0.228 | 0.234 | -0.42 | -1 | -0.42 |
| Real personal consumption expenditures | PCEC96 | LOG_DIFF | 0.337 | 0.248 | 0.206 | +0.43 | -1 | +0.43 |
| Real manufacturing & trade sales | CMRMTSPL | LOG_DIFF | 0.428 | 0.263 | 0.399 | +0.41 | -1 | +0.41 |
| Core capital goods orders | NEWORDER | LOG_DIFF | 1.27 | 0.379 | 1.22 | +0.73 | -1 | +0.73 |
| Heavy truck sales | HTRUCKSSAAR | LOG_DIFF | -4.78 | 0.280 | 3.08 | -1.64 | -1 | -1.64 |
| Cass Freight shipments index | FRGSHPUSM649NCIS | YOY | licensed | -1 | licensed | |||
| Electricity output | IPG2211S | YOY | 7.00 | 1.95 | 3.47 | +1.45 | -1 | +1.45 |
Labor Marketblock score +0.220 = average of 13 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| Nonfarm payrolls | PAYEMS | LOG_DIFF | 0.032 | 0.142 | 0.125 | -0.88 | -1 | -0.88 |
| Household survey employment | CE16OV | LOG_DIFF | 0.182 | 0.117 | 0.163 | +0.40 | -1 | +0.40 |
| Initial jobless claims, 4-week average | IC4WSA | YOY | -14.38 | -3.59 | 12.61 | -0.86 | +1 | +0.86 |
| Continuing claims | CCSA | YOY | -11.06 | -4.38 | 14.46 | -0.46 | +1 | +0.46 |
| Insured unemployment rate | IURSA | DIFF | -0.033 | -0.008 | 0.043 | -0.58 | +1 | +0.58 |
| Average weekly hours, manufacturing | AWHMAN | DIFF | 0.100 | 0.000 | 0.099 | +1.01 | -1 | +1.01 |
| Average weekly hours, all private | AWHAETP | DIFF | 0.033 | 0.000 | 0.049 | +0.67 | -1 | +0.67 |
| Temporary help services employment | TEMPHELPS | LOG_DIFF | -0.276 | 0.328 | 0.665 | -0.91 | -1 | -0.91 |
| Quits rate | JTSQUR | DIFF | -0.033 | 0.000 | 0.049 | -0.67 | -1 | -0.67 |
| Job openings | JTSJOL | LOG_DIFF | -2.09 | 0.230 | 1.94 | -1.20 | -1 | -1.20 |
| Layoffs and discharges | JTSLDL | LOG_DIFF | -2.35 | -0.422 | 2.49 | -0.78 | +1 | +0.78 |
| Prime-age employment-population ratio | LNS12300060 | DIFF | 0.167 | 0.033 | 0.099 | +1.35 | -1 | +1.35 |
| Indeed job postings index | IHLIDXUS | YOY | licensed | -1 | licensed | |||
Household & Consumerblock score +0.419 = average of 8 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| Real disposable personal income | DSPIC96 | LOG_DIFF | 0.214 | 0.224 | 0.231 | -0.04 | -1 | -0.04 |
| U. Michigan consumer sentiment | UMCSENT | DIFF | licensed | -1 | licensed | |||
| Retail sales, control group | RSXFS | LOG_DIFF | 0.377 | 0.403 | 0.294 | -0.09 | -1 | -0.09 |
| Real retail & food services sales | RRSFS | LOG_DIFF | 0.321 | 0.179 | 0.335 | +0.42 | -1 | +0.42 |
| Total vehicle sales, SAAR | TOTALSA | LOG_DIFF | -1.67 | 0.173 | 1.94 | -0.95 | -1 | -0.95 |
| Credit card delinquency, all banks | DRCCLACBS | DIFF | -0.060 | -0.020 | 0.156 | -0.26 | +1 | +0.26 |
| Household net worth | TNWBSHNO | LOG_DIFF | 6.76 | 1.90 | 1.76 | +2.76 | -1 | +2.76 |
| SLOOS consumer credit standards | DRTSCLCC | NONE | 6.70 | 5.60 | 16.01 | +0.07 | +1 | -0.07 |
Business, Orders & Surveysblock score +0.325 = average of 6 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| Empire State manufacturing survey | GACDISA066MSFRBNY | NONE | 7.60 | 7.43 | 15.89 | +0.01 | -1 | +0.01 |
| Philadelphia Fed manufacturing survey | GACDFSA066MSFRBPHI | NONE | 37.80 | 10.75 | 16.92 | +1.60 | -1 | +1.60 |
| Chicago Fed National Activity Index | CFNAI | NONE | -0.040 | 0.050 | 0.608 | -0.15 | -1 | -0.15 |
| CFNAI diffusion index | CFNAIDIFF | NONE | 0.020 | 0.070 | 0.378 | -0.13 | -1 | -0.13 |
| Corporate profits, NIPA basis | CPATAX | YOY | 18.16 | 6.78 | 12.59 | +0.90 | -1 | +0.90 |
| Business formation applications | BAHBATOTALSAUS | LOG_DIFF | -0.364 | 0.092 | 1.62 | -0.28 | -1 | -0.28 |
Credit & Financial Conditionsblock score +0.351 = average of 14 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| Moody's Baa minus 10-year Treasury | BAA10Y | DIFF | licensed | +1 | licensed | |||
| Moody's Aaa minus 10-year Treasury | AAA10Y | DIFF | licensed | +1 | licensed | |||
| Excess bond premium (Gilchrist-Zakrajsek) | EBP | NONE | -0.310 | -0.048 | 0.342 | -0.77 | +1 | +0.77 |
| Chicago Fed National Financial Conditions Index | NFCI | NONE | -0.555 | -0.505 | 0.476 | -0.10 | +1 | +0.10 |
| Adjusted NFCI | ANFCI | NONE | -0.585 | -0.265 | 0.604 | -0.53 | +1 | +0.53 |
| NFCI credit subindex | NFCICREDIT | NONE | -0.062 | -0.259 | 0.513 | +0.38 | +1 | -0.38 |
| NFCI leverage subindex | NFCILEVERAGE | NONE | 0.084 | -0.235 | 0.641 | +0.50 | +1 | -0.50 |
| St. Louis Fed financial stress index | STLFSI4 | NONE | -0.787 | -0.268 | 0.558 | -0.93 | +1 | +0.93 |
| Kansas City Fed financial stress index | KCFSI | NONE | -0.946 | -0.410 | 0.619 | -0.87 | +1 | +0.87 |
| SLOOS: C&I tightening, large firms | DRTSCILM | NONE | 0.000 | 0.000 | 17.79 | +0.00 | +1 | -0.00 |
| SLOOS: CRE standards | SUBLPDRCSN | NONE | -11.30 | 7.00 | 17.38 | -1.05 | +1 | +1.05 |
| SLOOS: loan demand | DRSDCILM | NONE | 16.10 | 0.000 | 23.94 | +0.67 | -1 | +0.67 |
| Total bank credit | TOTBKCR | YOY | 6.08 | 6.69 | 3.33 | -0.18 | -1 | -0.18 |
| Bank deposits | DPSACBW027SBOG | YOY | 6.64 | 6.81 | 2.87 | -0.06 | -1 | -0.06 |
Monetary Policy, Rates & Liquidityblock score -0.636 = average of 9 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| Effective fed funds rate | DFF | DIFF | 0.084 | 0.001 | 0.129 | +0.65 | +1 | -0.65 |
| 5-year real yield | DFII5 | DIFF | 0.200 | -0.006 | 0.108 | +1.90 | +1 | -1.90 |
| 10-year real yield | DFII10 | DIFF | 0.182 | -0.006 | 0.093 | +2.01 | +1 | -2.01 |
| M2 money stock | M2SL | YOY | 5.66 | 6.63 | 2.93 | -0.33 | -1 | -0.33 |
| Real M2 | M2REAL | YOY | 2.19 | 2.99 | 3.81 | -0.21 | -1 | -0.21 |
| Fed balance sheet total assets | WALCL | YOY | 2.07 | 3.76 | 11.52 | -0.15 | -1 | -0.15 |
| Reserve balances at Federal Reserve banks | WRESBAL | LOG_DIFF | -0.717 | -0.065 | 2.93 | -0.22 | -1 | -0.22 |
| Overnight reverse repo balances | RRPONTSYD | DIFF | -0.169 | 0.002 | 7.61 | -0.02 | -1 | -0.02 |
| Treasury General Account | WTREGEN | LOG_DIFF | 2.03 | 0.318 | 7.61 | +0.22 | +1 | -0.22 |
The Yield Curve Complexblock score -0.235 = average of 2 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| 10-year minus 3-month Treasury spread | T10Y3M | NONE | 1.08 | 1.28 | 1.48 | -0.13 | -1 | -0.13 |
| 10-year minus 2-year Treasury spread | T10Y2Y | NONE | 0.460 | 0.778 | 0.945 | -0.34 | -1 | -0.34 |
Energy Pricesblock score -0.479 = average of 3 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| WTI crude oil | DCOILWTICO | LOG_DIFF | 4.59 | 0.000 | 2.69 | +1.71 | +1 | -1.71 |
| Hamilton net oil price increase | OIL_SHOCK | MAX_3Y_INCREASE | 0.000 | 0.000 | 3.84 | +0.00 | +1 | -0.00 |
| Natural gas, Henry Hub | DHHNGSP | LOG_DIFF | -2.17 | -0.024 | 7.96 | -0.27 | +1 | +0.27 |
Housing & Constructionblock score +0.104 = average of 9 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| Building permits | PERMIT | LOG_DIFF | -0.166 | 0.310 | 2.58 | -0.18 | -1 | -0.18 |
| Housing starts | HOUST | LOG_DIFF | 2.52 | 0.014 | 3.55 | +0.71 | -1 | +0.71 |
| New home sales | HSN1F | LOG_DIFF | 2.43 | 0.160 | 3.54 | +0.64 | -1 | +0.64 |
| Months' supply of new homes | MSACSR | DIFF | -0.233 | 0.000 | 0.222 | -1.05 | +1 | +1.05 |
| 30-year fixed mortgage rate | MORTGAGE30US | DIFF | licensed | +1 | licensed | |||
| Residential fixed investment share of GDP | A011RE1Q156NBEA | DIFF | -0.100 | 0.000 | 0.148 | -0.67 | -1 | -0.67 |
| Total construction spending | TTLCONS | LOG_DIFF | 0.530 | 0.317 | 0.701 | +0.30 | -1 | +0.30 |
| Private nonresidential construction | PNRESCONS | LOG_DIFF | 1.03 | 0.143 | 0.956 | +0.93 | -1 | +0.93 |
| Rental vacancy rate | RRVRUSQ156N | DIFF | 0.000 | 0.000 | 0.297 | +0.00 | +1 | -0.00 |
Equity, Volatility & Cross-Assetblock score +0.252 = average of 2 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| Equity drawdown from trailing 12-month high | SP500 | NONE | licensed | +1 | licensed | |||
| CBOE volatility index | VIXCLS | NONE | licensed | +1 | licensed | |||
Commodities & Dollarblock score -0.040 = average of 3 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| Broad trade-weighted dollar index | DTWEXBGS | LOG_DIFF | 0.272 | 0.010 | 0.810 | +0.32 | +1 | -0.32 |
| Advanced-economy dollar index | DTWEXAFEGS | LOG_DIFF | 0.328 | 0.104 | 0.962 | +0.23 | +1 | -0.23 |
| Agricultural commodity index | WPU01 | YOY | -2.45 | 1.42 | 8.85 | -0.44 | +1 | +0.44 |
Policy Uncertaintyblock score -4.000 = average of 1 signed values
| Factor | Series | Transform | Value | Median | Scale | z-score | Sign | Signed z |
|---|---|---|---|---|---|---|---|---|
| Economic Policy Uncertainty index | USEPUINDXD | NONE | 294.7 | 98.34 | 46.42 | +4.00 | +1 | -4.00 |
From block scores to a probability
| Block | Score x | Weight β | β × x |
|---|---|---|---|
| Intercept | -1.8917 | ||
| Real Activity & Output | +0.0770 | -0.1163 | -0.0090 |
| Labor Market | +0.2200 | -0.4037 | -0.0888 |
| Household & Consumer | +0.4190 | +0.0000 | +0.0000 |
| Business, Orders & Surveys | +0.3250 | -0.4564 | -0.1483 |
| Credit & Financial Conditions | +0.3510 | +0.0000 | +0.0000 |
| Monetary Policy, Rates & Liquidity | -0.6360 | +0.0000 | -0.0000 |
| The Yield Curve Complex | -0.2350 | -0.6519 | +0.1532 |
| Energy Prices | -0.4790 | -0.0854 | +0.0409 |
| Housing & Construction | +0.1040 | +0.0000 | +0.0000 |
| Equity, Volatility & Cross-Asset | +0.2520 | -0.1231 | -0.0310 |
| Commodities & Dollar | -0.0400 | -0.5043 | +0.0202 |
| Policy Uncertainty | -4.0000 | +0.0000 | -0.0000 |
| Total (η) | -1.9545 |
Probability = Φ(-1.9545) = 2.53% (logged: 2.53%)
Does one recession drive the weights?
The model refitted 7 more times, leaving out one recession's run-up each time
| Block | All 7 recessions | Range, one left out | Weakest without the recession that began | Times at zero |
|---|---|---|---|---|
| Real Activity & Output | -0.12 | -0.30 to -0.03 | Feb 1980 | 0 |
| Labor Market | -0.40 | -0.48 to -0.22 | Apr 2001 | 0 |
| Business, Orders & Surveys | -0.46 | -0.58 to -0.25 | Aug 1990 | 0 |
| The Yield Curve Complex | -0.65 | -0.75 to -0.58 | Apr 2001 | 0 |
| Energy Prices | -0.09 | -0.16 to -0.05 | Apr 2001 | 0 |
| Equity, Volatility & Cross-Asset | -0.12 | -0.19 to +0.00 | Apr 2001 | 1 |
| Commodities & Dollar | -0.50 | -0.58 to -0.41 | Dec 1973 | 0 |
Weights that fall below half their size, or to zero, when one recession is dropped rest mainly on that episode: Real Activity & Output and Equity, Volatility & Cross-Asset. Stability is not proof of forecasting power: these fits still learn from the same history. The out-of-sample test is the scorecard behind kill rule K4, which this model currently loses to the yield-curve benchmark.
View the Python · recession.live.weight_stability 28 lines
def weight_stability(T: pd.Timestamp, scores: Optional[pd.DataFrame] = None,
paths: Optional[pd.DataFrame] = None) -> Optional[pd.DataFrame]:
"""
Leave one recession out: refit the 12-block model exactly as forecast()
does at decision date T, once with all training months and once without
each recession's run-up (the months labelled for that peak). A weight that
holds its size whichever recession is dropped rests on more than one
episode; one that collapses when a single recession is removed is fragile.
Rows: "all" and each left-out peak; columns: one weight per block.
"""
fd = features.FEATURE_DIR
scores = pd.read_parquet(fd / "block_scores.parquet") if scores is None else scores
paths = pd.read_parquet(fd / "feature_paths.parquet") if paths is None else paths
panel = _panel(scores, paths)
blocks = model.block_columns(panel)
present = panel[blocks].notna().sum(axis=1) >= model.MIN_BLOCKS_PRESENT
Xb = panel[blocks].fillna(0.0)
tr = model._training_rows(panel, T) & present
peaks = sorted(panel.loc[tr & (panel["y"] == 1), "peak"].dropna().unique())
names = {b.number: b.name for b in R.BLOCKS}
label = lambda c: f"B{int(c[1:3]):02d} {names.get(int(c[1:3]), c)}" if c[:1] == "B" and c[1:3].isdigit() else c
rows = {}
for drop in [None] + peaks:
keep = tr & ~(panel["peak"] == drop) if drop is not None else tr
fit = Probit(model.RIDGE_LAMBDA, sign_constrained=True).fit(Xb.loc[keep].values, panel.loc[keep, "y"].values)
rows["all" if drop is None else str(drop)[:7]] = fit.beta[1:]
return pd.DataFrame(rows, index=[label(c) for c in blocks]).T
Known issues
Open problems, for the next model version. None is fixed by editing the current model or any logged forecast.
- Core capital goods orders (NEWORDER) are in dollars, not adjusted for inflation, yet sit in the Real Activity block.
- Household net worth is in dollars and mostly tracks stock prices, so it partly double-counts the equity block.
- Electricity output is now driven partly by data-center demand, a structural trend rather than the business cycle.
- Block scores are averages of different numbers of signals, so their spread differs (0.59 to 1.38 as of Oct 5, 2026); the Uncertainty block exists only from 2012 and is filled with zero before that. Standardizing the blocks and dropping the zero-fill changed the backtest by at most 0.004 in AUROC (table).
- The excess bond premium has no real-time history. With a real-time credit spread (Baa) in its place, the benchmark leads in 1980–2024 but trails in 1990–2024 on AUROC (table).
- The OECD leading indicator benchmark is entered as a level and ranks risk backwards; it should be a change.
| Backtest variant | Window | AUROC | Brier |
|---|---|---|---|
| v2 published | 1980-2024 ex-COVID | 0.887 | 0.084 |
| v2 standardized | 1980-2024 ex-COVID | 0.883 | 0.084 |
| v2 standardized, no zero-fill | 1980-2024 ex-COVID | 0.887 | 0.083 |
| yield-curve benchmark (hindsight EBP) | 1980-2024 ex-COVID | 0.899 | 0.098 |
| term spread + Baa spread (real-time) | 1980-2024 ex-COVID | 0.900 | 0.097 |
| term spread alone | 1980-2024 ex-COVID | 0.895 | 0.086 |
| v2 published | 1990-2024 ex-COVID | 0.898 | 0.071 |
| v2 standardized | 1990-2024 ex-COVID | 0.896 | 0.072 |
| v2 standardized, no zero-fill | 1990-2024 ex-COVID | 0.896 | 0.072 |
| yield-curve benchmark (hindsight EBP) | 1990-2024 ex-COVID | 0.907 | 0.090 |
| term spread + Baa spread (real-time) | 1990-2024 ex-COVID | 0.884 | 0.104 |
| term spread alone | 1990-2024 ex-COVID | 0.876 | 0.092 |
Experiments: tools/review_experiments.py, run Oct 5, 2026 after an outside review.
How the weights are fit
- Refitted at every forecast on 640 months covering 7 recessions: only months whose outcome the NBER had announced by the decision date, ending at least 12 months before it (the forecast horizon), so no answer leaks in.
- A ridge penalty (λ = 10.0) shrinks the weights toward zero, because seven recessions cannot pin down twelve weights precisely.
- Every weight is constrained to be zero or negative: a weaker block can only raise the probability. Blocks at zero add nothing once the others are known.
- 2020 is excluded as an exogenous shock.
View the Python · recession.model._training_rows 6 lines
def _training_rows(panel: pd.DataFrame, origin: pd.Timestamp) -> pd.Series:
return (panel["y"].notna()
& (panel["known_from"] <= origin)
& (panel.index <= origin - pd.offsets.MonthEnd(EMBARGO_MONTHS))
& ~panel["exogenous"].astype(bool))
The 80% range and the backtest
- 80% range (1.6–3.9%). 4,000 sets of weights are drawn from their estimated uncertainty (a Laplace approximation of the fit); the range holds the middle 80% of the resulting probabilities. It covers uncertainty in the weights only, so it is too narrow (the model's is 1.6–3.9%, the benchmark's 2.8–6.1%). Model choice matters more: the forecasts we track range from 2.5% to 9.1% today.
- Backtest. From January 1980 each month is forecast with a model refitted every 12 months on the recessions announced by then, using the data as it was known then. Live forecasts are refitted at every forecast instead, so the record and the live numbers are not exactly like-for-like. The Track record section scores those forecasts. Before about 2010 most inputs come from later-revised data, so that record is an optimistic ceiling.
