Prequential (test-then-update) streaming evaluation for stateful time-series forecasters, with covariate-arrival simulation, streaming MASE/WQL/SQL/coverage, compute-cost accounting, and adaptive conformal calibration.
Project description
preqts
Prequential (test-then-update) streaming evaluation for time-series forecasters.
Real forecasting is streaming: observations arrive over time, covariates interact, some are known into the future, and a model must update its prediction at each step. Public benchmarks evaluate the batch case: at each cutoff they re-forecast an independent window (fev, GIFT-Eval, GluonTS/Darts rolling backtests). None of them evaluate a stateful forecaster that carries hidden state and updates at constant cost, which is exactly the regime of the new recurrent and state-space foundation models (TiRex-2 / xLSTM, FlowState / SSM). When the TiRex-2 authors needed to measure streaming behaviour, they had to hand-roll a bespoke protocol because nothing standard existed.
preqts is that missing harness: it drives any forecaster through a test-then-update loop, scores
stateful and batch models identically on one stream, simulates realistic covariate arrival, and
measures the per-step compute cost so the streaming-cost argument is verified, not asserted.
Part of the ChronoScope time-series method atlas (extracted as a standalone, PyPI-destined library per CAOS ADR-0061). Core depends on numpy only (Pyodide-safe); model backends are optional extras.
Install
pip install preqts # core (numpy only)
pip install "preqts[statsforecast]" # + a batch backend to wrap
pip install "preqts[dev]" # + pytest, ruff
Quickstart
import numpy as np
from preqts import SeasonalNaive, Stream, run_prequential
rng = np.random.default_rng(0)
y = 50 + 10 * np.sin(np.arange(600) * 2 * np.pi / 12) + rng.normal(0, 2, 600)
stream = Stream(y, seasonality=12, name="demo")
result = run_prequential(SeasonalNaive(12), stream, horizon=1, quantile_levels=[0.1, 0.5, 0.9])
print(result.summary()) # MASE, WQL, SQL, coverage, total_compute_s, final_predict_ms
See examples/quickstart.py for the stateful-vs-batch cost comparison.
Core concepts
| Object | Role |
|---|---|
StatefulForecaster |
The contract every evaluated model implements: start / predict / ingest. |
Stream + Covariate |
A target series plus covariates with arrival policies: aligned past, lagged (late-arriving) past, known-future; NaN for missing. |
SeasonalNaive |
A real pure-numpy reference forecaster with empirical, horizon-widening quantiles. |
ReplayAdapter |
Wraps any batch forecaster into the streaming protocol by replaying accumulated history (makes batch and stateful comparable, and exposes batch cost growth). |
run_prequential |
The test-then-update loop; returns a PrequentialResult. |
AdaptiveConformal |
Online (ACI) recalibration of a forecaster's outer interval to track nominal coverage. |
Metrics
mase, weighted_quantile_loss (WQL), scaled_quantile_loss (SQL, the probabilistic analogue of
MASE used by fev-bench), empirical_coverage, and pinball_loss. MASE and SQL are scaled by the
in-sample seasonal-naive MAE fixed from the warmup window, so every cutoff and every model share one
denominator.
Wrapping a real batch model
Any callable batch_fn(context, horizon, past_cov, future_cov, quantile_levels) -> (horizon, Q) can
be wrapped:
from preqts import ReplayAdapter, run_prequential
def chronos_batch(context, horizon, past_cov, future_cov, levels):
# call a Chronos-2 / TimesFM / StatsForecast model on `context`, return quantile columns
...
result = run_prequential(ReplayAdapter(chronos_batch, name="chronos-2"), stream, horizon=12)
The adapter re-reads the whole history each predict, so result.predict_latency shows the batch
cost growing with the stream: the phenomenon a streaming-native model avoids.
Docs
The docs/ wiki explains the method and the design: why prequential
(docs/00_why-prequential.md), the protocol and stream model
(docs/01_protocol-and-stream.md), the metrics
(docs/02_metrics.md), and the conformal module
(docs/03_conformal.md).
Status
v0.01.000 (2026-07-03), alpha. API may change while 0.x. 32 tests, ruff-clean.
License
Apache-2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file preqts-0.3.0.tar.gz.
File metadata
- Download URL: preqts-0.3.0.tar.gz
- Upload date:
- Size: 32.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de9341668926c264b0e6fc3a3501bb295d218f218ff09ddeed48c8988cf387b1
|
|
| MD5 |
24ccde6bddc5df17c57adf8bbd5fd147
|
|
| BLAKE2b-256 |
f013ef51ada758d3953bdf374377257b8240a18585d03d1d7b25fb1308d49ab2
|
Provenance
The following attestation bundles were made for preqts-0.3.0.tar.gz:
Publisher:
publish-pypi.yml on fsantibanezleal/CAOS_PreqTS
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
preqts-0.3.0.tar.gz -
Subject digest:
de9341668926c264b0e6fc3a3501bb295d218f218ff09ddeed48c8988cf387b1 - Sigstore transparency entry: 2138285006
- Sigstore integration time:
-
Permalink:
fsantibanezleal/CAOS_PreqTS@545e7f82386d262410bb6d56d9974a0df77052a0 -
Branch / Tag:
refs/tags/v0.03.000 - Owner: https://github.com/fsantibanezleal
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@545e7f82386d262410bb6d56d9974a0df77052a0 -
Trigger Event:
release
-
Statement type:
File details
Details for the file preqts-0.3.0-py3-none-any.whl.
File metadata
- Download URL: preqts-0.3.0-py3-none-any.whl
- Upload date:
- Size: 28.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
571b191498f1ef1d1e4a183b727a53a5d4b20e743d8b6799cbf2ec4cb7d993ef
|
|
| MD5 |
2e18b2a5572d1e93f986f1374fd34183
|
|
| BLAKE2b-256 |
c852dacd2920879fd0a8350e28ba1d06887c420991321790b3151e896edfd4eb
|
Provenance
The following attestation bundles were made for preqts-0.3.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on fsantibanezleal/CAOS_PreqTS
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
preqts-0.3.0-py3-none-any.whl -
Subject digest:
571b191498f1ef1d1e4a183b727a53a5d4b20e743d8b6799cbf2ec4cb7d993ef - Sigstore transparency entry: 2138285014
- Sigstore integration time:
-
Permalink:
fsantibanezleal/CAOS_PreqTS@545e7f82386d262410bb6d56d9974a0df77052a0 -
Branch / Tag:
refs/tags/v0.03.000 - Owner: https://github.com/fsantibanezleal
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@545e7f82386d262410bb6d56d9974a0df77052a0 -
Trigger Event:
release
-
Statement type: