Create an autoregressive forecasting model and publish it to Huggingface
Build and backtest an autoregressive SE3 consumption forecaster, then deploy it to Rebase and publish the model version to Hugging Face.
This example starts with model development. It fetches daily SE3 consumption from the public eSett Open Data API, builds a simple autoregressive model, and compares it against a persistence baseline. Only after the local backtest looks reasonable do we deploy the model and publish the resulting Rebase model version to Hugging Face.
The target variable is SE3 total consumption in MWh from eSett's consumption endpoint. eSett exposes consumption by Metering Balance Area (MBA); SE3's MBA code is 10Y1001A1001A46L.
1. Install
Create a local Python environment:
uv venv .venv
source .venv/bin/activate
uv pip install "rebase-toolkit[huggingface]" numpy pandas requests scikit-learnConfigure Rebase now if you plan to deploy later:
rebase setupHugging Face authentication is not needed while developing the model. You only need it when you publish:
rebase connect huggingface2. Fetch SE3 Consumption
Create se3_ar_forecast.py:
from __future__ import annotations
import math
from typing import Any
import numpy as np
import pandas as pd
import requests
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_absolute_error, mean_squared_error
import rebase
ESETT_API = "https://api.opendata.esett.com"
SE3_MBA = "10Y1001A1001A46L"
DEFAULT_START = "2025-01-01"
DEFAULT_END = "2026-05-31"
DEFAULT_LAGS = [1, 2, 3, 7, 14, 28]
def fetch_se3_consumption(start: str = DEFAULT_START, end: str = DEFAULT_END) -> pd.DataFrame:
response = requests.get(
f"{ESETT_API}/EXP15/Aggregate",
params={
"start": f"{start}T00:00:00.000Z",
"end": f"{end}T00:00:00.000Z",
"mba": SE3_MBA,
"resolution": "Day",
},
headers={"Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
rows = response.json()
frame = pd.DataFrame(rows)
if frame.empty:
raise ValueError("eSett returned no SE3 consumption rows")
frame["timestamp"] = pd.to_datetime(frame["timestampUTC"], utc=True)
frame["consumption_mwh"] = -frame["total"].astype(float)
return frame[["timestamp", "consumption_mwh"]].sort_values("timestamp").reset_index(drop=True)The eSett API returns consumption values as negative MWh values. The example flips the sign so the model sees positive consumption.
3. Build Autoregressive Features
Append feature-building helpers to se3_ar_forecast.py:
def _feature_row(history: list[float], timestamp: pd.Timestamp, lags: list[int]) -> dict[str, float]:
day = float(timestamp.dayofweek)
row = {
"dow_sin": math.sin(2.0 * math.pi * day / 7.0),
"dow_cos": math.cos(2.0 * math.pi * day / 7.0),
"trend": float(len(history)),
}
for lag in lags:
row[f"lag_{lag}"] = float(history[-lag])
return row
def make_training_matrix(
frame: pd.DataFrame,
lags: list[int] | None = None,
) -> tuple[pd.DataFrame, pd.Series]:
resolved_lags = lags or DEFAULT_LAGS
max_lag = max(resolved_lags)
values = frame["consumption_mwh"].astype(float).tolist()
timestamps = frame["timestamp"].tolist()
rows: list[dict[str, float]] = []
targets: list[float] = []
for index in range(max_lag, len(values)):
rows.append(_feature_row(values[:index], timestamps[index], resolved_lags))
targets.append(values[index])
return pd.DataFrame(rows), pd.Series(targets, name="consumption_mwh")
def fit_autoregressive_model(frame: pd.DataFrame, lags: list[int] | None = None) -> Ridge:
features, target = make_training_matrix(frame, lags=lags)
return Ridge(alpha=1.0).fit(features, target)This is intentionally modest: lagged consumption explains most of the daily baseline, while day-of-week features capture a simple weekly shape.
4. Backtest Against Persistence
Before deploying anything, compare the autoregressive model to a persistence baseline. Persistence predicts that tomorrow will equal today.
Add the backtest:
def backtest(
frame: pd.DataFrame,
holdout_days: int = 60,
lags: list[int] | None = None,
) -> dict[str, Any]:
resolved_lags = lags or DEFAULT_LAGS
max_lag = max(resolved_lags)
if len(frame) <= holdout_days + max_lag:
raise ValueError("not enough data for the requested backtest")
train = frame.iloc[:-holdout_days].reset_index(drop=True)
test = frame.iloc[-holdout_days:].reset_index(drop=True)
model = fit_autoregressive_model(train, lags=resolved_lags)
history = train["consumption_mwh"].astype(float).tolist()
ar_predictions: list[float] = []
persistence_predictions: list[float] = []
for _, row in test.iterrows():
features = pd.DataFrame([_feature_row(history, row["timestamp"], resolved_lags)])
ar_prediction = float(model.predict(features)[0])
persistence_prediction = float(history[-1])
ar_predictions.append(ar_prediction)
persistence_predictions.append(persistence_prediction)
history.append(float(row["consumption_mwh"]))
actual = test["consumption_mwh"].astype(float).to_numpy()
ar_mae = mean_absolute_error(actual, ar_predictions)
persistence_mae = mean_absolute_error(actual, persistence_predictions)
return {
"holdout_days": holdout_days,
"ar_mae_mwh": float(ar_mae),
"persistence_mae_mwh": float(persistence_mae),
"ar_rmse_mwh": float(mean_squared_error(actual, ar_predictions) ** 0.5),
"persistence_rmse_mwh": float(mean_squared_error(actual, persistence_predictions) ** 0.5),
"mae_improvement_vs_persistence_pct": float(100.0 * (persistence_mae - ar_mae) / persistence_mae),
}Run a local backtest without changing the model module:
python - <<'PY'
import json
from se3_ar_forecast import backtest, fetch_se3_consumption
data = fetch_se3_consumption()
metrics = backtest(data)
print(json.dumps(metrics, indent=2))
PYAt this point you are still just developing the model. If persistence wins, change the lags, add calendar features, use a longer history window, or test a different model class before deploying.
5. Wrap It as a Rebase Model
Once the local backtest is acceptable, append a deployable predictor to the same file:
def forecast_next_days(
frame: pd.DataFrame,
horizon_days: int = 7,
lags: list[int] | None = None,
) -> list[dict[str, Any]]:
resolved_lags = lags or DEFAULT_LAGS
model = fit_autoregressive_model(frame, lags=resolved_lags)
history = frame["consumption_mwh"].astype(float).tolist()
timestamp = frame["timestamp"].iloc[-1]
forecast: list[dict[str, Any]] = []
for _ in range(horizon_days):
timestamp = timestamp + pd.Timedelta(days=1)
features = pd.DataFrame([_feature_row(history, timestamp, resolved_lags)])
value = float(model.predict(features)[0])
history.append(value)
forecast.append(
{
"timestamp": timestamp.isoformat(),
"consumption_mwh": value,
}
)
return forecast
class SE3AutoregressiveConsumption(rebase.Predictor):
project = "forecasting"
name = "se3-autoregressive-consumption"
description = "Daily SE3 consumption forecast using eSett Open Data and autoregressive lags."
dependencies = [
"numpy",
"pandas",
"requests",
"scikit-learn",
]
def predict(
self,
start: str = DEFAULT_START,
end: str = DEFAULT_END,
horizon_days: int = 7,
holdout_days: int = 60,
) -> dict[str, Any]:
frame = fetch_se3_consumption(start=start, end=end)
return {
"target": "SE3 daily total consumption",
"unit": "MWh",
"training_start": frame["timestamp"].iloc[0].isoformat(),
"training_end": frame["timestamp"].iloc[-1].isoformat(),
"backtest": backtest(frame, holdout_days=holdout_days),
"forecast": forecast_next_days(frame, horizon_days=horizon_days),
}
model = SE3AutoregressiveConsumption()Run it locally:
python - <<'PY'
from se3_ar_forecast import model
result = model.predict(horizon_days=3)
print(result["backtest"])
print(result["forecast"])
PY6. Deploy to Rebase
Deploy the model to dev without involving Hugging Face yet:
rebase deploy se3_ar_forecast.py --name modelrebase deploy creates or updates the Rebase model and deploys the current version to dev by default. Inspect the model version and environment pointer:
rebase model versions se3-autoregressive-consumption --project forecasting
rebase model deployments se3-autoregressive-consumption --project forecastingValidate the deployed model:
rebase model run se3-autoregressive-consumption \
--project forecasting \
--env dev \
--param horizon_days=37. Publish to Hugging Face
Publishing is a separate step after the model and backtest flow are working. First, create a Hub README as README.hf.md:
---
library_name: scikit-learn
tags:
- forecasting
- time-series
- electricity-consumption
- se3
- esett
---
# SE3 Autoregressive Consumption Forecast
Daily SE3 total consumption forecast trained on eSett Open Data.
The model uses lagged daily consumption, day-of-week features, and a linear Ridge regressor. The accompanying `backtest.json` compares the autoregressive forecast against a persistence baseline.
The Rebase SDK also publishes `rebase_model.json` with model-version provenance, including the Rebase model version and the Hugging Face commit recorded for this publication.Then create a small artifact folder with the data window, backtest summary, and Hub README:
import json
from pathlib import Path
from se3_ar_forecast import DEFAULT_END, DEFAULT_START, backtest, fetch_se3_consumption, model
import rebase
HUGGINGFACE_README = Path("README.hf.md")
def write_huggingface_artifacts() -> Path:
artifact_dir = Path("artifacts/se3-autoregressive-consumption")
artifact_dir.mkdir(parents=True, exist_ok=True)
frame = fetch_se3_consumption(DEFAULT_START, DEFAULT_END)
frame.to_csv(artifact_dir / "training_data.csv", index=False)
metrics = backtest(frame)
(artifact_dir / "backtest.json").write_text(json.dumps(metrics, indent=2) + "\n", encoding="utf-8")
(artifact_dir / "README.md").write_text(HUGGINGFACE_README.read_text(encoding="utf-8"), encoding="utf-8")
return artifact_dir
deployed = model.deploy(
environment="dev",
huggingface=rebase.HuggingFacePublishConfig(
repo_id="your-hf-org/se3-autoregressive-consumption",
repo_type="model",
private=False,
artifact_path=write_huggingface_artifacts(),
sync_source_git=True,
),
)
print("huggingface_publication:", deployed.data["huggingface_publication"])Save that as publish_huggingface.py and run:
python publish_huggingface.pyThe Hub repo receives the artifact folder plus Rebase provenance. Rebase records the Hub repo, revision, commit SHA, visibility, and the model version that produced the publication.
Set sync_source_git=False if this example is running outside a clean Git checkout:
rebase.HuggingFacePublishConfig(
repo_id="your-hf-org/se3-autoregressive-consumption",
artifact_path=write_huggingface_artifacts(),
sync_source_git=False,
)8. Promote to Production
Promote the same immutable model version to staging:
rebase model promote se3-autoregressive-consumption \
--project forecasting \
--from dev \
--to stagingRequest production approval:
rebase model request-promotion se3-autoregressive-consumption \
--project forecasting \
--from staging \
--to prod \
--reason "Backtest passed against persistence and Hugging Face publication is recorded"Approve the request:
rebase model approve-promotion <promotion-request-id> \
--reason "Approved for production"Promote to production:
rebase model promote se3-autoregressive-consumption \
--project forecasting \
--from staging \
--to prod \
--promotion-request-id <promotion-request-id>Run production:
rebase model run se3-autoregressive-consumption \
--project forecasting \
--env prod \
--param horizon_days=79. Inspect Lineage
List model versions and environment pointers:
rebase model versions se3-autoregressive-consumption --project forecasting
rebase model deployments se3-autoregressive-consumption --project forecastingThe Rebase model publication links the immutable model version to the Hugging Face repo and Hub commit. The Hub repo contains rebase_model.json, training_data.csv, and backtest.json.

