Crypto Regime Detection with a Local AI Agent: Bull vs Bear vs Sideways
Crypto regime detection is the skill that separates consistently profitable traders from those who keep getting wrecked. Most traders treat the market as one thing — when it's actually three completely different environments: bull (trending up), bear (trending down), and sideways (ranging). The strategy that crushes it in a bull market gets destroyed in a bear. This guide shows you how to build a local AI agent that classifies the current regime and adjusts your approach accordingly.
Why Regime Detection Matters More Than Signal Quality
Here's the hard truth r/algotrading learned the expensive way: a strategy's win rate is dominated by the regime it's running in, not the quality of its signals.
An RSI mean-reversion strategy:
- In sideways market: 65% win rate. The market bounces between support and resistance. RSI oversold/overbought actually mean something.
- In bull market: 40% win rate. "Oversold" RSI just means a dip to buy — but the strategy sells too early.
- In bear market: 25% win rate. "Oversold" RSI is a falling knife. Every bounce fails.
The strategy didn't change. The market regime did.
The solution isn't finding a better strategy. It's knowing which regime you're in and either switching strategies or sitting out entirely.
Defining the Three Regimes
Before building the detector, define what each regime actually means:
Bull Regime:
- Price is above the 50-day and 200-day moving averages
- 50-day MA is above 200-day MA (golden cross)
- Higher highs, higher lows on the weekly chart
- Volume increases on up moves, decreases on down moves
Bear Regime:
- Price is below the 50-day and 200-day moving averages
- 50-day MA is below 200-day MA (death cross)
- Lower highs, lower lows on the weekly chart
- Volume increases on down moves (capitulation)
Sideways/Ranging Regime:
- Price oscillates between defined support and resistance levels
- Moving averages flat or entangled
- ADX (Average Directional Index) below 20-25
- No clear trend in either direction
Building the Regime Detector
import requests
import pandas as pd
import numpy as np
def get_btc_data(days=200):
"""Fetch BTC OHLC from Binance free API."""
url = "https://api.binance.com/api/v3/klines"
params = {"symbol": "BTCUSDT", "interval": "1d", "limit": days}
data = requests.get(url, params=params).json()
df = pd.DataFrame(data, columns=[
"time", "open", "high", "low", "close", "volume",
"close_time", "quote_vol", "trades", "buy_base", "buy_quote", "ignore"
])
df["time"] = pd.to_datetime(df["time"], unit="ms")
df.set_index("time", inplace=True)
for col in ["open", "high", "low", "close", "volume"]:
df[col] = df[col].astype(float)
return df
def calculate_adx(df, period=14):
"""Calculate ADX (trend strength indicator)."""
df = df.copy()
df["tr"] = np.maximum(
df["high"] - df["low"],
np.maximum(
abs(df["high"] - df["close"].shift(1)),
abs(df["low"] - df["close"].shift(1))
)
)
df["dm_plus"] = np.where(
(df["high"] - df["high"].shift(1)) > (df["low"].shift(1) - df["low"]),
np.maximum(df["high"] - df["high"].shift(1), 0), 0
)
df["dm_minus"] = np.where(
(df["low"].shift(1) - df["low"]) > (df["high"] - df["high"].shift(1)),
np.maximum(df["low"].shift(1) - df["low"], 0), 0
)
atr = df["tr"].rolling(period).mean()
di_plus = (df["dm_plus"].rolling(period).mean() / atr) * 100
di_minus = (df["dm_minus"].rolling(period).mean() / atr) * 100
dx = (abs(di_plus - di_minus) / (di_plus + di_minus)) * 100
adx = dx.rolling(period).mean()
return adx, di_plus, di_minus
def detect_regime(df):
"""Classify current market regime."""
close = df["close"]
# Moving averages
ma50 = close.rolling(50).mean()
ma200 = close.rolling(200).mean()
# ADX for trend strength
adx, di_plus, di_minus = calculate_adx(df)
# Current values
current_price = close.iloc[-1]
current_ma50 = ma50.iloc[-1]
current_ma200 = ma200.iloc[-1]
current_adx = adx.iloc[-1]
current_di_plus = di_plus.iloc[-1]
current_di_minus = di_minus.iloc[-1]
# Recent trend (last 20 candles)
recent_high = df["high"].iloc[-20:].max()
recent_low = df["low"].iloc[-20:].min()
mid_price = (recent_high + recent_low) / 2
# Regime classification
regime_score = 0
signals = []
# Price vs moving averages
if current_price > current_ma50:
regime_score += 1
signals.append("Price above MA50 ✅")
else:
regime_score -= 1
signals.append("Price below MA50 ❌")
if current_price > current_ma200:
regime_score += 1
signals.append("Price above MA200 ✅")
else:
regime_score -= 1
signals.append("Price below MA200 ❌")
# MA alignment
if current_ma50 > current_ma200:
regime_score += 1
signals.append("Golden cross (MA50 > MA200) ✅")
else:
regime_score -= 1
signals.append("Death cross (MA50 < MA200) ❌")
# ADX trend strength
is_trending = current_adx > 25
if is_trending:
if current_di_plus > current_di_minus:
regime_score += 1
signals.append(f"Strong uptrend (ADX: {current_adx:.1f}) ✅")
else:
regime_score -= 1
signals.append(f"Strong downtrend (ADX: {current_adx:.1f}) ❌")
else:
signals.append(f"Weak trend / ranging (ADX: {current_adx:.1f}) ↔️")
# Classify regime
if not is_trending:
regime = "SIDEWAYS"
recommendation = "Mean-reversion strategies may work. Reduce position size. Consider sitting out."
elif regime_score >= 2:
regime = "BULL"
recommendation = "Trend-following and momentum strategies. Buy dips. Avoid selling too early."
elif regime_score <= -2:
regime = "BEAR"
recommendation = "Sit out or hedge. Mean-reversion sells bounce. Trend-following is risky."
else:
regime = "TRANSITIONING"
recommendation = "Mixed signals. Reduce position size. Wait for clearer regime confirmation."
return {
"regime": regime,
"score": regime_score,
"adx": current_adx,
"price": current_price,
"ma50": current_ma50,
"ma200": current_ma200,
"signals": signals,
"recommendation": recommendation
}
# Run it
df = get_btc_data(200)
result = detect_regime(df)
print(f"\n🎯 MARKET REGIME: {result['regime']}")
print(f"Confidence Score: {result['score']} (range: -4 to +4)")
print(f"\nBTC Price: ${result['price']:,.0f}")
print(f"MA50: ${result['ma50']:,.0f}")
print(f"MA200: ${result['ma200']:,.0f}")
print(f"ADX: {result['adx']:.1f}")
print(f"\nSignals:")
for s in result['signals']:
print(f" {s}")
print(f"\n💡 Recommendation: {result['recommendation']}")
Adding an LLM Layer for Natural Language Interpretation
The numbers above are useful. But if you're running this inside an OpenClaw agent, you can add an Ollama-powered LLM to give a more nuanced interpretation:
import ollama
def interpret_regime_with_llm(regime_data):
"""Use local LLM to interpret regime data in plain English."""
prompt = f"""You are a crypto market analyst. Based on the following technical data,
provide a 3-4 sentence interpretation of the current market regime and what it means
for a retail trader.
Data:
- Regime: {regime_data['regime']}
- BTC Price: ${regime_data['price']:,.0f}
- 50-day MA: ${regime_data['ma50']:,.0f}
- 200-day MA: ${regime_data['ma200']:,.0f}
- ADX (trend strength): {regime_data['adx']:.1f}
- Confidence Score: {regime_data['score']} out of ±4
Be specific, educational, and avoid generic advice. No financial advice disclaimer needed —
this is internal analysis for a paper trading agent."""
response = ollama.chat(model="llama3", messages=[
{"role": "user", "content": prompt}
])
return response["message"]["content"]
# Add LLM interpretation
interpretation = interpret_regime_with_llm(result)
print(f"\n🧠 LLM Analysis:\n{interpretation}")
Sending Regime Reports to Telegram
Wire this into your OpenClaw agent to send daily regime updates:
import requests
import os
def send_regime_report(regime_data, interpretation):
"""Send regime analysis to Telegram."""
token = os.environ.get("TELEGRAM_BOT_TOKEN")
chat_id = os.environ.get("TELEGRAM_CHAT_ID")
regime_emoji = {"BULL": "🟢", "BEAR": "🔴", "SIDEWAYS": "🟡", "TRANSITIONING": "🟠"}
message = (
f"{regime_emoji.get(regime_data['regime'], '⚪')} **MARKET REGIME: {regime_data['regime']}**\n\n"
f"BTC: ${regime_data['price']:,.0f}\n"
f"MA50: ${regime_data['ma50']:,.0f}\n"
f"MA200: ${regime_data['ma200']:,.0f}\n"
f"ADX: {regime_data['adx']:.1f}\n\n"
f"📊 Analysis: {interpretation[:300]}...\n\n"
f"⚡ Recommendation: {regime_data['recommendation']}"
)
url = f"https://api.telegram.org/bot{token}/sendMessage"
requests.post(url, json={"chat_id": chat_id, "text": message, "parse_mode": "Markdown"})
How to Use Regime Detection in Practice
The regime detector is a filter, not a signal. Here's how it fits into your workflow:
- Run regime detection daily (or before each potential trade)
- In BULL regime: Use momentum/trend strategies. Be more aggressive with entries.
- In BEAR regime: Consider sitting out. If you trade, trade short-duration.
- In SIDEWAYS regime: Mean-reversion strategies. Smaller position sizes.
- In TRANSITIONING: Reduce size to 25-50% of normal. Wait for confirmation.
The most important decision the regime detector helps you make: whether to trade at all. In a bear market, the best strategy is often no strategy.
Get the Pre-Built Skill
Rather than building from scratch, you can install the Bear Market Regime Detector from the OpenClaw Skills Hub: https://paarthurnax970-debug.github.io/cryptoclawskills/
The skill runs this detection automatically, sends you daily regime reports via Telegram, and flags when regime changes.
Want the complete setup? The Home AI Agent Kit includes the regime detector, paper trading environment, and all supporting scripts.
Disclaimer: Regime detection is an analytical tool, not a trading signal. Markets can behave unexpectedly regardless of technical indicators. This article is for educational purposes only and does not constitute financial or investment advice. Always use paper trading before risking real capital.
Top comments (0)