tsbootstrap: Distribution-Free Uncertainty Quantification and Conformal Prediction for Time Series

Sankalp Gilda
DeepThought Solutions
ORCID: 0000-0002-3645-4501


Abstract

Finance, sensing, and demand streams violate the exchangeability that IID conformal prediction and the IID bootstrap assume, and existing libraries implement either a general resampling engine or conformal calibration without the other. tsbootstrap provides block, residual, sieve, and wild resampling, classical bootstrap confidence intervals, and adaptive conformal calibrators (EnbPI, ACI, NexCP, AgACI) through a single typed API in which a specification object selects each method. In a controlled coverage study the IID bootstrap undercovers sharply under dependence; dependence-aware methods reduce the coverage deficit, the sieve nearest to nominal under short-memory linear dependence. On the shared fixed-statistic path a compiled backend runs several times faster than arch, and a streaming reduce avoids materializing the \(O(Bn)\) replicate tensor, limiting peak extra memory to \(O(B)\) for the statistic array. The software is MIT licensed (v0.6.1).

conformal prediction, uncertainty quantification, bootstrap, time series, distribution-free inference

Introduction and Statement of Need

Practitioners pair point forecasts with calibrated intervals, but the two distribution-free tools that would supply one assume away temporal structure: split conformal prediction assumes exchangeable calibration data, the ordinary bootstrap assumes IID observations, and both undercover on autoregressive streams. The time series bootstrap restores validity under dependence, and conformal calibration supplies the finite-sample coverage guarantee. Prior libraries supply one piece: forecasting toolkits (skforecast, darts, statsforecast, neuralforecast) attach residual-bootstrap or split-conformal intervals to their own forecasters, conformal libraries (MAPIE, TorchCP, puncc) calibrate a fitted predictor, and resampling libraries (arch) stop at classical intervals. To our knowledge, no existing library combines a general dependence-aware resampling engine (block, residual, sieve, and wild methods with typed specs, for arbitrary statistics) with an adaptive conformal layer and a streaming reduce in one API; tsbootstrap provides this combination. Derivations appear in the companion methods manuscript (arXiv:2404.15227).

Design and API

The single typed entry point bootstrap(X, *, method=spec, ...) selects the method by a concrete specification object whose type identifies it, visible to editors and type checkers. A typical analysis consists of three calls: diagnose(X) to recommend a spec, bootstrap(X, method=spec, n_bootstraps=999, random_state=0), and conf_int(res, statistic="mean").

The call returns a structured BootstrapResult: samples (the replicate array), provenance (spec, seed, backend), out-of-bag (the held-out index EnbPI calibrates on), and in-bag (the drawn index, for jackknife-after-bootstrap and studentized errors). Inputs are handled by a narwhals layer (a lightweight compatibility layer over dataframe libraries): NumPy arrays, Python lists, and frames or series from pandas, Polars, and PyArrow. Each spec carries a MethodMetadata record (metadata_for(spec)) declaring assumptions, capabilities, references, cost, and failure modes; diagnose(X) reads the lag-one autocorrelation and an ADF stationarity test and returns a frozen Diagnosis naming recommended specs with an auto-selected block length.

Results are deterministic per backend for a fixed seed: each replicate draws from its own RNG stream, so output is invariant to replicate ordering, parallelism, and thread count. The default numpy backend spawns per-replicate PCG64 streams via SeedSequence and is bit-for-bit reproducible for a pinned seed and environment. The optional compiled numba backend ([accel] extra), the source of the Section [sec:sec:perf] throughput, draws from a distinct counter-based stream, equal in distribution but not bit-identical to the numpy path; its byte stream is pinned within a release, not across releases.

Methods and UQ Coverage

Block methods cover moving [1], circular [2], stationary [3], non-overlapping [4], and tapered blocks with energy-normalized windows [5], with block length defaulting to the automatic spectral-density plug-in rule of [6], with the correction of [7]. Model-based bootstraps (ResidualBootstrap over AR, ARIMA, and VAR) regenerate series recursively from the fitted dynamics and resampled centered innovations [8], rather than the less accurate fitted-plus-residuals scheme; SieveAR implements the sieve bootstrap [9], [10], with exogenous support (ARX, VARX, ARIMAX). Innovations may be resampled IID (default), wild (Rademacher by default) [11][14], or block-wild for dependent innovations [15].

Classical intervals via conf_int cover percentile [16], basic, studentized bootstrap-t with a dependence-aware block-jackknife standard error [1], and BCa [17], the last gated to the IID spec because the studentized route is the second-order-correct choice under dependence [18]. The uq API adds conformal and adaptive intervals: EnbPI out-of-bag intervals for sklearn-style regressors [19], AR forecast bands, and static, sliding-window, ACI [20], NexCP [21], and AgACI [22] calibrators for stationary, volatility-clustered, and drifting regimes. Table 1 reports the empirical coverage these methods achieve.

Coverage under dependence. The study crosses five DGPs, 5,000 Monte Carlo datasets each, eight methods, and four functionals: 800,000 percentile intervals from \(B{=}999\) replicates at \(\alpha{=}0.10\); the table shows the mean for four DGPs and four methods. DGPs: white noise \(N(0,1)\); AR(1), \(\phi{=}0.9\), Gaussian innovations; an MA(2) control, \(\theta{=}(0.6, 0.3)\) (not displayed); AR(1)+ARCH(1), \(x_t = 0.5x_{t-1} + \sigma_t\varepsilon_t\) with \(\sigma_t^2 = 0.2 + 0.4x_{t-1}^2\); ARFIMA(\(0, 0.4, 0\)); series lengths in the caption. The ARFIMA series are generated exactly by the Davies-Harte circulant-embedding method; the white-noise series are drawn directly, the MA(2) series by a direct causal filter, and the AR(1) and AR+ARCH series recursively from their defining equations, with burn-ins of 400 (AR(1)), 200 (MA(2)), and 300 (AR+ARCH). Coverage targets are exact-analytic from each DGP’s closed-form autocovariance (for AR+ARCH via its martingale-difference property), corroborated by independent Monte Carlo to within 1%. Block lengths use the automatic rule above; the sieve selects its order by BIC; the design is paired (one shared bootstrap seed per dataset across methods); the maximum Monte Carlo standard error is 0.7 percentage points. Dependence-aware methods correct much of the IID bootstrap’s undercoverage: on AR(1) with \(\phi{=}0.9\) at \(n{=}200\), coverage recovers from 27.8% to roughly 70% (blocks) and 83.1% (sieve), nearest to nominal under short-memory linear dependence; the blocks fall short because the dependence is strong relative to the series length. Under AR(1)+ARCH(1) the sieve reaches 89.1%, within a point of nominal. No method considered attains acceptable coverage under long-memory ARFIMA (\(d{=}0.4\)), so the library exposes the method choice rather than imposing a single default.

Table 1: Empirical coverage (%) of the bootstrap mean interval at 90%nominal (\(n{=}200\) white noise and AR(1), \(n{=}500\) AR+ARCH, \(n{=}1000\)ARFIMA; 5,000 Monte Carlo datasets per cell; max Monte Carlo standarderror 0.7pp). The sieve is nearest to nominal under short-memory lineardependence; no method reaches acceptable coverage under ARFIMA.
DGP (nominal 90%) IID Moving Stationary Sieve
White noise 89.6 89.0 88.6 88.6
AR(1) 27.8 69.7 70.2 83.1
AR + ARCH 65.1 85.2 84.8 89.1
ARFIMA (long memory) 8.2 27.1 28.9 21.9

Comparison

Table 2 compares the five nearest libraries; tsbootstrap is the only column with entries in both the resampling and conformal rows. TorchCP [23] and MAPIE target post-hoc conformal calibration, tsbootstrap dependence-aware uncertainty quantification. Because the adaptive calibrators are pure functions over user-supplied nonconformity scores, residuals from any prediction pipeline can be calibrated. Beyond the table, puncc ships EnbPI; statsforecast and neuralforecast attach split-conformal intervals to their own forecasters; none has a general resampling engine.

Table 2: Capability comparison; conservative entries where support islimited rather than absent. Versions (source-verified 2026-07-06):arch 8.0, MAPIE 1.4.1, TorchCP 1.2.1,darts 0.45.0, skforecast 0.23.0, puncc 0.9.3,statsforecast 2.0.3, neuralforecast 3.1.9;tsbootstrap 0.6.1. \(^{\dagger}\)MAPIE’s block bootstrap isonly an EnbPI calibration cross-validator.\(^{\ddagger}\)skforecast resamples one-step residuals to simulateforecast paths for its own forecasters (no block or sieve engine, noarbitrary statistics); its conformal mode is split, without adaptivecalibrators. Adaptive row: tsbootstrap ships EnbPI, ACI, NexCP,AgACI; TorchCP ships EnbPI, ACI, AgACI and MAPIE EnbPI,ACI (for fitted predictors, not coupled to a resampling engine);darts ships split and quantile wrappers only. NexCP istsbootstrap-only among the compared libraries.
Capability tsbootstrap arch MAPIE TorchCP darts skforecast
Dependence-aware TS resampling partial\(^{\dagger}\) no no partial\(^{\ddagger}\)
Wild / block-wild innovations no no no no no
Classical bootstrap CIs no no no no
Adaptive conformal calibrators no partial partial no no
Recommender + method metadata no no no no no
Streaming bounded-memory reduce no no no no
Multivariate VAR + ragged panel partial no no partial no
sktime / skbase adapters no no no no no

3.5pt

Performance

Our harness (bench_vs_arch.py) runs the compiled reducer and arch’s bs.apply(np.mean, reps=B), each library’s own streaming reducer and the closest like-for-like path, on the same warmed workload on a dedicated Hetzner ccx33 (eight vCPU, AMD EPYC-Milan; Ubuntu 24.04, Python 3.12.3, NumPy 2.4.6, arch 8.0.0, numba 0.65.1), measured at tsbootstrap 0.4.0 (commit 53aa089) and confirmed directionally by a pinned v0.6.1 re-run. The comparison is not symmetric in generality: arch pays a per-replicate Python callback for an arbitrary statistic, while the compiled kernel fixes the statistic, fuses the loop, and runs replicate-parallel on all eight threads against arch’s single-threaded apply loop; the speedups below quantify this fixed-statistic path only.

The sixteen-cell grid crosses the four methods both libraries implement (IID, MovingBlock, CircularBlock, StationaryBlock) with \(n \in \{200, 2000\}\) and \(B \in \{999, 10000\}\) on an AR(1) input (\(\phi{=}0.6\)), block length 20, mean statistic; each cell is the median of three warmed repetitions, JIT and import excluded (full grid in benchmarks/). The compiled backend is faster in all sixteen cells: at \(n{=}2000\) it runs \(3.9\times\) faster on IID at \(B{=}999\) (3.0 versus 11.7 ms) and \(34.0\times\) faster on CircularBlock at \(B{=}10000\) (4.9 versus 167.0 ms); the \(n{=}200\) cells are larger still, from arch’s per-replicate Python overhead; pinned to one thread it still leads by roughly \(2\times\) to \(8\times\) (v0.6.1 re-measurement). For a generality-matched comparison, the default numpy backend (which, like arch, accepts an arbitrary statistic) runs \(1.3\times\) to \(2.6\times\) slower than arch on the same cells; the compiled speedup comes entirely from fusing a fixed statistic into the kernel. On memory, bootstrap_reduce avoids materializing the \(O(Bn)\) replicate tensor (the numpy backend reduces in 2,048-replicate chunks; the compiled kernel holds only the \((B, |\theta|)\) statistic array). At \(n{=}2000\), \(B{=}50000\) (MovingBlock mean), peak resident memory above the interpreter floor is 1,944 MB materializing every replicate versus 20 MB streaming, about \(96\times\) (\(103\times\) at \(B{=}10000\); fresh subprocesses, warmed JIT, benchmarks/results/). The streaming footprint grows only with \(B\), about 0.4 KB per replicate, not with \(n \times B\).

Reproducibility, Quality, and Availability

tsbootstrap is MIT licensed (github.com/astrogilda/tsbootstrap, PyPI v0.6.1, Zenodo DOI 10.5281/zenodo.8226495) and supports Python 3.10–3.13 with five core dependencies (numpy, scipy, pydantic, scikit-base, narwhals); statsmodels, scikit-learn, and numba are optional extras. Test coverage is 93% overall with a blocking 80% per-file CI gate; GitHub Actions CI is green on main across a 12-cell matrix (Python 3.10–3.13 on Linux, macOS, and Windows) and further gates lint, zero-error mypy and pyright, executed notebooks, and the docs build; a nightly mutation ratchet (about 1,100 mutants over the engine and model core) fails on any new survivor. The core also ships as sktime/skbase estimator adapters (validated by check_estimator) plus a read-only MCP server (tsbootstrap-mcp, stdio, uvx-runnable) for LLM agents; 14 CI-executed notebooks are at tsbootstrap.readthedocs.io.

References↩︎

[1]
Künsch, H. R. (1989). The jackknife and the bootstrap for general stationary observations. The Annals of Statistics, 17(3):1217–1241.
[2]
Politis, D. N. and Romano, J. P. (1992). A circular block-resampling procedure for stationary data. In Exploring the Limits of Bootstrap, pages 263–270. Wiley.
[3]
Politis, D. N. and Romano, J. P. (1994). The stationary bootstrap. Journal of the American Statistical Association, 89(428):1303–1313.
[4]
Carlstein, E. (1986). The use of subseries values for estimating the variance of a general statistic from a stationary sequence. The Annals of Statistics, 14(3):1171–1179.
[5]
Paparoditis, E. and Politis, D. N. (2001). Tapered block bootstrap. Biometrika, 88(4):1105–1119.
[6]
Politis, D. N. and White, H. (2004). Automatic block-length selection for the dependent bootstrap. Econometric Reviews, 23(1):53–70.
[7]
Patton, A., Politis, D. N., and White, H. (2009). Correction to “Automatic block-length selection for the dependent bootstrap” by D. Politis and H. White. Econometric Reviews, 28(4):372–375.
[8]
Pascual, L., Romo, J., and Ruiz, E. (2004). Bootstrap predictive inference for ARIMA processes. Journal of Time Series Analysis, 25(4):449–465.
[9]
Bühlmann, P. (1997). Sieve bootstrap for time series. Bernoulli, 3(2):123–148.
[10]
Kreiss, J.-P. (1992). Bootstrap procedures for AR(\(\infty\)) processes. In Bootstrapping and Related Techniques, pages 107–113. Springer.
[11]
Wu, C. F. J. (1986). Jackknife, bootstrap and other resampling methods in regression analysis. The Annals of Statistics, 14(4):1261–1295.
[12]
Liu, R. Y. (1988). Bootstrap procedures under some non-i.i.d. models. The Annals of Statistics, 16(4):1696–1708.
[13]
Mammen, E. (1993). Bootstrap and wild bootstrap for high dimensional linear models. The Annals of Statistics, 21(1):255–285.
[14]
Davidson, R. and Flachaire, E. (2008). The wild bootstrap, tamed at last. Journal of Econometrics, 146(1):162–169.
[15]
Shao, X. (2010). The dependent wild bootstrap. Journal of the American Statistical Association, 105(489):218–235.
[16]
Efron, B. (1979). Bootstrap methods: another look at the jackknife. The Annals of Statistics, 7(1):1–26.
[17]
Efron, B. (1987). Better bootstrap confidence intervals. Journal of the American Statistical Association, 82(397):171–185.
[18]
Götze, F. and Künsch, H. R. (1996). Second-order correctness of the blockwise bootstrap for stationary observations. The Annals of Statistics, 24(5):1914–1933.
[19]
Xu, C. and Xie, Y. (2021). Conformal prediction interval for dynamic time-series. Proceedings of the 38th International Conference on Machine Learning, PMLR 139:11559–11569.
[20]
Gibbs, I. and Candès, E. (2021). Adaptive conformal inference under distribution shift. Advances in Neural Information Processing Systems, 34:1660–1672.
[21]
Barber, R. F., Candès, E. J., Ramdas, A., and Tibshirani, R. J. (2023). Conformal prediction beyond exchangeability. The Annals of Statistics, 51(2):816–845.
[22]
Zaffran, M., Féron, O., Goude, Y., Josse, J., and Dieuleveut, A. (2022). Adaptive conformal predictions for time series. Proceedings of the 39th International Conference on Machine Learning, PMLR 162:25834–25866.
[23]
Huang, J., Song, J., Zhou, X., Jing, B., and Wei, H. (2025). TorchCP: A Python library for conformal prediction. Journal of Machine Learning Research, 26(266):1–25.