KB / operations
Event Feed
Last verified
The Event Feed is the platform’s append-only history of “something changed”. Every cycle, the capture stage diffs the current classifier state against the previous snapshot and writes a row for each flip into signal_transitions. The dashboard’s Event Feed and the JSON API’s /api/v1/events/feed compose these alongside fired alerts, news items, and source failures into one unified stream.
The 18 classifiers
KNOWN_SIGNALS in app/signals/transitions.py is the canonical list. Adding a 19th means adding the emit logic, the classifier, and the entry to this set.
| Signal | What it tracks |
|---|---|
zgl_cross | SPY above / below the Zero Gamma Level |
gex_sign | GEX regime band: deep_positive / positive / neutral / negative / deep_negative |
pcr_0dte_band | 0DTE put/call ratio band: bullish_skew / neutral / bearish_skew |
regime | Market regime change (PANIC / RISK-OFF / CAUTIOUS / TRANSITIONAL / RISK-ON) |
energy_regime | Energy regime change (STABLE / ELEVATED / SHOCK / CRISIS / SHOCK_UP / FALLING / SHOCK_DOWN / RISING) |
alignment_severity | Signal alignment severity (STRONG_ALIGNMENT / MIXED / MODERATE_DIVERGENCE / STRONG_DIVERGENCE) |
wall_proximity | SPY approaching the gamma call or put wall |
dix_band | DIX classifier band (Favorable / Leaning / Neutral / Cautionary / Adverse) |
volatility_regime | VIX term-structure regime flip (CONTANGO / BACKWARDATION / FLAT) |
vix_band | VIX level band change (Low / Normal / Elevated / High / Fear / Extreme fear) |
health_score_band | Health score band crossing (favorable / mixed / cautionary / adverse) |
cascade_stage | Transmission cascade stage 0–5 |
zero_dte_notional_band | 0DTE notional dollar volume band (light / active / elevated / heavy / extreme) |
mag7_weight_band | Mag-7 share of S&P 500 (concentrated / elevated / normal) |
sma_stack | CTA trend-stack score 0–4 (count of SPY ≥ 20/50/100/200 SMA) |
cot_specs_band | CFTC speculator net positioning Z-band (extreme_short / short / neutral / long / extreme_long) |
aaii_spread_band | AAII bull-bear spread band (extreme_bear / bear / neutral / bull / extreme_bull) |
squeeze_setup | Composite short-squeeze setup state (none / partial / primed) |
Row shape
Every row in signal_transitions carries:
| Column | Meaning |
|---|---|
timestamp | ET-aware ISO 8601 with offset (Law 1) — the moment the transition was detected |
signal | One of the 18 classifier names above |
from_state | The previous band/state (NULL only on the very first sighting per signal) |
to_state | The new band/state |
value | The raw numeric reading at the transition, where applicable |
spy_at_event | SPY close at event time (for cross-referencing price action) |
context | JSON blob with extra per-classifier context (optional) |
Dedup and restart behavior
Two mechanisms prevent transition floods:
- Schema dedup.
UNIQUE (signal, to_state, timestamp)prevents identical rows from being inserted twice.INSERT OR IGNOREswallows the duplicate without raising. - In-process last-seen cache. Same-state repeats don’t emit at all. The cache is seeded from the most recent DB row per signal at boot (
seed_last_seen_from_db), so a process restart doesn’t produce aNULL → Xflood — only a real state change emits.
A startup lint pass (lint_transitions) validates timestamps and known-signal coverage, surfacing any drift between KNOWN_SIGNALS and what’s actually in the table.
Retention
signal_transitions is keep-forever by default. Pruning is opt-in via RETENTION_TRANSITIONS_DAYS; default 0 means never prune. The retention contract is enforced by tests/test_retention_guard.py — any DELETE against this table that isn’t gated on the env-var check fails CI.
The endpoint that queries transitions caps its default window at 36 hours (days=1–14), but that’s a query convenience, not a retention horizon. The full history stays on disk indefinitely.
The unified events feed
/api/v1/events/feed composes five event kinds into one chronological stream:
| Kind | Source table | Severity surface |
|---|---|---|
alert | alerts (fired/resolved rows) | info / warn / critical per alert definition |
signal_transition | signal_transitions | Derived from signal + to_state |
cascade_transition | cascade_transitions | info for stage-down, warn for stage-up |
news | news_items | info |
source_failure | source_runs where ok=0 | warn if a source failed in the window |
Query knobs: days (1–14, default 7), kinds (comma-separated whitelist), severity minimum (info / warn / critical), limit (default 200, hard cap 1000), bucket=true to group by trading-day.
The dashboard’s Event Feed widget consumes this endpoint directly — the “Today / Yesterday / This week” grouping comes from bucket=true.
See also
- Lifecycle — stage 4 (capture) is where new transition rows get written.
- Source health —
source_failurerows in the unified feed. - Alignment — the
alignment_severitytransition is one of the loudest divergence signals. - Health score — the
health_score_bandtransition fires on score band crossings. - Code:
app/signals/transitions.py(emit + dedup + lint),app/signals/routes.py:get_events_feed(composer),app/signals/database.py(schema + retention helper).