The question
Can public price and volume indicators predict whether a stock closes higher on the next trading day? I built a complete pipeline to test the idea against a simple baseline, then made the results and model explanations visible in a Streamlit dashboard.
Data and approach
I used two years of daily AAPL price and volume data from Yahoo Finance. The latest 20% of labelled observations formed a chronological holdout of 91 trading days in the 25 September 2026 run. The target is whether the next closing price exceeds the current close.
I converted the inputs into 18 scale-free features, including returns, distances from moving averages, RSI, MACD as a percentage of price, Bollinger measures, relative volatility and volume ratios. This avoids feeding raw price levels into the model, although it does not establish that a model trained on one ticker generalises to another.
How I built it
Separate Python modules fetch data, engineer features and train models. I compared Logistic Regression, Random Forest and XGBoost, then tuned XGBoost with 108 parameter combinations across five TimeSeriesSplit folds: 540 cross-validation fits. The dashboard loads saved artefacts, displays candlestick charts and uses SHAP to explain individual predictions.
I kept the latest unlabelled row for live prediction rather than predicting from the last row whose outcome was already known. For the dashboard backtest, a fresh model is fitted on the first 80% of the selected ticker’s history and evaluated on the remaining 20%.
What the evaluation showed
No model exceeded the always-UP baseline’s 54.9% accuracy on this holdout. Random Forest had the highest ROC-AUC at 0.545, but this small test set does not establish a reliable predictive advantage.
| Model | Accuracy | ROC-AUC |
|---|---|---|
| Logistic Regression | 54.9% | 0.543 |
| Random Forest | 53.8% | 0.545 |
| XGBoost, default | 54.9% | 0.533 |
| XGBoost, tuned | 53.8% | 0.499 |
| Always predict UP | 54.9% | 0.500 |
The tuned model reached 0.526 ROC-AUC in cross-validation and 0.499 on the holdout. Tuning did not translate into better unseen performance. With roughly 360 training rows, that is consistent with selecting noise rather than finding a useful signal.
These are the saved results from the repository’s 25 September run, not a fresh evaluation on today’s market data. Downloads and adjusted-price revisions can change the scores.
What I changed and why
I replaced raw indicator levels with ratios and percentages, separated live prediction from labelled training rows, and corrected the backtest’s timing. A signal computed at day t’s close is now paired with the return from that close to the next day’s close, rather than an earlier return.
I also display the always-UP baseline alongside model accuracy. A complex model matching a simple rule is not a useful improvement. SHAP makes the model’s behaviour easier to inspect, but an explanation does not prove that a prediction is reliable.
What worked, and what did not
The engineering workflow connects live data, feature generation, model comparison, explanations and a held-out backtest. The predictive hypothesis did not earn its place: none of the models showed a clear advantage over the baseline, and tuning made the saved model’s holdout ROC-AUC worse.
The practical outcome was a decision to keep this as an evaluation and engineering project, rather than present it as a trading signal. Measuring a failed idea is useful when it prevents an unsupported deployment claim.
Limits and next steps
The raw probability scores are not calibrated. The backtest excludes transaction costs and slippage and assumes execution at the close used to calculate the signal. Those assumptions would need a more realistic execution model before interpreting returns as achievable performance.
Preprocessing is still fitted before cross-validation rather than inside each fold, and the splits have no explicit gap for next-day labels. I would tighten that evaluation setup, test multiple time windows and tickers, and assess calibration before drawing stronger conclusions. The saved prediction model is trained on AAPL; choosing another ticker requires retraining for a ticker-specific model.
Run the demo
The Streamlit dashboard runs locally. It supports ticker selection, optional retraining, price charts, SHAP explanations and a comparison with buy-and-hold. The repository includes setup instructions and offline feature/target tests. There is no public hosted demo linked from this project.