Building a BTC Decision Engine with Hidden Markov Models
Why a Decision Engine, Not a Trading Bot
Let me be clear upfront: this is not a trading bot, and it does not make autonomous trades. The BTC Control Engine is a decision support tool that analyses market conditions and provides structured signals. The final decision always stays with the human.
I built it because I wanted a disciplined, quantitative framework for thinking about Bitcoin positions. Emotions are the enemy of good decision-making in volatile markets, and a systematic approach helps me stay rational.
Hidden Markov Models: The Core Idea
A Hidden Markov Model (HMM) assumes that the system you are observing (in this case, the BTC market) exists in one of several hidden states, and the observable data (price, volume, volatility) are emissions from those states. The "hidden" part is key: you cannot directly observe which state the market is in, but you can infer it from the data.
For BTC, I defined four market states:
- Accumulation: Low volatility, sideways price action, often precedes a move
- Markup: Trending upward with increasing volume
- Distribution: High volatility at elevated prices, potential reversal signals
- Markdown: Declining prices with varying volume patterns
Model Training
I used the hmmlearn library to train a Gaussian HMM on historical BTC data. The observation features are:
import numpy as np
from hmmlearn import hmm
def prepare_features(df):
features = np.column_stack([
df["log_return"].values,
df["volatility_20d"].values,
df["volume_zscore"].values,
df["rsi_normalised"].values
])
return features
# Train the model
model = hmm.GaussianHMM(
n_components=4,
covariance_type="full",
n_iter=200,
random_state=42
)
model.fit(training_features)
The model learns the transition probabilities between states and the emission distributions for each state. Once trained, it can take current market data and estimate which state the market is most likely in.
Signal Generation
The HMM state inference feeds into a signal generation layer. Each state maps to a set of possible actions, weighted by confidence:
- Accumulation with high confidence: Signal to consider building position
- Markup: Signal to hold and consider taking partial profits at resistance levels
- Distribution: Signal to tighten risk management and reduce exposure
- Markdown: Signal to stay defensive, look for accumulation patterns
Importantly, the engine never outputs a simple "buy" or "sell." It provides the inferred state, the confidence level, the transition probabilities (what state is likely next), and a structured analysis. The human interprets this in the context of broader information.
Real-Time Data Pipeline
The engine ingests data from multiple sources:
- Binance API for price and volume data at 1-minute, 1-hour, and 1-day intervals
- On-chain metrics from Glassnode for network activity indicators
- Fear and Greed Index for sentiment context
async def fetch_market_data():
tasks = [
fetch_binance_ohlcv("BTCUSDT", "1h", limit=500),
fetch_onchain_metrics(),
fetch_sentiment_index()
]
ohlcv, onchain, sentiment = await asyncio.gather(*tasks)
return merge_market_data(ohlcv, onchain, sentiment)
Backtesting Framework
Before trusting any signal system, you need to test it against historical data. I built a backtesting framework that replays historical data through the engine and measures how well the state classifications align with actual market behaviour:
- State persistence: When the model identifies "Markup," does the price actually continue upward? In testing, markup states preceded positive returns over the next 7 days roughly 72% of the time.
- Transition accuracy: When the model predicts a transition from Accumulation to Markup, how often does it happen? About 65% of the time, which is significantly better than chance.
- False signals: Distribution states that resolve back into Markup are the main source of false signals. This is expected and is why the engine provides confidence levels.
The Dashboard
The engine outputs to a dashboard that shows:
- Current inferred state with confidence percentage
- State transition probability matrix for the next period
- Historical state timeline overlaid on price chart
- Active signals with their reasoning
Technical Challenges
HMMs have some well-known limitations that I had to work around:
- Regime sensitivity: A model trained on 2020-2022 data does not perform well on 2024 data because market dynamics shift. I retrain monthly on a rolling 18-month window.
- Number of states: Choosing 4 states was partly empirical. I tested 3, 4, 5, and 6 states. Four produced the most interpretable and stable classifications.
- Feature engineering: Raw price is useless as an input. Log returns, normalised volatility, and z-scored volume work much better because they are stationary or near-stationary.
Risk Management Integration
The engine integrates a position sizing module that suggests allocation sizes based on the current market state and confidence level. In high-confidence accumulation states, the suggested allocation is larger. In distribution or low-confidence states, the suggested allocation shrinks. This is based on the Kelly criterion adapted for the specific risk profile I have configured. The position sizing suggestions are always conservative, never recommending more than a predefined maximum percentage of total portfolio value. This systematic approach to risk management is arguably more valuable than the signal generation itself, because it prevents the emotional tendency to over-allocate during periods of excitement.
Alerts and Notifications
The engine sends notifications when significant state transitions occur. If the model detects a shift from Accumulation to Markup, or from Distribution to Markdown, I receive an alert via Telegram with the state change details, confidence level, and suggested actions. These alerts are rate-limited to prevent notification fatigue. At most, I receive 2 to 3 alerts per week, and each one contains enough context to be actionable without opening the dashboard.
What I Learned
Building the BTC Control Engine reinforced an important principle: the best quantitative tools do not replace human judgement. They structure it. Having a systematic framework for market state assessment helps me avoid the two biggest mistakes in volatile markets: panic selling and euphoric over-buying. The engine does not tell me what to do. It tells me what the data suggests, and I decide.