Is the crypto market bull, chop, or bear? One command, public data, no API keys, zero dependencies.
$ coinregime -H
market: BEAR (BTC $61460.01 vs SMA100 $72886.27 / SMA200 $78160.27; breadth 0/8 above 30d SMA)
ADA ---- 0/4 [-]
BNB ---- 0/4 [-]
BTC ---- 0/4 [-]
DOGE ---- 0/4 [-]
ETH ---- 0/4 [-]
LINK ---- 0/4 [-]
SOL ---- 0/4 [-]
XRP ---- 0/4 [-]
coinregime answers two questions every trading bot, DCA script, and dip-buyer eventually hard-codes badly:
- What regime is the market in?
bull/chop/bear, from BTC's position vs its 100d and 200d SMA plus market breadth (how many watchlist coins sit above their own 30d SMA). - Which coins are running? A per-coin RUN gate that fires only on confluence — at least 3 of 4 independent strength signs — never on a single indicator.
This is the extracted steering layer of a live trading bot: the same rule that decides, in production, whether a take-profit ladder should trail a runner or harvest at fixed thresholds. It exists as a standalone CLI because the question "should my mean-reversion strategy even be on right now?" is useful far beyond that one bot.
pipx install git+https://github.com/renezander030/coinregime
# or: pip install git+https://github.com/renezander030/coinregime
# or just grab the single file — it's stdlib-only:
# wget https://raw.githubusercontent.com/renezander030/coinregime/main/coinregime.pyPython 3.9+. No dependencies, no config file, no API keys.
coinregime # JSON (pipe to jq, feed your bot)
coinregime -H # compact human table
coinregime --coins BTC,ETH,PEPE,WIF
coinregime --force # skip the 6h cache, refetch now
coinregime --min-signals 4 # stricter RUN gateJSON output is stable and bot-friendly:
{
"market": "bear",
"btc": { "px": 61460.01, "sma100": 72886.27, "sma200": 78160.27 },
"breadth_above_sma30": "0/8",
"coins": {
"SOL": {
"running": false,
"signals": {
"above_sma30": false,
"ret30_strong": false,
"fresh_30d_high": false,
"higher_lows": false
},
"px": 84.21,
"sma30": 95.10
}
}
}Use it in a shell pipeline:
# halt a dip-buy script in bear markets
[ "$(coinregime | jq -r .market)" = "bear" ] && exit 0
# list running coins
coinregime | jq -r '.coins | to_entries[] | select(.value.running) | .key'Market label — conservative by design:
| Label | Condition |
|---|---|
bull |
BTC above both its 100d and 200d SMA and ≥ half the watchlist above its own 30d SMA |
bear |
BTC below its 200d SMA and ≤ a third of the watchlist above its 30d SMA |
chop |
everything else |
Requiring breadth to confirm the BTC trend filters out the classic trap: BTC pumping alone while the rest of the market bleeds.
Per-coin RUN gate — a coin is "running" when ≥ 3 of these 4 agree:
- price above its 30d SMA
- 30d return ≥ +25%
- fresh 30d high within the last 3 days
- higher lows: the 7d low sits above the prior 7d low
Each sign measures a different aspect of strength (level, momentum, recency, structure), so the gate needs genuine confluence and ignores single-indicator noise.
Why this matters for mean-reversion strategies: a dip-buy / take-profit ladder beats buy-and-hold in chop and down markets, but structurally lags a coin that keeps rising — it shaves the winner early. The RUN gate recognizes that condition before you pay for it, instead of after your realized returns lag.
- Daily klines from the Binance public API, with automatic fallback to the Gate.io public API (covers geo-blocks and pairs Binance doesn't list). No authentication anywhere.
- Fails safe: any fetch or compute error marks that coin "not running" and can degrade the market label to
"unknown"— it never fabricates a bullish answer. Errors are reported per-coin in the output. - Results are cached for 6 hours (
~/.cache/coinregime/state.json) so cron jobs and pipelines don't hammer public endpoints.--forcerefetches;--ttltunes it.
| Flag | Env var | Default | Meaning |
|---|---|---|---|
--coins |
COINREGIME_COINS |
BTC,ETH,SOL,BNB,XRP,ADA,DOGE,LINK |
watchlist |
--min-signals |
COINREGIME_MIN_SIGNALS |
3 |
signs (of 4) required for RUN |
--ret30 |
COINREGIME_RET30_MIN |
25 |
30d-return threshold, percent |
--ttl |
COINREGIME_TTL_H |
6 |
cache TTL, hours |
from coinregime import fetch_closes, running_from_series, market_label
closes = fetch_closes("SOL")
running, signals = running_from_series(closes)running_from_series(closes, ppd=...) accepts any close series — pass ppd=24 for hourly candles. The same function the CLI uses is the one you backtest, so what you validate offline is exactly what runs live.
MIT