Skip to content

KB/framework

Screener Ripeness — Lifecycle Stages & the Buy-Zone Gate

Last verified

Sibling reading: The Screener System (S1 → S2 → S3 vision) and Screener Pipeline & Lifecycle (the operational lifecycle). This article covers ripeness — the one classifier that decides whether a name has seasoned into its historically-good entry zone, and the two views built on top of it.

What ripeness is

A name firing on the screener is not the same as a name worth acting on. An archetype can flag a ticker on day 1 (brand new, unconfirmed), watch it persist for a week (seasoning), or hold it so long the early-entry edge has decayed (too late). Ripeness is the single classifier that reads where a candidate sits in that lifecycle and labels it with one of seven stages.

There is one pure classifier, two views (design doctrine P12 — graduate by tier):

Ripeness is a CONTEXT / triage classification, never advice. “Ripe” means “persisted into the historically-good entry zone, with conviction” — it is not a directive to buy.

The classifier lives in app/screener/ripeness.py (classify_ripeness). All thresholds are operator-tunable in screener_config.json § ripeness — there are no magic numbers in code.

The seven stages

Stages are evaluated in order; the first matching rule wins. day is the candidate’s days_on_list; window is the archetype’s relevance window (screener_config.json § lifecycle.relevance_windows); cutoff = ripe_window_frac × window is the buy-zone time boundary.

Stage Condition What it means
unknown ABSTAIN — status, day, or window missing/invalid (or window ≤ 0) Not enough lifecycle data to classify. A data hole never fakes a ripe setup.
stale terminal status (decayed / exited), or day > window The candidacy is over, or relevance has elapsed.
fresh status == new Brand-new flag, awaiting confirmation. Not yet actionable.
building status == persisting, in the buy zone by time (day ≤ cutoff), but conviction below min_conviction_tier (or a required state not yet met) Alive and seasoning, not yet convicted. Stays off the Tape.
late status == persisting and day > cutoff Still on the list, but entry is too far along — the early-entry edge has decayed.
confirmed status == persisting, day ≤ cutoff, conviction ≥ min_conviction_tier (and state == require_state when that gate is set), and state != primed RIPE. Persisted into the buy zone with conviction.
loaded same as confirmed, plus state == primed RIPE — strongest. Confirmed and the archetype engine is fully primed.

ripe == true only for confirmed and loaded (the module-level _RIPE_STAGES constant is the single source of truth — ripeness is derived mechanically, never passed in per-call).

The buy-zone gate

A candidate is ripe when all of these hold:

  1. Persistingstatus == persisting (survived past the brand-new fresh day, not yet decayed/exited).
  2. In the buy zone by timeday ≤ ripe_window_frac × window. The early part of an archetype’s relevance window is where the historical edge concentrates; past cutoff the name reads late.
  3. Convictedconviction_tier ≥ min_conviction_tier (high > medium > low). Below it, the name reads building.
  4. State gate (optional) — if require_state is set, the archetype state must match it.

confirmed vs loaded is the same gate; loaded simply adds that the engine state is primed (strongest conviction). Both are ripe.

The three tunable thresholds

All in screener_config.json § ripeness. A missing or malformed block falls back to the code defaults (0.66 / medium / no state gate).

Key Default Effect
ripe_window_frac 0.66 Buy-zone cutoff as a fraction of the relevance window. A persisting name is eligible for confirmed only while days_on_list ≤ ripe_window_frac × window; past that it is late. 0.66 = the first two-thirds of the window.
min_conviction_tier medium Minimum conviction to reach confirmed. medium admits high + medium; high admits only high; low admits all. A persisting, in-window name below this tier reads building.
require_state null Optional engine-state gate (null / partial / primed). When set, confirmed additionally requires state to match. null = no requirement — a primed name is still labeled loaded, but primed is not required for ripeness.

Progress, day, and window

The classifier returns {stage, ripe, day, window, progress, reasons}:

The two views in the UI

/screener (the workbench) — every firing name, full lifecycle:

The Tape Company Action Board — only confirmed + loaded names. Because the gate is selective, the board is sometimes sparse or empty, and that is correct: when nothing is ripe, the honest answer is an empty board. Picks are never manufactured to fill slots.