← Work

Bitcoin Price ForecastMachine Learning

Predicting tomorrow's Bitcoin high, packaged as a working prediction API.

A decision-support tool, not a trading bot: it forecasts tomorrow's Bitcoin high so a trader can sanity-check a plan. The first model failed the way naive price models fail, and reframing the target (predict the change, not the price) cut test error by six times. A Dockerised FastAPI serves the model on live prices.

Choosing the data like a trader

The raw material is daily price history for Bitcoin, Ethereum, XRP and Solana. Two data decisions shaped everything, and both are argued in the notebook rather than assumed. XRP was cut after a dedicated check: its year-by-year correlation with Bitcoin swings between 0.38 and 0.74, too unstable to trust as a signal, while Ethereum (0.83) and Solana (0.84) hold steady. And joining Solana's shorter history trims the data to 2020 onward, a loss accepted deliberately: the pre-2020 market was retail-dominated, and next to today's institution-heavy flows it reads as historical noise. Splits are strictly chronological (1,027 train, 343 validation, 343 test days), so the model is always judged on days after everything it learned from.

When the baseline broke

The baseline predicted the price directly, and the test set showed exactly why that fails: when Bitcoin rallied past 100,000 dollars in late 2024, the model kept predicting the 65,000 dollar world it was trained in, off by 6,752 dollars on average. A regime shift is precisely where a price forecaster earns its keep, and precisely where a naive one collapses.

Predict the change, not the price

The fix was to change the question: predict how far tomorrow's high sits above today's close, as a ratio, with a quantile LightGBM. Momentum, volatility and volume features came from my own pip package, reused from the draft prediction project. Two restraint decisions are documented rather than hidden: alpha 0.8 aims at the upper tail of the distribution, where a next-day high actually lives in a bullish regime, and there is deliberately no hyperparameter sweep, because naive cross-validated search leaks the future in time series and aggressively tuned parameters tend to memorise one market regime. Test error fell from 6,752 to 1,110 dollars, and the forecast tracks a rally it had never seen.

Served, not shelved

The trained model ships inside a Dockerised FastAPI service that pulls live daily prices from the Kraken exchange API, rebuilds the features, and answers /predict/btc with tomorrow's forecast high. The repository is public.

# ===== Quantile LGBM (alpha=0.8) =====
q_model = LGBMRegressor(
    objective='quantile',
    alpha=0.8,
    n_estimators=600,
    learning_rate=0.05,
    num_leaves=63,
    subsample=0.8,
    colsample_bytree=0.8,
    min_child_samples=40,
    reg_alpha=0.1,
    reg_lambda=0.3,
    random_state=42
)
# serving: y_pred_price = (1.0 + y_pred_ratio) * today_price
The model that survived the rally: a quantile LightGBM (alpha 0.8) on a ratio target, converted back to a price at serving time (from the experiment notebook).