Engineered features
The full catalog of derived columns — lags, rolling statistics, differences, event and calendar features.
An engineered feature is a derived column: a transformation of one source field (or the row timestamp), declared on the segment and computed identically at training and inference time. They give the model context a single raw value can't carry — where the series has been (lags), how it's been behaving (rolling statistics), when it last moved (event features), and what time it is (calendar features).
Declaring one
Each entry in engineered_features names the output column, the
technique, and the source field:
{
"engineered_features": [
{
"name": "sales_ma7",
"technique": "moving_average_7",
"techniqueName": "Moving Average (7)",
"sourceField": "daily_sales",
"category": "Moving Averages"
},
{
"name": "dow",
"technique": "day_of_week",
"techniqueName": "Day of Week",
"sourceField": "ts",
"category": "Time Features"
}
]
}| Field | What it is |
|---|---|
name | The output column name — this is what the model sees. |
technique | A technique ID from the catalog below. |
sourceField | The field to derive from; calendar features use the timestamp (ts). |
techniqueName, category | Display metadata (shown in the app and in field_info). |
parameters | Optional per-technique parameters (e.g. {"window": 14}, {"period_seconds": 604800}). |
Engineered features are computed after gap-filling, so windows and
lags run over the normalized grid — one value per row, no gaps. Every
technique must come from the catalog; unknown techniques are rejected
with 422 at create/edit time.
Windows and periods
Most rolling techniques encode their window in the ID (rolling_std_7 =
7 rows). The window counts grid rows, not wall-clock time: on a daily
segment moving_average_7 is a 7-day average; on an hourly segment it's
7 hours. Window families accept any window size — the catalog lists the
common presets, and you can request e.g. rolling_std_30 or pass
parameters.window explicitly.
The catalog
Lag features
The value N rows back. The single most useful family — a label's own recent history is usually its strongest predictor.
| Technique | Description |
|---|---|
lag_1 … lag_7 | Previous value, 1–7 steps back (any lag_<n> works) |
Moving averages
| Technique | Description |
|---|---|
moving_average_3, moving_average_7, moving_average_14, moving_average_30 | N-period simple moving average |
Rolling statistics
| Technique | Description |
|---|---|
rolling_std_7 | Rolling standard deviation |
rolling_min_7 / rolling_max_7 | Rolling extremes |
rolling_median_7 | Rolling median |
rolling_sum_7 | Rolling sum |
rolling_kurtosis_<w> | Rolling kurtosis — tail behavior for heavy-tailed series |
Differences and changes
| Technique | Description |
|---|---|
lag_diff_1 | Difference from the previous row |
lag_pct_change | Percentage change from the previous row |
momentum_3 | N-period momentum |
rate_of_change_5 | N-period rate of change |
Volatility and bands
| Technique | Description |
|---|---|
rolling_zscore_7 | Rolling z-score — how unusual is the current value |
rolling_volatility_14 | Rolling volatility (stddev of percentage changes) |
rolling_vol_diff_<w> | Volatility of one-row differences — use instead of rolling_volatility_* for series that cross zero |
bollinger_upper_20 / bollinger_lower_20 | SMA ± 2 standard deviations |
rolling_bandwidth_20 | Band width relative to the moving average |
cumulative_return | Cumulative return since the window start |
Event features
Sparse, slow-moving signals (monthly indicators, rarely-changing flags) carry most of their information at the moment the value changes. These turn that moment into a dense column the model can learn from on every row:
| Technique | Description |
|---|---|
release_flag | 1 on the row where the source value changed (or first appeared), 0 otherwise |
release_surprise | The signed delta from the previous value — pair with rolling_zscore_7 for a normalized surprise |
time_since_release | Seconds since the value last changed; resets to 0 on each change |
time_since_spike | Seconds since the source last moved more than parameters.threshold in one row |
staleness | Seconds since the source's last raw observation — true data age, independent of gap-filling |
Calendar features
Computed from the row timestamp (in the segment's time_zone) — no
source value involved:
| Technique | Description |
|---|---|
hour_of_day, day_of_week, day_of_month, day_of_year | Position within the day/week/month/year |
week_of_year, month_of_year, quarter_of_year, year | Coarser calendar position |
is_weekend, is_business_day | Weekend / weekday flags |
is_month_start, is_month_end, is_quarter_start, is_quarter_end, is_year_start, is_year_end | Boundary flags |
cyclic_sin / cyclic_cos | Smooth phase encoding of an arbitrary period (parameters.period_seconds) — better than raw calendar integers for strong seasonality |
Seeing them in action
Engineered features show up like any other column — in
preview output, in field_info, and in the
table the model trains on:
{
"data": {
"data": {
"timestamp": ["2026-07-13T00:00:00.000000Z", "2026-07-14T00:00:00.000000Z"],
"daily_sales": [12100.0, 12841.5],
"sales_ma7": [11980.2, 12111.5],
"dow": [0, 1]
},
…
}
}Goal discovery generates engineered features automatically from measured data — inspect a goal's segment to see which transforms it chose. For hand-built segments, start with a lag or two and a moving average of the label; add event features for sparse drivers.

