scalecast: A Forecaster Object That Wraps Dozens of Time Series Models
The practitioner's forecasting library
At a glance
- What is it?
- scalecast is a Python library that puts scikit-learn, statsmodels and TensorFlow models behind one Forecaster interface, then adds transformation pipelines, grid search and backtesting on top. The design pays off for exploratory work on messy series and gets awkward once you need streaming inference or a maintained release cadence.
- Who is it for?
- Adopt scalecast if you are doing exploratory or research forecasting on a handful of series and want LSTM, Prophet, auto-ARIMA and gradient boosting compared under one API with a shared validation split. Do not adopt it if you need a supported release train, streaming or online updates, or a library whose changelog you can track against a version pin.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 38 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The Problem scalecast Targets: Model Comparison Without Glue Code
Fitting an LSTM, a Prophet model and an ARIMA model on the same series normally means three different data preparation paths, three different output shapes, and a hand-written loop to compare them on one validation window. The README frames scalecast around exactly that friction. It lists use cases where series need dynamic transformations to extract signals, series have missing values, and where you need proof-of-concept experimentation with model validation. Academic research and internal production or sandbox environments are named as well.
The audience is therefore the analyst who already knows which model families they want to try and does not want to spend the day reconciling APIs. The Forecaster object is the unit of work. You hand it an array of values, an array of dates, a forecast horizon, an optional test length, a flag for conformal confidence intervals, and a list of metrics. Everything downstream (predictions, derived metrics, plots) stays attached to that object rather than being scattered across return values. The README states that data storage and processing become easy because applicable data, predictions and many derived metrics are contained in a few objects.
That is a real convenience, but it is a convenience for a specific shape of work: batch fitting, comparison, and reporting. It is not a serving framework, and nothing in the material suggests otherwise.
Inside the Forecaster Object: Estimators, Signals and Stacking
The mechanism is an estimator slot on the Forecaster. You call set_estimator with a name, then call manual_forecast with keyword arguments that the underlying library understands. The README shows this twice: once for lstm with lags, batch_size, epochs, validation_split, activation, optimizer, learning_rate, lstm_layer_sizes and dropout, and once for prophet with no arguments at all. The same pattern covers the scikit-learn models named in the tuning example: ridge, lasso, xgboost, lightgbm and knn.
Stacking is built on the same idea. After forecasting with lstm, prophet and auto_arima, the example calls add_signals(['lstm','prophet','arima']) and then fits catboost. Those model outputs become input features for the next model. The README notes there are two stacking routes: scikit-learn's StackingRegressor or scalecast's own procedure. That choice matters, because the two approaches will not produce identical leakage behaviour during cross-validation, and the documentation does not spell out which one is safer for a given split strategy.
auto_arima is imported from scalecast.auxmodels and takes the Forecaster as its only visible argument. It is a convenience wrapper, not a new estimator. If you already have a pmdarima setup you like, this adds little beyond consistency with the rest of the object.
The MVForecaster extends the pattern to several series at once. It is constructed from N Forecaster objects, and the README's example passes three. MVPipeline then takes lists of transformers and reverters, one per series, so each series can be transformed differently before the joint model is fit. That is a more honest design than forcing a single transformation across correlated series, and it is the part of the library that looks least like a thin wrapper.
Auto Feature Selection, Grid Search and What the Validation Actually Does
auto_Xvar_select iterates through combinations of covariates. In the README example it is called with estimator='lasso', alpha=.2, monitor='ValidationMetricValue', cross_validate=True and cvkwargs={'k':3}. The monitor argument is the interesting one: the selection loop is driven by whatever metric name you pass, evaluated on the validation set. That means the feature set chosen depends on your metric choice, and switching from rmse to mape can change which covariates survive. The documentation does not describe a stability check across folds, so a covariate that appears in one run may not appear in the next.
Tuning runs through GridGenerator and tune_test_forecast. GridGenerator.get_example_grids() populates grids for the named models; GridGenerator.get_mv_grids() does the same for the multivariate case. tune_test_forecast takes a list of model names plus limit_grid_size, feature_importance, cross_validate, rolling and k. The README annotates limit_grid_size=.2 as a randomized grid search over 20 percent of the original grid sizes, and rolling=True as rolling time series cross validation. When cross_validate is False, the library instead uses a separate validation set that you specify.
Two details are worth flagging. First, feature_importance saves permutation feature importance per model, which is one of the few interpretability hooks visible in the material. Second, the example grids are examples. Nothing in the README claims they are tuned defaults, and a randomized search over 20 percent of an example grid is a starting point rather than a search you should trust for a production series.
Pipelines, Transformations and Backtesting in Practice
The Pipeline class composes three step types: Transformer, Reverter and the forecasting function itself. The README's example builds the transformer and reverter pair with find_optimal_transformation(f), then assembles steps as ('Transform',transformer), ('Forecast',forecaster), ('Revert',reverter). Calling pipeline.fit_predict(f) returns a fitted Forecaster, and pipeline.backtest(f) returns results that backtest_metrics turns into a metrics object.
The Reverter step is the part that is easy to underestimate. A transformation applied to the target has to be undone before the forecast is meaningful, and the pipeline forces you to declare that reversal as an explicit step rather than leaving it as a line of code somewhere after the model call. For series that need differencing, scaling or a variance-stabilising transform, that structure is the difference between a reproducible result and a notebook you cannot rerun.
The MVForecaster has a parallel MVPipeline with the same three step names, except Transform and Revert take lists. The README shows the multivariate version returning f1, f2, f3 from fit_predict and passing the same three objects to backtest.
find_optimal_transformation is described in the README only as one of several ways to select transformations for a series. The selection criterion is not stated in the material, so treat the chosen transformation as a suggestion to inspect rather than an answer. If your series has a known structural break, an automated selection routine has no way to know about it.
Getting It Running: The Calls You Actually Type
The README gives the import paths directly, so there is no ambiguity about module layout. The main object comes from scalecast.Forecaster import Forecaster. The multivariate one comes from scalecast.MVForecaster import MVForecaster. Pipelines come from scalecast.Pipeline import Transformer, Reverter, Pipeline, and the multivariate pipeline is scalecast.Pipeline import MVPipeline. GridGenerator is imported as from scalecast import GridGenerator. Utility functions live in scalecast.util: find_optimal_transformation and backtest_metrics. auto_arima lives in scalecast.auxmodels.
Construction is a single call with named arguments: y, current_dates, future_dates, test_length, cis and metrics. The README comments test_length as whether you want to test all models and on how many or what percent of observations, and cis as whether to evaluate conformal confidence intervals for all models. metrics is a list, shown as ['rmse','mape','mae','r2'].
From there the sequence in the README is set_estimator, manual_forecast, and optionally add_signals before fitting a final model. For batch comparison it is GridGenerator.get_example_grids() followed by tune_test_forecast with the model list. Plotting uses matplotlib directly: f.plot_test_set(models=models, order_by='TestSetRMSE', ax=ax[0]) and f.plot(models=models, order_by='TestSetRMSE', ax=ax[1]).
What the README does not give is a pip install line, a minimum Python version, or pinned dependency ranges. The topics list names tensorflow, scikit-learn and statsmodels as the model sources, so those are implied dependencies, but you should check the package metadata before assuming a version floor. The README also does not state which Python versions are supported.
Where scalecast Is the Wrong Tool
The release history is the first constraint. The three releases listed are 0.1.2, 0.1.3 and 0.1.4, all dated 2021-07-01, within roughly an hour of each other. The last push to the default branch is dated 2026-08-09, which is years after those tags. So the version numbers on the repository do not describe the state of the code you would get from the main branch. If your team pins versions and expects a changelog entry per behaviour change, that gap is a problem you have to resolve before adoption, not after.
Second, the abstraction is batch-oriented. A Forecaster holds a fixed array of dates and a fixed forecast horizon. Nothing in the material describes incremental updates, online learning, or fitting on a stream. If your requirement is to re-forecast every few minutes as new observations arrive, the object model works against you.
Third, the library is a coordination layer, not a modelling engine. When tune_test_forecast returns a poor result, the cause is usually in the underlying estimator or in the transformation, and debugging means going through scalecast's call into that library. The convenience of a uniform API is paid for at exactly that moment.
Fourth, the LSTM path pulls in TensorFlow. That is a heavy dependency for a library whose other models are scikit-learn and statsmodels based, and the README's LSTM example configures three stacked layers of size 100 with 36 lags. On a small series that configuration is more likely to overfit than to help, and nothing in the material suggests scalecast guards against that.
How It Differs From darts, sktime and Plain statsmodels
The closest comparison in this space is darts, which also wraps multiple forecasting model families behind a unified object. The difference visible in the material is where the abstraction sits. darts centres on a ForecastingModel class with fit and predict, and its pipelines are composed from model objects. scalecast centres on a stateful Forecaster that accumulates every model you have run against it, which is why add_signals can pull earlier model outputs back in as features for a later model without you storing them yourself. That accumulator design is what makes the stacking example three lines long.
Against sktime, the difference is the estimator protocol. sktime follows the scikit-learn fit and predict convention and composes transformers through a pipeline that mirrors scikit-learn's. scalecast's Transformer, Reverter and Pipeline are its own classes with its own step-name convention, so a scalecast pipeline is not interchangeable with a scikit-learn one. You are adopting a second composition model alongside the one you already use for preprocessing.
Against using statsmodels and scikit-learn directly, scalecast's contribution is the validation and reporting layer: a shared test set, a shared metric list, and plot methods that order models by TestSetRMSE. If your workflow already has a comparison harness you trust, that layer is duplicate work. If it does not, it is the part you would otherwise write badly.
Licence, Maintenance Cost and What to Verify Before You Commit
The licence is MIT, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is a permissive licence, and it means you can vendor the library into an internal package if you need to. It also means there is no patent grant clause and no warranty, so the usual caveat applies: MIT says nothing about whether the code is correct. This is a description of the licence text, not legal advice, and you should have your own counsel review anything you redistribute.
The maintenance cost is harder to estimate from the material alone. The tagged releases are all from July 2021, while the default branch was pushed in August 2026. That combination suggests active development without a matching release cadence, which has a concrete consequence: installing from PyPI and installing from git will give you different code, and only one of them matches the documentation you are reading. The README's change log link points at Read the Docs rather than at a file in the repository, so the changelog lives outside the source tree.
The dependency surface adds to the cost. TensorFlow for the LSTM path, scikit-learn and statsmodels for the classical models, matplotlib for plotting, and whatever xgboost, lightgbm and catboost require for the boosting examples. Upgrading any one of those can break the estimator slot it feeds, and there is no compatibility matrix in the material.
What to verify first, in order: the actual latest release tag and its date; whether the README examples run against that tag rather than against main; the Python version floor from the package metadata; and whether find_optimal_transformation returns a transformation you would defend to a reviewer on one of your own series. If the first three check out and the fourth does, the library is worth a proof-of-concept. If the release tag is still 0.1.4, plan to pin a git commit rather than a version number.
Editorial conclusion
Adopt scalecast if you are doing exploratory or research forecasting on a handful of series and want LSTM, Prophet, auto-ARIMA and gradient boosting compared under one API with a shared validation split. Do not adopt it if you need a supported release train, streaming or online updates, or a library whose changelog you can track against a version pin. Before committing, check the Read the Docs change log against the version you intend to install, confirm the current release tag rather than the 0.1.x tags listed on the repository, and run the Pipeline example end to end on one of your own series to see whether find_optimal_transformation picks a transformation you would accept.
Community notes