Skip to content

Connect any forecasting model

Stockcast works with any forecasting library: statistical models, machine learning, deep learning, foundation models, or your own code. The connection is always the same three steps:

  1. Forecast the next \(H\) periods from data up to the decision.
  2. Summarise the forecast into one target per SKU for the whole window.
  3. Fit a policy with that target and its dates.

This page shows the recipe with smooth, which the notebooks use, and with a generic model that produces sample paths.

What Stockcast needs from your model

For each SKU and decision, one of:

Your model gives Turn it into a target by
sample paths (simulations, bootstrap, probabilistic networks) summing each path over the window, then taking the quantile
a quantile of the cumulative forecast using it directly
per-period means and standard deviations the independent-normal mode, or your own covariance formula

and three dates: the forecast origin (last observed demand), the frequency, and the end date \(= \text{origin} + H\Delta\). Forecast targets explains the theory.

With smooth

smooth can forecast the cumulative demand over a horizon directly. For a daily series history ending at origin:

from smooth import ES

H = lead_time + review_period
model = ES(model="ANN")
model.fit(history["y"])
total = model.predict(h=H, interval="prediction", level=0.95,
                      side="upper", cumulative=True)

target = pd.DataFrame({
    "unique_id": [sku],
    "target": [float(total.upper.iloc[-1, 0])],
    "target_end_date": [origin + pd.Timedelta(days=H)],
})

Then fit the policy on it as in Walkthrough step 4, with forecast_origin=origin. Notebooks 04, 04c, 04e, and 04f run this end to end.

With any model that samples paths

Many libraries can draw future sample paths. The helper below turns a (n_paths, H) array per SKU into a target table; plug in whatever produces the paths:

import numpy as np
import pandas as pd


def target_from_paths(paths_by_sku, *, probability, origin, horizon, freq="D"):
    """One cumulative target per SKU from arrays of shape (n_paths, horizon)."""
    offset = pd.tseries.frequencies.to_offset(freq)
    for sku, p in paths_by_sku.items():
        if p.ndim != 2 or p.shape[1] < horizon:
            raise ValueError(f"{sku}: paths need shape (n_paths, {horizon}), got {p.shape}")
    return pd.DataFrame({
        "unique_id": list(paths_by_sku),
        "target": [float(np.quantile(p[:, :horizon].sum(axis=1), probability))
                   for p in paths_by_sku.values()],
        "target_end_date": origin + horizon * offset,
    })


# Stand-in for your model: 5,000 simulated futures for two SKUs.
rng = np.random.default_rng(1)
paths = {
    "tea_250g": rng.poisson(6.0, size=(5_000, 6)),
    "coffee_1kg": rng.negative_binomial(3, 0.4, size=(5_000, 6)),
}
origin = pd.Timestamp("2026-01-05")
targets = target_from_paths(paths, probability=0.95, origin=origin, horizon=6)
targets
    unique_id  target target_end_date
0    tea_250g    46.0      2026-01-11
1  coffee_1kg    41.0      2026-01-11
from stockcast.policies import OrderUpToPolicy

policy = OrderUpToPolicy(lead_time=2, review_period=4, freq="D", service_level=0.95,
                         allow_backorders=False, date_column="target_end_date").fit(
    targets, target_column="target",
    forecast_origin=origin,
)
policy.get_target_levels()
    unique_id  target_level
0    tea_250g          46.0
1  coffee_1kg          41.0

Notes

  • Long format fits. Most forecasting libraries (Nixtla's, sktime's, skforecast's multi-series tools) use the same long unique_id / date / value layout as Stockcast, so reshaping is usually a groupby.
  • Forecast the window you need. Ask your model for exactly \(H\) steps from the decision's origin. For irregular schedules, \(H\) differs per decision.
  • Forecast demand. A target protects against demand, so fit the model on demand. Sales are demand on days that had stock left; on days that ran out they are a floor. Sales and demand shows how to forecast from such a history.
  • Use the correct origin. Fit on data up to the origin only. For a decision at demand period \(t\), the origin is the date of period \(t - 1\) (the opening date for \(t = 0\)). Stockcast checks the dates you declare.
  • Quantile forecasts per step are not enough on their own. They are marginal: combine them through paths or a cumulative forecast, not by adding them (why).
  • Record how the target was made. Keep the model, its settings, and the seed next to the target table. The run manifest holds the target's metadata and a fingerprint that identifies the table, not a copy of it, so keep the table too.