Instructions to use shalev396/stock-lstm-forecast with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Keras
How to use shalev396/stock-lstm-forecast with Keras:
# Available backend options are: "jax", "torch", "tensorflow". import os os.environ["KERAS_BACKEND"] = "jax" import keras model = keras.saving.load_model("hf://shalev396/stock-lstm-forecast") - Notebooks
- Google Colab
- Kaggle
Stock Price LSTM
Forecasts Apple's (AAPL) daily closing price after a chosen cutoff date. A small LSTM reads the last 60 daily
log-returns and predicts the next one. Each prediction is fed back in to forecast up to 60 business days.
The repo ships a frozen copy of the price history (prices.csv), so forecasts need no market-data API.
The honest result: on the held-out test period (2024-11-04 to 2026-07-31), the model's next-day RMSE is
$4.48. The naive forecast "tomorrow = today" scores $4.47, so the model does not beat it. Daily
prices are close to a random walk, and this model has learned little more than the average daily drift
(numbers from the run of 2026-09-25, metrics.json).
Model
- Architecture (
model.build_lstm):Input(60, 1) -> LSTM(64) -> Dropout(0.2) -> Dense(1), 16,961 parameters, float32. It is saved asmodel.keras(Keras 3). - Input: the last 60 daily log-returns
r_t = ln(C_t / C_(t-1))up to the cutoff, standardized with the training split's mean (0.000827) and standard deviation (0.018858). Both are stored inconfig.json. - Output: the next standardized log-return. Prices are rebuilt as
C_cutoff × exp(cumulative returns). For a horizon ofhdays, the model runshtimes, and each prediction becomes the newest input. prices.csv: 2,911 daily closes from 2015-01-02 to 2026-07-31 (yfinanceClose, split-adjusted, not dividend-adjusted). Valid cutoffs run from 2015-03-31 (the first day with a full 60-return window) to 2026-07-31. Other dates are clamped to that range and snapped to the previous trading day.
predict(cutoff, horizon) returns the portfolio's timeseries format:
{"model": "LSTM(64) on log-returns",
"history": [{"date": "2025-01-06", "price": 245.0}, ..., {"date": "2025-06-30", "price": 205.17}],
"forecast": [...], "actual": [...], "naive": [...],
"mae": 5.7788, "naiveMae": 7.8303}
history: up to 120 trading days, ending at the effective cutoff.forecast/naive: one point per forecast day.naiverepeats the cutoff close.actual: the real closes after the cutoff that exist inprices.csv. It is empty for the last date.mae/naiveMae: mean absolute error in $ over the days that have anactualprice, ornull.
Usage
from huggingface_hub import snapshot_download
import sys
path = snapshot_download("shalev396/stock-lstm-forecast")
sys.path.insert(0, path)
import model
predictor = model.load(path, device="cpu")
result = predictor.predict("2025-06-30", 30) # cutoff "YYYY-MM-DD", horizon in business days (1-60)
print(result["forecast"][-1], result["mae"], result["naiveMae"])
- Space API:
POST /gradio_api/call/predictwith{"data": ["2025-06-30", 30]}. See the Space. - Inference Endpoint:
handler.pyaccepts{"inputs": {"cutoff": "2025-06-30", "horizon": 30}}, or{"inputs": "2025-06-30", "parameters": {"horizon": 30}}.
Training
- Data: AAPL daily OHLCV from yfinance, downloaded on 2026-08-01 (2015-01-02 to 2026-07-31). The first 14 days are dropped as RSI warm-up, which leaves 2,897 days.
- Split: chronological 70 / 15 / 15. Each 60-day window belongs to the split of the day it predicts, so no window crosses a boundary. Train has 1,966 windows (2015-04-22 to 2023-02-09), validation 435 (2023-02-10 to 2024-11-01) and test 435 (2024-11-04 to 2026-07-31). Every scaler is fit on the train rows only.
- Recipe: Adam 1e-3, MSE on the scaled target, batch 32, up to 40 epochs. Early stopping (patience 6) and learning-rate halving watch validation loss, and the best epoch's weights are restored.
- Selection: among the univariate models the Space can run recursively, the one with the lowest validation RMSE is deployed. Test metrics are computed once.
- Hardware / time: CPU. Training all four networks took 575.3 s in this run, but the CPU was shared with
several other training jobs. An earlier uncontended run of the same recipe took 71.8 s (the legacy
results.json). - Full code: training/ · Colab
Experiments
Next-day (one-step) predictions on the original $ scale, for the same 435 test days across all variants. Rows are ranked by validation RMSE, and the deployed model is in bold:
| variant | params | val RMSE ($) | test RMSE ($) | test MAE ($) | test MAPE (%) |
|---|---|---|---|---|---|
| naive_persistence | - | 2.583 | 4.469 | 3.020 | 1.233 |
| lstm_returns (deployed) | 16,961 | 2.588 | 4.483 | 3.026 | 1.237 |
| simple_rnn_levels | 4,289 | 4.562 | 12.875 | 10.803 | 4.159 |
| lstm_levels | 16,961 | 5.896 | 14.482 | 10.904 | 4.173 |
| stacked_lstm_multi | 51,009 | 6.544 | 21.975 | 18.107 | 6.854 |
- Price levels break under a train-fit scaler. Train closes range from $22.58 to $182.01, and test closes from $172.42 to $340.08. Models that predict MinMax-scaled levels must extrapolate beyond the range they were trained on, so they lag badly: their test RMSE is $12.88 to $21.98. Adding volume, RSI and EMA features (the stacked multivariate LSTM) makes this worse.
- Log-returns fix the scaling problem, but the model has no signal to find. The returns target is roughly stationary, so the LSTM stays in its training range and matches naive persistence: test RMSE $4.483 vs $4.469, MAPE 1.237 % vs 1.233 %. Its validation loss was lowest after the first epoch, which means it mostly predicts the average daily return.
- Multi-day forecasts (what the Space shows): the recursive forecast was run from 84 test cutoffs (every 5th test day, 2024-11-01 to 2026-07-02) for 20 business days each. Its mean MAE is $10.67, against $10.87 for repeating the cutoff close. The small edge comes from the positive drift built into the forecast during a period when AAPL mostly rose. It is not evidence of predictive skill.
- Changes from the earlier version of this project: the model is now chosen on validation instead of test RMSE, and everything runs in float32 (the old GPU path enabled mixed precision without float32 heads). The Space now serves a frozen copy of the data instead of calling yfinance, and it compares every forecast with the naive baseline. With the same data and seed, the retrained test metrics match the earlier run.
Evaluation
Deployed model (lstm_returns), next-day predictions on the test split (2024-11-04 to 2026-07-31):
| metric (test) | value |
|---|---|
| rmse | 4.4831 |
| mae | 3.0255 |
| mape | 1.2368 |
Naive persistence on the same days: RMSE 4.469, MAE 3.020, MAPE 1.233 %.
Limitations
- It does not beat naive persistence on next-day prices. Do not use it for trading. Not financial advice.
- One stock, one period. It was trained on AAPL from 2015 to 2023 only. The return statistics (mean and std) are Apple's, and the model was not validated on any other ticker.
- Frozen data. Forecasts exist only for cutoffs up to 2026-07-31. Refreshing needs a retrain and a new
prices.csv. - Recursive forecasts converge to the drift. After a few steps the forecast is close to a smooth exponential trend at about 0.08 % a day. It has no notion of earnings, news or volatility regimes.
- Close only. Prices are split-adjusted but not dividend-adjusted, and there are no uncertainty bands.
- Educational portfolio project.
- Downloads last month
- 11
Space using shalev396/stock-lstm-forecast 1
Evaluation results
- rmse on AAPL daily closes (yfinance)test set self-reported4.483
- mae on AAPL daily closes (yfinance)test set self-reported3.026
- mape on AAPL daily closes (yfinance)test set self-reported1.237



