Per-symbol calibration shipped
Until this date every symbol ran on BTC/ETH-fit thresholds. After the sweep each of the remaining eighteen gets its own thresholds, lifting balanced F1 by 2–6 points for alts (LINK highest at 0.45).
Exactly how the regime label for a given symbol is computed. If anything on this page becomes wrong, it's a bug — tell us.
For each (symbol, timeframe):
rt = ln(closet / closet-1).RVt = stdev(rt-29..t) × √(bars_per_year). Bars-per-year at the bar interval — annualized for comparability across timeframes.μt and σt over the last 240 values of RV.zt = (RVt − μt) / σt.Three states — low, normal, high. Starts in normal. Transitions require enter_k consecutive observations past the threshold; exits require exit_k observations back inside. Hysteresis prevents single-bar wicks from flipping the state.
normal → high when z ≥ high_enter for enter_k bars normal → low when z ≤ low_enter for enter_k bars high → normal when z ≤ high_exit for exit_k bars low → normal when z ≥ low_exit for exit_k bars
Per symbol, per timeframe, we grid-sweep 2,304 (high_enter, high_exit, low_enter, low_exit, enter_k, exit_k) combinations against 180 days of 1m klines, using a smoothed-percentile ground truth (top/bottom 10% of forward-window RV). For each combination we compute macro-F1 across the three classes and the mean alerts-per-day. The three named presets (conservative, balanced, aggressive) are picks on the Pareto front of (F1, alerts/day).
Per-symbol presets are stored at data/calibration/{symbol}/presets.json and loaded by the service at boot. Nightly CI reruns the sweep and opens a PR if F1 moved more than 0.001 on any preset — see changelog.
Coverage disclosure. The initial 180-day sweep produced per-symbol presets for 10 symbols (BTC, ETH, BNB, XRP, POL, SOL, DOGE, ADA, AVAX, LINK) — the F1 table in the blog post is from that run. The other 10 tracked symbols (SUI, TON, DOT, APT, NEAR, ATOM, OP, ARB, HBAR, LTC) run live on the BTC/ETH-derived defaults until their own sweep ships. Scale-free z-scoring keeps the shared thresholds reasonable, and the next calibration pass will publish their F1 alongside the existing ten.
Alongside the close-to-close RV that drives the FSM, each /v1/regime/{symbol} response includes two intraday-efficient estimators computed over the same 30-bar window:
parkinson_vol — √( Σ ln(H/L)² / (4 N ln 2) ), annualized. Uses the full bar range; 5× more statistically efficient than close-to-close but assumes no drift inside the bar.yang_zhang_vol — Yang & Zhang (2000): combines overnight variance, open-to-close variance, and Rogers-Satchell. Robust to both drift and open/close gaps.These are informational — useful for anyone who wants to sanity-check the RV signal or build their own thresholds off a more efficient estimator. The FSM deliberately stays on close-to-close because it's the broadly understood benchmark and every venue we read reports it cleanly. Values come through as null when the bar source (e.g. Coinbase fallback) didn't provide OHLC.
A second engine polls Binance, Coinbase and Bybit independently, computes a stateless (no FSM hysteresis) regime label per venue, and reports the majority. When the three agree you're seeing structural vol; when they diverge, one venue has a book anomaly.
low 1% / normal 92% / high 7%, vs the 10/80/10 ground-truth labelling. Hysteresis (the enter_k/exit_k consecutive-bar requirement) suppresses short spikes on purpose, so the FSM spends less time in the extreme classes than a classifier trained to recover 10/10 exactly would. That's a design trade-off: we accept lower recall on the extremes in exchange for fewer false-positive alerts. The F1 target is macro over three classes, so a calibrator that optimised for matching the 10/10 split would raise class-level recall but drop precision and overall F1.enter_k/exit_k streak the hysteresis counters stall but do not reset. Every event in the archive therefore has a real exchange bar behind it.aggressive (or your own /regime/{sym}/custom call).data/calibration/{symbol}/presets.json in the repo records the exact thresholds in use, and every calibration rerun updates them.Until this date every symbol ran on BTC/ETH-fit thresholds. After the sweep each of the remaining eighteen gets its own thresholds, lifting balanced F1 by 2–6 points for alts (LINK highest at 0.45).
Shares the same thresholds as the shorter timeframes for now. Per-tf calibration is on the list; the scale-free z framing makes the shared values reasonable in the meantime.
Added Coinbase and Bybit to the poller. Stateless classification only — hysteresis would require tracking FSM state per venue, which is on the list.
BTC/ETH swept over 180 days, three Pareto-front presets published. F1 0.37 / 0.39 / 0.41 at conservative / balanced / aggressive.