Beat-again probability
We take this stock's trailing beat rate over its prior up-to-8 earnings events (only when at least 4 prior events exist — otherwise no bucket) and assign it to one of four buckets: <=0.25, 0.25-0.5, 0.5-0.75, >0.75 (left-open, right-closed; the first bucket includes 0). We then query earnings_beat_base_rates for that bucket and read p_beat — the realized fraction of all historical announcement EVENTS (one observation per event, non-overlapping) in that same bucket that actually beat. That fraction is pBeatNext. The table validates itself out-of-time: it fits cell rates on events through T-12m, scores them on the last 12m (brier_model vs brier_climatology), and stores a verdict. If the verdict is not "persistent_skill" — or the bucket is empty or unavailable — the service serves the pooled climatology rate instead, with an honest flag, never a manufactured signal.
Strengthens a pre-earnings thesis when pBeatNext sits above the pooled base rate and cohort_multiple (cell rate / pooled rate, populated only on the persistent-skill path) is meaningfully above 1.0; weakens it when pBeatNext sits below the pooled rate. Neither alone is actionable — pair it with consensus_bias (does the street sandbag?) and reaction_asymmetry (does a beat even move the stock up?) before forming a view. When the flag reads "no persistent skill in beat persistence — pooled base rate", the bucket conditioning adds nothing statistically, so read it as climatology, not an edge.
pBeatNext is the realized historical beat frequency for this stock's bucket (or the pooled rate when no bucket-level skill is detected). A higher value means names in this trailing-beat-rate bucket historically beat more often; a lower value, less often. The exact pooled and per-bucket numbers are computed live from earnings_beat_base_rates and are not hard-coded — read the live value (and its flag/cohort_n) to know which regime you are in: a cell rate with verdict "persistent_skill", or the pooled climatology fallback.
A probability in [0.0, 1.0]. By construction no cell can exceed 1.0 or fall below 0.0 (it is a fraction of events that beat). The specific pooled rate and per-bucket spread are NOT hard-coded — they are recomputed from the earnings_beat_base_rates window each run — so any concrete band would be illustrative, not code-true; cite actual figures only from a live DB read. Cells with too little history publish NULL (n=0) so consumers flag rather than imply.
The spine is survivor-tilted: a yfinance capture of (mostly) currently-listed names, large-cap-skewed (measured 2026-06: ~70% of megas vs ~5% of pennies have history). Delisted companies that missed repeatedly are absent, so the rates are conditioned on "still listed today" and run modestly optimistic versus a true full-universe base rate. The window is whatever the intel_earnings_events spine covers (exposed as window.start / window.end), not a fixed start year.