Back to all posts

AI Engineering · June 23, 2026 · 3 min read

CryptoSense: Predicting BTC Price with LSTM and TensorFlow

CryptoSense: Predicting BTC Price with LSTM and TensorFlow
PythonTensorFlowKerasFastAPIGoogle ColabPandasScikit-learn

Why I Built This

I wanted to learn TensorFlow properly, not just follow a tutorial and forget it. So I picked a real problem, real data, and built end to end: data pipeline, model training, evaluation, and a live API.

CryptoSense is that project. A lightweight LSTM that takes the last 60 hours of BTC price and predicts the next one.

The Data

Kaggle's Bitcoin Historical Dataset, 7.6 million rows of minute-level OHLCV data going back to 2012. That's 13 years of every single minute BTC ever traded.

Raw minute data is too noisy and too large to train on directly. First step was resampling to hourly candles, which brought it down to 126k rows — clean, manageable, and still plenty of signal.

No nulls, no missing candles after resampling. Surprisingly clean dataset.

Model Architecture

Kept it intentionally small. This isn't a production quant model, it's a learning exercise with a real output.

LSTM(64, return_sequences=True)

Dropout(0.2)

LSTM(32)

Dropout(0.2)

Dense(1)

29,345 total parameters. Trains in under 10 minutes on a Colab T4 GPU.

Input shape is (60, 1) — 60 hourly close prices, scaled to 0-1 using MinMaxScaler. Output is the predicted next hour price, inverse transformed back to USD.

Training

80/20 train/test split, no shuffling because order matters in time series. EarlyStopping with patience of 5 monitored val_loss and stopped training at epoch 8 — best weights were from epoch 3 where val_loss hit 2.55e-06.

One thing worth noting: val_loss bounced around instead of decreasing cleanly. Mild overfitting on a dataset that spans 13 years of wildly different market regimes — 2012 BTC at $4 vs 2025 BTC at $125k. The model can't perfectly generalize across that range and that's expected.

Results

Evaluated on 25k unseen hourly candles:

Metric Value
MAE $4,908
RMSE $7,192

At current BTC price around $65k that's roughly 7.5% average error. The model tracks trends well but undershoots during sharp spikes — which is the hardest part of any time series problem.

The predicted vs actual chart tells the story better than the numbers. Orange follows cyan closely across the full price history, diverging most during the 2025 ATH spike around $125k. Expected behavior — black swan events don't have patterns to learn from.

Saving and Serving

Two things you must save after training, not just the model:

model.save('cryptosense_model.keras')

import pickle
with open('scaler.pkl', 'wb') as f:
    pickle.dump(scaler, f)

The scaler is just as important as the model. If you create a new scaler at inference time it produces different normalization and your predictions will be garbage. Save it, load it, use it consistently.

FastAPI Endpoint

Wrapped the model in a single POST endpoint:

@app.post("/predict")
def predict(data: PriceInput):
    input_array = np.array(data.prices).reshape(-1, 1)
    scaled_input = loaded_scaler.transform(input_array)
    scaled_input = scaled_input.reshape(1, 60, 1)
    predicted_scaled = loaded_model.predict(scaled_input)
    predicted_price = loaded_scaler.inverse_transform(predicted_scaled)
    return {"predicted_next_hour_price": round(float(predicted_price[0][0]), 2)}

Send 60 close prices, get back a predicted next hour price. Tested live in Colab via the auto-generated Swagger UI at /docs.

{
  "predicted_next_hour_price": 61635.61
}

What I Learned

A few things that actually stuck from building this:

Double brackets matter. df[['Close']] returns a DataFrame with shape (n, 1). df['Close'] returns a Series with shape (n,). MinMaxScaler needs 2D input — one wrong bracket and you get an error that takes 10 minutes to debug.

Never shuffle time series. Standard train/test split shuffles by default. For time series the past must stay before the future or you're leaking future data into training.

Save the scaler. Everyone forgets this the first time.

EarlyStopping is your friend. Set it and forget it. It stopped my training at epoch 8 instead of wasting compute on 42 more epochs that wouldn't have helped.

Next Steps

The obvious extension is plugging this into CatalystWatch as the anomaly detection layer — replacing rule-based spike detection with an LSTM-based anomaly score. That's the plan once I'm happy with the model's signal quality.

This project is for educational purposes only and does not constitute financial advice.

Ask me anything 👋

Ask about Malahim

AI online
Hi! I can answer questions about Malahim's work, projects, skills, and experience. What do you want to know?

Suggested

Enter to send · Shift+Enter for new line