AI/XGBoost Bitcoin Price Prediction in Python (Free Guide)
By Shakti Tiwari — Nifty Option Trader, Research Analyst & XGBoost Expert
Can machine learning predict Bitcoin? Not the price, no — but a well-built XGBoost classifier can estimate the probability that tomorrow's close is higher than today's, and that probabilistic edge is what real quant work looks like. In this free, code-first guide we build a complete BTC direction model in Python: free Binance data via ccxt, honest feature engineering, a time-series split (never a random one), and evaluation that doesn't lie to you. Everything runs free on a laptop or even a phone with Termux.
Frame the Problem Correctly
Predicting the exact price is regression on a near-random walk — you'll get a model that basically predicts "tomorrow ≈ today." Instead, predict direction:
- Target = 1 if next day's close > today's close, else 0
- Model output = probability, not certainty
- Anything consistently above ~52–55% accuracy with real out-of-sample data is already interesting; anyone promising 90% is selling you something.
Step 1: Free Bitcoin Data
pip install ccxt pandas numpy xgboost scikit-learn
import ccxt, pandas as pd, numpy as np
ex = ccxt.binance()
rows, since = [], ex.parse8601("2018-01-01T00:00:00Z")
while True:
b = ex.fetch_ohlcv("BTC/USDT", "1d", since=since, limit=1000)
if not b: break
rows += b
since = b[-1][0] + 1
if len(b) < 1000: break
df = pd.DataFrame(rows, columns=["ts","open","high","low","close","volume"])
df["date"] = pd.to_datetime(df["ts"], unit="ms")
df = df.set_index("date").drop(columns="ts")
No API key needed — Binance public market data is free, and BTC/USDT is the world's most liquid crypto pair. Indian traders can research on this data and execute on FIU-registered exchanges like CoinDCX in INR.
Step 2: Feature Engineering (Where the Edge Lives)
XGBoost is only as good as its features. Use only information available at the close of day t to predict day t+1.
def make_features(df):
out = df.copy()
out["ret_1"] = out["close"].pct_change()
out["ret_5"] = out["close"].pct_change(5)
out["ret_10"] = out["close"].pct_change(10)
out["vol_10"] = out["ret_1"].rolling(10).std()
out["vol_30"] = out["ret_1"].rolling(30).std()
out["ma_ratio"] = out["close"] / out["close"].rolling(20).mean()
out["hl_range"] = (out["high"] - out["low"]) / out["close"]
out["vol_chg"] = out["volume"].pct_change(5)
# RSI(14)
d = out["close"].diff()
up = d.clip(lower=0).rolling(14).mean()
dn = (-d.clip(upper=0)).rolling(14).mean()
out["rsi"] = 100 - 100 / (1 + up / (dn + 1e-9))
out["dow"] = out.index.dayofweek # BTC trades weekends too
return out
df = make_features(df)
df["target"] = (df["close"].shift(-1) > df["close"]).astype(int)
df = df.dropna()
The shift(-1) on the target is the only place the future is allowed to appear. Every feature uses past data only.
Step 3: Time-Series Split — Never Shuffle
A random train/test split leaks the future into training (adjacent days are correlated). Split by date instead:
FEATURES = ["ret_1","ret_5","ret_10","vol_10","vol_30",
"ma_ratio","hl_range","vol_chg","rsi","dow"]
split = int(len(df) * 0.8)
X_tr, y_tr = df[FEATURES].iloc[:split], df["target"].iloc[:split]
X_te, y_te = df[FEATURES].iloc[split:], df["target"].iloc[split:]
Step 4: Train XGBoost
from xgboost import XGBClassifier
from sklearn.metrics import accuracy_score, roc_auc_score
model = XGBClassifier(
n_estimators=300, max_depth=3, learning_rate=0.03,
subsample=0.8, colsample_bytree=0.8, eval_metric="logloss"
)
model.fit(X_tr, y_tr)
proba = model.predict_proba(X_te)[:, 1]
pred = (proba > 0.5).astype(int)
print("Accuracy:", round(accuracy_score(y_te, pred), 4))
print("ROC-AUC :", round(roc_auc_score(y_te, proba), 4))
Keep max_depth small (2–4). Deep trees on a few thousand daily candles memorize noise. If train accuracy is 90% and test is 50%, you've built a noise museum, not a model.
Step 5: From Probability to a Strategy
Only act on confident calls, and always cost it like an Indian trader — exchange fees plus the 1% TDS on sells make churn expensive.
test = df.iloc[split:].copy()
test["proba"] = proba
test["pos"] = np.where(test["proba"] > 0.55, 1, 0) # trade only high-confidence longs
test["strat"] = test["pos"] * test["close"].pct_change().shift(-1)
COST = 0.002
test["strat_net"] = test["strat"] - test["pos"].diff().abs().fillna(0) * COST
print("Strategy equity:", round((1 + test["strat_net"].dropna()).prod(), 3))
print("Buy & hold :", round((1 + test["close"].pct_change().dropna()).prod(), 3))
What to Check Before Believing Your Model
-
Feature importance:
model.feature_importances_— if one feature dominates, suspect leakage. - Walk-forward validation: retrain every 6 months on a rolling window instead of one static split.
- Multiple regimes: test across bull, bear, and sideways periods; BTC's regime shifts are violent.
- Baseline: always compare against buy-and-hold and a coin-flip. Beating neither means delete and restart.
Key Takeaway
XGBoost won't tell you Bitcoin's price next week — nothing will. But a small, disciplined feature set, a strict time-based split, and probability-threshold trading give you a measurable, testable edge framework. The model is 20% of the work; refusing to leak the future is the other 80%.
Disclaimer
This article is for education only and is not financial, investment, or trading advice. Crypto assets are volatile and high-risk; past model performance never guarantees future results. Consult a qualified professional before acting.
🔗 Get the book on Amazon: https://www.amazon.in/dp/B0H9ZNTBPK
📢 Daily Nifty analysis on Telegram: https://t.me/shaktitrade
💬 Need Help? (Free Support)
If you have any trouble building your own AI trading model, or questions about any article — I can help you for free.
📧 Email: shaktitiwari715@gmail.com
📢 Telegram: https://t.me/shaktitrade
No course, no upsell — just genuine help for retail traders.

Top comments (0)