The Forecasting Company Logo
Cookbook

Backtest on past dates

One cutoff gives you one forecast. A list of cutoffs re-runs the same forecast from several points in the past, so you can compare predictions to what actually happened:

JSONCode
{ "input": { "source": { "inline": { "date": ["2024-01-01", "2024-01-02", "2024-01-03"], "sales": [142, 137, 145] } }, "columns": { "date": { "kind": "time", "frequency": "1d" }, "sales": { "kind": "target" } } }, "context": "90d", "prediction_length": "28d", "cutoff": [{ "every": "2w" }], "compute_metrics": true, "quantiles": [0.1, 0.5, 0.9] }

Post it to https://api.retrocast.com/v1-beta/forecast?model=t0-alpha. model names the forecasting model and is required — without it the request is rejected with missing field "model".

What each part does:

  • "cutoff": [{ "every": "2w" }] runs the forecast again every two weeks. At each cutoff the model sees context of history and predicts prediction_length ahead.
  • "compute_metrics": true scores every cutoff where the actual values are already in your data.

If you leave cutoff out, it defaults to ["latest"]: a single forecast starting at the most recent date in your data. That's what you want for a normal forward-looking forecast.

What comes back

One row per (cutoff, step). The cutoff column tells you which origin each prediction came from, so a backtest and a plain forecast have the same shape.

How many cutoffs you get depends on how much history you send: the three days above are enough to read, but they only fit one. The response below is the same request over 200 days of daily sales, which fits 15 — 420 rows, trimmed here to the first two:

JSONCode
{ "data": { "time": ["2024-01-05T00:00:00Z", "2024-01-06T00:00:00Z"], "cutoff": ["2024-01-04T00:00:00Z", "2024-01-04T00:00:00Z"], "lead_time": ["PT0S", "P1D"], "span": ["P1D", "P1D"], "identifiers": {}, "targets": { "sales": { "quantiles": { "0.1": [136.9, 136.8], "0.5": [139.9, 139.5], "0.9": [147.7, 147.2], "mean": [140.8, 140.5] } } } }, "metrics": [ { "identifiers": {}, "cutoff": "2024-01-04T00:00:00Z", "targets": { "sales": { "crps": 1.623, "scaled_crps": 0.0116, "mae": 2.522, "mape": 0.0180, "wape": 0.0180, "scaled_bias": -0.00036 } } } ] }

identifiers is empty here because this request declares no identifier columns — one series, so there is nothing to name it by.

metrics appears only when you set compute_metrics: true. There is one entry per series and cutoff, trimmed above to the first: each scores the forecast made from its cutoff against the actual values in its forecast window. A cutoff is scored when that window lies inside your data and holds at least one actual value. Cutoffs that run past the end of your data are still predicted, they just aren't scored.

mean is in the response even though the request asked for three quantiles: some of the scores are computed on it, so compute_metrics forecasts it too.

A metric is null where it doesn't apply — mape when every actual in the window is zero, for instance. mape and wape are fractions, not percentages.

Last modified on