Earnings miss predictor (track record, sector, analysts, price, guidance)
Predicts the probability that a US company will miss the consensus EPS estimate at its next quarterly earnings announcement, about 90 days ahead, from:
- its own recent earnings surprises (5 numbers),
- its GICS sector (10 sectors; Real Estate not supported),
- analyst actions in the 90 days before: rating changes and price targets raised or cut (4 numbers),
- the stock's run-up vs the S&P 500 (SPY) over 1 and 3 months, and its volatility (3 numbers), and
- what its latest earnings press release says about guidance: raised, cut, maintained, narrowed, introduced or withdrawn, and how hedged the outlook is (10 numbers from regex rules).
It is a logistic regression (32 features β 1 logit) wrapped as a PyTorch module. It was built as a learning project; the GitHub repo has the full pipeline and the docs, including every idea that did not work.
What the evaluation says: most of the skill comes from the track record and the sector. On companies the model never saw, analyst, price and guidance features add +0.020 AUC over those two [+0.010, +0.030]. Forward in time the gain is +0.010 [-0.006, +0.027]: it helped in 2023 and 2024 but not in 2025β26 (table below).
Usage
# pip install torch huggingface_hub numpy pandas yfinance
# download miss_model.py, guidance_rules.py and market_features.py from this repo
from miss_model import HubMissModel
model = HubMissModel.from_pretrained("Guruprasath12/lie-detect")
p_miss = model.predict(
past_surprises=[4.2, -1.5, 2.8, 6.0], # EPS surprise % of earlier announcements, oldest β newest
sector="Industrials", # one of model.sectors
release_text=open("press_release.txt").read(), # latest earnings press release (8-K Exhibit 99.1)
ticker="CAT", # analyst actions and prices are fetched from Yahoo Finance
announcement_date="2026-11-04", # the announcement being predicted (YYYY-MM-DD)
)
Pass market_inputs=(analyst_actions, prices, spy_prices) instead of ticker to use your own data
(format: market_features.market_vector). model.predict_proba(X) takes a raw feature matrix in
model.features order; the model standardises inputs with training-set statistics stored as buffers.
History features (computed from announcements before the one predicted):
| Feature | Definition |
|---|---|
prev_miss |
1 if the company missed the EPS estimate at its previous announcement, else 0 |
prev_surprise |
previous announcement's EPS surprise in %, clipped to [-100, 100] |
miss_rate_4q |
share of misses over the last 4 announcements |
mean_surprise_4q |
mean EPS surprise % over the last 4 announcements |
std_surprise_4q |
std of EPS surprise % over the last 4 announcements (0 if < 2) |
Sector: one-hot over model.sectors (Communication Services, Consumer Discretionary, Consumer Staples, Energy, Financials, Health Care, Industrials, Information Technology, Materials, Utilities).
Analyst and price features: market_features.market_vector. Windows end 2 days before the
announcement date, so the release and the reactions to it never leak in. Analyst actions (last 90 days):
log count, upgrades β downgrades, share of price targets raised β lowered, mean % target change. Price:
1- and 3-month return minus SPY's, 3-month volatility.
Guidance features: guidance_rules.guidance_vector(text) finds sentences mentioning
guidance / outlook / forecast (minus boilerplate) and takes the direction verb closest to that noun.
About 85% precise on spot checks. Use the text of the press release, without tables.
Training data
- 6,916 (press release, next announcement) pairs from 188 S&P 500 companies, 2017β2026; 16.8% misses. The release is filed on announcement day; it is paired with the next announcement (83β98 days later), never its own.
- Text: 8-K Item 2.02 Exhibit 99.1 from SEC EDGAR. Labels, analyst actions and prices: Yahoo Finance. The data itself is not included here.
- Logistic regression with balanced class weights; C = 10.0 chosen on validation 2024 (train < 2024). The released weights are refit on all years with that C.
Evaluation
Rolling-origin test: each year predicted by a model trained only on earlier years (2,816 announcements, 469 misses):
| Model | Pooled AUC | ΞAUC vs history [95%] | Precision in top 10% |
|---|---|---|---|
| history only | 0.655 | β | 34% |
| history + sector | 0.670 | +0.015 [-0.002, +0.033] | 36% |
| this model | 0.680 | +0.025 [+0.004, +0.047] | 36% |
By test year:
| Test year | Announcements / misses | history + sector | this model | Ξ |
|---|---|---|---|---|
| 2023 | 726 / 138 | 0.670 | 0.704 | +0.034 |
| 2024 | 727 / 135 | 0.658 | 0.679 | +0.021 |
| 2025-26 | 1,363 / 196 | 0.673 | 0.671 | -0.002 |
Base rate 16.7%. On companies held out of training (5-fold by company): AUC 0.688, +0.020 [+0.010, +0.030] over history + sector. The analyst + price part of that gain (+0.019) is unchanged when each test row's own year is left out of training, so it is not a same-quarter market shock.
Limitations
- Modest skill. AUC β 0.68 ranks misses better than chance, not reliably. Flagging the riskiest 10% finds misses at 2.2Γ the base rate.
- The most recent test period (2025β26) shows no gain over history + sector. With three test years we can't tell a change in markets from noise.
- Depends on Yahoo Finance at prediction time. Yahoo's analyst-action history is used as if it were complete and unrevised; that isn't guaranteed.
- The text adds little on its own; an LLM reading the same guidance passages did no better.
- Survivorship: companies are today's S&P 500 members (plus 49 large caps); REITs are excluded because their EPS labels mix FFO and GAAP bases.
- Probabilities come from class-weighted training, so they are not calibrated (they overstate the miss rate). Use them for ranking.
- Not investment advice. Educational project only.
- Downloads last month
- 37