Quant Forecast Lab
Reproducible deep-learning research for financial time-series forecasting.
This repository began as an LSTM stock-price forecasting experiment in 2020. The current architecture keeps those historical entry points compatible while evolving the project into a more rigorous quantitative forecasting laboratory.
The objective is not to make a chart that visually tracks a price series. The objective is to measure whether a model has out-of-sample forecasting skill beyond simple baselines, whether uncertainty is calibrated, and what remains after realistic trading frictions.
Highlights
- TensorFlow LSTM models for price, return, delta and trend-residual targets.
- v7 direction + magnitude forecasting with recursive future inference.
- v8 shared multi-task LSTM architecture for direction and magnitude.
- Recorded experiment seeds and runtime metadata; exact numeric reproducibility can vary by hardware and backend.
- Training/validation/test separation for model fitting and evaluation.
- Naive last-price benchmark and naive-relative RMSE skill.
- Directional accuracy.
- Expanding-window walk-forward split primitives.
- One-step held-out residual bands, explicitly labelled as unvalidated for recursive horizons.
- Transaction-cost-aware directional strategy diagnostics.
- CSV benchmark CLI.
- CI, tests, packaging metadata and contributor templates.
Why the benchmark matters
Financial price levels are persistent. A forecast can look excellent on a chart while failing to beat:
tomorrow's price = today's price
For that reason, model error should not be presented in isolation. The maintained evaluation layer reports:
RMSE skill = 1 - RMSE(model) / RMSE(naive)
A positive value means the model beats the last-observation baseline on RMSE for the evaluated sample.
Quick start
Conda
conda env create -f environment.yml
conda activate stock-prediction
pip
python -m venv .venv
# activate the environment for your platform
python -m pip install -r requirements.txt
python -m pip install -e .
Run the stock_prediction_*.py compatibility scripts and notebooks from a source checkout. The published package installs the quant-forecast CLI and library; it does not bundle those root scripts or historical notebooks.
For development:
python -m pip install -e ".[dev]" --no-deps
python -m pip install -r requirements.txt
pytest
ruff check quant_forecast_lab tests tests_runtime
Train the compatibility model
The historical CLI remains supported:
python stock_prediction_deep_learning.py \
-ticker=GOOG \
-start_date=2017-11-01 \
-validation_date=2022-09-01 \
-epochs=150 \
-batch_size=32 \
-time_steps=30 \
-use_returns=false \
-model_version=v7 \
-forecast_horizon=1 \
-seed=42
The cleanup keeps v7 as the compatibility default so existing users do not silently receive different model semantics.
Train the shared multi-task model
v8 is an opt-in model that shares one temporal representation and predicts both move direction and magnitude:
+--> P(up)
input --> LSTM --> LSTM -|
+--> magnitude
Run it with:
python stock_prediction_deep_learning.py \
-ticker=GOOG \
-start_date=2017-11-01 \
-validation_date=2022-09-01 \
-epochs=150 \
-batch_size=32 \
-time_steps=30 \
-use_returns=false \
-model_version=v8 \
-seed=42
v7 remains the compatibility default for historical comparisons. New training runs fit scalers only on the chronological fit window, so their numerical outputs can differ from earlier runs.
Explore the v9 return model
The v9 notebook trains a shared LSTM with return, direction, and ordered quantile heads. It saves the model, scalers, input-data hash, test metrics, future forecast, and inference settings. The future forecast includes independent raw and anchored recursive paths. The model remains opt-in because the current held-out FTSE run did not beat the zero-return baseline on RMSE; see model semantics.
Benchmark saved predictions
Given an aligned CSV such as:
Date,Actual,LSTM,MultiTask
2026-01-05,100.0,100.4,100.2
2026-01-06,101.0,100.8,101.1
...
run:
quant-forecast benchmark \
--csv results.csv \
--actual-col Actual \
--prediction-col LSTM \
--prediction-col MultiTask
The output includes RMSE, MAE, directional accuracy and RMSE skill versus the previous-price naive baseline.
Financial Forecasting Arena
For a multi-asset or multi-horizon comparison, use the arena. It consumes already out-of-sample predictions rather than training models inside the evaluator, keeping model fitting and evaluation cleanly separated.
quant-forecast arena \
--csv benchmark.csv \
--actual-col Actual \
--prediction-col LSTM \
--prediction-col MultiTask \
--group-col Ticker \
--group-col Horizon \
--time-col Date \
--previous-actual-col OriginClose \
--cost-bps 5 \
--output reports/generated/arena.html
The arena produces a self-contained HTML report and a table containing:
- RMSE and MAE;
- directional accuracy;
- RMSE skill versus the previous-price naive forecast;
- annualised directional-strategy return and volatility;
- Sharpe-like diagnostic;
- maximum drawdown;
- average turnover;
- explicit transaction-cost assumption.
Published benchmark results belong under benchmarks/ and should never be generated from training observations or a hand-picked single run.
Repository architecture
.
├── quant_forecast_lab/ # maintained, testable research utilities
│ ├── arena.py
│ ├── backtest.py
│ ├── benchmark.py
│ ├── config.py
│ ├── evaluation.py
│ ├── experiment.py
│ ├── reproducibility.py
│ ├── uncertainty.py
│ └── validation.py
├── tests/
├── docs/
│ ├── ARCHITECTURE.md
│ ├── METHODOLOGY.md
│ └── assets/legacy/
├── examples/
│ └── notebooks/
├── stock_prediction_*.py # compatibility entry points
├── pyproject.toml
└── environment.yml
The migration is deliberately compatibility-first: stable research components are introduced around the historical scripts rather than breaking old commands through a big-bang move.
See Architecture and Methodology.
See also the Model catalogue for the exact semantics of v1-v9.
Evaluation protocol
The training path separates three roles:
- Training — optimise model weights.
- Validation — early stopping and learning-rate decisions.
- Test — final out-of-sample evaluation only.
The fixed date split remains available for compatibility. For research comparisons, prefer chronological expanding windows:
from quant_forecast_lab.validation import expanding_window_splits
folds = expanding_window_splits(
n_samples=2500,
min_train_size=1250,
validation_size=125,
test_size=125,
gap=1,
)
The splitter is intentionally independent of TensorFlow so the evaluation protocol can be tested without downloading market data.
Uncertainty: scenario paths vs calibrated intervals
The inference implementation can generate stochastic trajectories by perturbing the model forecast using volatility estimated from recent history. These are scenario paths, not automatically calibrated probability statements.
Where enough held-out one-step prediction residuals are available, inference reports a symmetric one-step residual band. It has no validated coverage guarantee for recursive multi-session forecasts. The underlying conformal primitive is also exposed directly for experiments whose calibration and evaluation observations satisfy its assumptions:
from quant_forecast_lab.uncertainty import symmetric_conformal_interval
lower, upper = symmetric_conformal_interval(
point_forecast,
calibration_actual,
calibration_predicted,
coverage=0.90,
)
Empirical interval coverage should still be measured on observations that were not used to set the interval radius.
Trading diagnostics
A low RMSE does not imply a profitable strategy. The research package includes an intentionally small directional diagnostic:
from quant_forecast_lab.backtest import backtest_directional_strategy
metrics = backtest_directional_strategy(
actual_returns,
predicted_returns,
transaction_cost_bps=5,
)
It reports annualised return/volatility, Sharpe, maximum drawdown and turnover. It is not an execution simulator and does not model market impact, borrow, financing or capacity.
Reproducibility
The training CLI accepts an explicit seed. The shared helper seeds Python, NumPy and TensorFlow and requests deterministic TensorFlow operations where supported.
from quant_forecast_lab.reproducibility import set_global_seed
set_global_seed(42)
Each run writes its configuration alongside the model artifacts, saves the exact close-price snapshot as market_data.csv, records its SHA-256 digest, and captures Python, TensorFlow, NumPy, Pandas and Git revision metadata when available.
GPU notes
TensorFlow 2.18.1 works on CPU. For NVIDIA GPU acceleration, use Linux or WSL2 on Windows. Native-Windows TensorFlow GPU support ended after TensorFlow 2.10, so the old native-Windows CUDA 11.2 instructions are intentionally removed.
Use the current TensorFlow installation guide for driver and CUDA requirements.
Historical material
The original notebook is retained at:
examples/notebooks/stock_prediction_lstm.ipynb
Historical screenshots are retained under:
docs/assets/legacy/
Generated ticker/date experiment directories are intentionally removed from the project root. They remain recoverable from Git history, while new runs are ignored by Git.
Forecasting a saved run
A known-good v7 FTSE reference run is retained under examples/runs/reference-v7-ftse, so the inference workflow remains executable after generated runs were removed from the repository root.
python stock_prediction_forecasting.py \
--run-folder examples/runs/reference-v7-ftse \
--ticker ^FTSE \
--calendar XLON \
--forecast-days 30
The calendar parameter is passed to exchange_calendars, so the reference forecast uses actual London Stock Exchange sessions rather than treating every weekday as tradable. For US equities use XNYS; for continuous markets an appropriate always-open calendar can be supplied.
The command reads the saved run without modifying it. Forecast CSV, metadata and plot outputs go to a fresh ignored directory under runs/forecasts/. Use --output-folder runs/my-forecast to choose a destination; it must be outside the model run folder.
Data
The compatibility scripts use yfinance as a convenient public-data source. Data quality, corporate actions, symbol history and survivorship assumptions remain the responsibility of each experiment.
Scope
This project is a forecasting research environment. Results from one asset, period or validation split should not be interpreted as evidence that a model generalises to another market regime or that a trading strategy is deployable.
Contributing
See CONTRIBUTING.md. Model contributions should include a reproducible configuration, a naive baseline and out-of-sample evidence.
License
Apache License 2.0.