MODELCATALOG

History in, future out — and the evaluation design is the hard part.

Time series forecasting

Predict future values from history.

Browse live models for this task →

What it is

Forecasting looks like regression but breaks most of regression's assumptions. Observations are ordered and correlated, the test set must come after the training set, and the thing you are predicting is often several steps ahead rather than one.

The field has recently acquired a genuinely new option. Pretrained foundation models for time series produce useful forecasts on series they have never seen, with no training at all — the same zero-shot idea that arrived in NLP a few years earlier. They do not always beat a well-tuned classical model, but they remove the cold-start problem entirely.

Model families 4

Sub-categories within this task. The distinction that matters is not which is best, but which fits your constraints.

Classical statistical

Negligible.
Use when
Your baseline, and frequently hard to beat. Strong seasonal structure, limited history, and a need to explain the forecast's components.
Avoid when
Many related series with shared behaviour, or many external drivers.
Examples
ARIMA / SARIMAETSThetaProphet

Gradient boosting on lag features

Megabytes.
Use when
You have covariates — promotions, rates, calendar effects — and want the flexibility of a general regressor. Often the pragmatic winner.
Avoid when
You need proper probabilistic intervals without extra work, or you must extrapolate a trend beyond the training range. Trees cannot.
Examples
LightGBM / XGBoost with lag and rolling features

Deep learning forecasters

1M–50M.
Use when
Hundreds or thousands of related series where a single model can learn shared patterns and pool information across them.
Avoid when
A handful of series. There is not enough data to justify it.
Examples
DeepARN-BEATSTemporal Fusion TransformerPatchTST

Time series foundation models

10M–700M.
Use when
Zero-shot forecasting with no training, or as an immediate strong baseline. Particularly useful for new series with little history.
Avoid when
You have abundant history and a well-tuned specialised model already performing well.
Examples
Chronos / Chronos-BoltTimesFMMoirai

In a bank 4

The third line in each is the part that makes it harder than the textbook version.

Liquidity and cash flow forecasting

Project net cash positions across currencies and entities to manage buffers.

The wrinkleRegulatory liquidity requirements make the tails matter more than the central forecast. You need calibrated quantiles, not a point estimate — and the model must be defensible to treasury and to supervisors.

ATM and branch cash demand

Forecast withdrawals per location to schedule replenishment.

The wrinkleThousands of related series with strong local calendar effects — paydays, holidays, local events. A classic case for a single global model across all locations rather than one model per ATM.

Deposit balance and attrition modelling

Project balances under rate scenarios for asset-liability management.

The wrinkleThis is scenario forecasting, not point forecasting. The model must respond sensibly to rate paths it has never seen, which is exactly where purely learned models are weakest and structural models earn their place.

Contact centre volume for staffing

Forecast call and chat volume by interval to schedule staff.

The wrinkleIntraday seasonality on top of weekly and annual patterns, plus shocks from outages and product events. Hierarchical reconciliation across intervals, days and sites matters as much as the base forecast.

Code 3

Named functions and the arguments that actually change the outcome. Comments flag the traps.

Backtest properly — this is the whole game

An expanding-window backtest is the only honest evaluation. A single train/test split tells you almost nothing.

import numpy as np
import pandas as pd

def backtest(series, model_fn, horizon=28, folds=6, step=28):
    """Expanding window: train on everything up to a cutoff, forecast
    the next `horizon`, walk forward, repeat."""
    results = []
    for k in range(folds):
        cut = len(series) - (folds - k) * step
        train, actual = series[:cut], series[cut:cut + horizon]
        if len(actual) < horizon:
            continue
        pred = model_fn(train, horizon)

        # MASE: scaled against a seasonal naive baseline, so a value
        # above 1 means you are worse than "same as last week".
        naive = np.mean(np.abs(np.diff(train, 7)))
        results.append({
            "fold": k,
            "MAE":  np.mean(np.abs(actual - pred)),
            "MASE": np.mean(np.abs(actual - pred)) / naive,
        })
    return pd.DataFrame(results)

# ALWAYS produce the naive baseline first. Plenty of deep learning
# forecasts lose to "same value as last week" - better to know early.
def seasonal_naive(train, h, period=7):
    return np.array([train[-period + (i % period)] for i in range(h)])

Zero-shot forecasting with Chronos

No training. Point the model at a series and it forecasts — with quantiles, which is what you need for liquidity work.

import torch
import numpy as np
from chronos import ChronosPipeline

pipe = ChronosPipeline.from_pretrained(
    "amazon/chronos-bolt-base",
    device_map="cuda", torch_dtype=torch.bfloat16,
)

context = torch.tensor(history_values, dtype=torch.float32)

# Returns SAMPLES, not a point forecast. The distribution is the point.
forecast = pipe.predict(context, prediction_length=28, num_samples=100)

q = np.quantile(forecast[0].numpy(), [0.05, 0.5, 0.95], axis=0)
low, median, high = q

# For treasury work the 5th percentile is often the number that matters,
# not the median - plan against the bad case.
print("median day 1:", median[0], " 5th pct:", low[0])

Many related series with AutoGluon

Fits classical, boosted and deep models plus foundation models, then ensembles by backtest performance. A strong baseline in one call.

from autogluon.timeseries import TimeSeriesDataFrame, TimeSeriesPredictor

# Long format: one row per (series, timestamp)
data = TimeSeriesDataFrame.from_data_frame(
    df, id_column="atm_id", timestamp_column="date"
)

predictor = TimeSeriesPredictor(
    prediction_length=28,
    target="withdrawals",
    eval_metric="MASE",
    known_covariates_names=["is_holiday", "is_payday"],  # known in advance
    quantile_levels=[0.05, 0.1, 0.5, 0.9, 0.95],
).fit(data, presets="high_quality", time_limit=1800)

print(predictor.leaderboard(data))    # see what actually won
preds = predictor.predict(data, known_covariates=future_calendar)

What catches people out 5

  1. Never shuffle, never random split

    Any cross-validation that mixes time order leaks the future. Use expanding or rolling windows exclusively. This is the single most common error in forecasting projects.

  2. Beat the naive baseline before anything else

    Seasonal naive — 'the same as this day last week' — is surprisingly strong. Report MASE so the comparison is explicit. A sophisticated model that loses to it is a finding, not a failure to hide.

  3. Know which covariates are known in the future

    Holidays and paydays are known in advance; competitor pricing is not. Using a covariate you will not have at prediction time is leakage, and it is easy to do accidentally.

  4. Forecast the distribution, not the number

    For anything feeding a buffer or a limit, the quantiles are the deliverable. A point forecast with no interval cannot support a decision about how much cash to hold.

  5. Hierarchies must reconcile

    Branch forecasts should sum to regional, and regional to national. Independent models will not, and stakeholders notice immediately. Reconciliation is a separate step you must plan for.