# 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": {},
      "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.
