# Backtest on past dates

> For AI agents: the complete documentation index is at
> [https://docs.retrocast.com/llms.txt](https://docs.retrocast.com/llms.txt) — every page is also available as
> markdown by appending `.md` to its URL.

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:

```json
{
  "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:

```json
{
  "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": {},
      "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**, not per cutoff: the scores cover every cutoff whose
forecast window is fully present in your data. Cutoffs that run past the end
of your actuals 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` against an actual of zero,
for instance. `mape` and `wape` are fractions, not percentages.
