The classifier, explained
The Read’s state line comes from a single, versioned, deterministic classifier -
regime.py’s classify() function. Given the same inputs, it always produces the
same output: no wall-clock reads, no external calls, nothing that could make the same
historical day classify differently on a re-run.
Version
The currently pinned version is v1.1. v1 shipped first and its thresholds are
frozen permanently once shipped - a rule change is never an edit to v1’s numbers, only
a new version (v2, or a dotted follow-up like v1.1). v1.1 is exactly that: an
additive follow-up, not a replacement. It changed exactly one thing - see “v1 versus
v1.1” below - and everything else about the classifier’s scope, inputs, and vocabulary
carries over from v1 unchanged.
Input set: core versus modifier versus adjacent
The classifier reads four input families, and only four: valuation (MVRV,
confirmed by Puell Multiple), flows (net exchange flow, exchange reserve change),
leverage (realized volatility), and halving position (calendar, always
available). These are the core inputs - the only things that can move state_id,
the six-way valuation-band × flow-direction label the Read shows.
Two other kinds of input exist and are deliberately kept out of state_id:
- Modifiers are stored and shown, but never gate the state itself. ETF net flow is one - real data, displayed alongside the flows module, but excluded from the state because its history is too short to be treated as a reliable classifier input. Puell Multiple is a similar case within valuation: it’s computed and shown as a corroborating driver for MVRV’s band, but does not override it - if the two disagreed, MVRV’s percentile still decides the band in v1.
- Excluded inputs aren’t computed by this classifier at all. Funding rate, open interest, DVOL, and options skew are excluded on data-licensing grounds (their upstream sources don’t permit the redistribution this classifier’s own historical backfill would require), not because they’re judged unimportant. Macro series (rates, DXY, credit spreads) are out of scope for this classifier version entirely, by design, not merely deprioritized.
Sentiment is adjacent, not core
Fear & Greed is computed and shown next to the state line as a divergence signal -
useful context for whether the market’s mood agrees or disagrees with the fundamentals
the classifier actually reads - but it never feeds state_id or the core inputs above,
in any version. A market that turns euphoric or panicked purely on sentiment, with
valuation and flows unchanged, reads as an unchanged state here. That’s the intended
design: this classifier describes valuation, flows, leverage, and cycle position, not
the full scope of market psychology, and sentiment is kept visibly separate rather
than quietly blended in.
The hysteresis rule
A raw daily classification only becomes a confirmed state change after it holds for 3 consecutive days. If today’s raw reading differs from the currently confirmed state, that’s a candidate - not yet a transition. Only once the same differing reading has held for three days running does the classifier record a transition, backdated to the first of those three days. A candidate streak that breaks early - reverting to the already-confirmed state, or switching to a third state before reaching three days - never becomes a transition at all; those in-between days end up attributed to whichever state the streak eventually resolves to. This rule exists specifically to keep a single noisy day from flipping the headline state and flipping it back.
v1 versus v1.1: the neutral-band fix
v1’s flow-direction signal draws on a 30-day rolling sum of net exchange flow, recomputed daily. Because it’s a rolling sum, a single volatile day entering or leaving that 30-day window can cross zero on its own, flipping the accumulating/distributing label with no real change in the underlying trend - a flicker, not a real regime shift. v1.1 addresses this with one addition: a neutral dead-zone of ±5,000 BTC around zero on the 30-day flow signal specifically. A value inside that band abstains from casting a vote rather than forcing a fresh accumulating/distributing reading off a borderline number; the 90-day exchange-reserve-change signal, a slower window and not the diagnosed source of the flicker, is left unbanded and can still decide the direction on its own. Nothing else changed - same six-state vocabulary, same core/modifier/adjacent split, same 3-day hysteresis rule, same institutional-era comparison window. v1 itself remains frozen and unedited; v1.1 is purely additive.