Predicting six-hour Bitcoin moves: a negative result
The point of this project is the pipeline discipline (pinned data, purged time-ordered splits, a frozen threshold and honest uncertainty) rather than a profitable signal.
- Role
- Sole author
- Context
- Can a blending ensemble predict tradeable six-hour BTCUSDT moves?
- Stack
- Pythonscikit-learnpandasNumPypytestJupyter
- balanced accuracy on the locked test; the bootstrap interval includes 0.5
- 0.514
- strategy return after costs, against −28.1% for buy-and-hold
- −36.7%
- six-hour candles collected and cross-checked against 79 monthly archives
- 9,613
- candidate features screened
- 159
January to July 2026, 846 six-hour bars. The vertical line is chance (0.50). The band is the 95% moving-block bootstrap interval for the blend, 0.483 to 0.543, and it includes chance.
The question
Can a blending ensemble of three base classifiers (L2 logistic regression, random forest and Gaussian naive Bayes, combined by a regularized logistic meta-learner) predict six-hour BTCUSDT moves large enough to clear a 0.1% one-way transaction cost?
The answer
No. On a locked January to July 2026 test of 846 bars, balanced accuracy was 0.514, ROC-AUC 0.519 and the Brier score 0.251. The moving-block bootstrap interval for balanced accuracy spans 0.5.
The blend did not beat its best base model out of sample: Gaussian naive Bayes alone scored 0.524 on the test window, which I read as model-selection noise rather than skill. A costed long/cash backtest returned −36.7% over the test window against −28.1% for buy-and-hold, across 289 position changes. The label is predictable slightly above chance in cross-validation, but not exploitable after costs in this window.
Why it is worth showing
The value is the discipline around the result.
- I collected the data myself from the Binance.US API (1 January 2020 to 31 July 2026) and cross-checked it against 79 monthly archives with published checksums. 11 cells differed between the two sources; they are disclosed, not averaged away.
- Every source file is hash-pinned. The notebook refuses to run if the data changes.
- Splits are time-ordered and purged at the boundaries, and the decision threshold was frozen on validation data before the locked test.
- A fixed seed makes the run deterministic, and the last cell re-asserts every headline number and fails if anything drifts.
- A pytest suite covers the backtest, the models, the evaluation code and source-data integrity.