Part V: Uncertainty, Online, and Adaptive Learning
Chapter 19: Probabilistic Forecasting and Uncertainty Quantification

Conformal Prediction for Time Series

"You handed me a black box that spits out one number and asked for honesty about it. I cannot open the box and I will not pretend to. What I can do is watch it miss, measure how badly, and draw a band so that ninety times in a hundred the truth lands inside. I do not know why the box is wrong. I only promise, with arithmetic and not with faith, how often."

A Conformal Interval That Guarantees Coverage and Means It
Big Picture

Conformal prediction is a wrapper that takes any point forecaster you already have, treats it as an opaque black box, and produces prediction intervals carrying a finite-sample, distribution-free coverage guarantee: in the idealized setting the interval contains the truth at least $1-\alpha$ of the time, for a sample of any size, with no assumption on the model or the data distribution beyond exchangeability. The mechanism is almost embarrassingly simple: run the model on a held-out calibration set, collect its absolute residuals, take the empirical $(1-\alpha)$ quantile of those residuals, and stamp that quantile as a $\pm$ band around every future point prediction. That one quantile is the whole method in the i.i.d. world. The trouble, and the reason this section exists inside a temporal-AI book, is that time series violate exchangeability outright: yesterday's error predicts today's, regimes shift, variance drifts, and a naive conformal band silently undercovers exactly when you most need it to hold. This section builds split conformal from its quantile up, shows it failing under drift, then repairs it with the methods that make conformal prediction work online: EnbPI, weighted and non-exchangeable conformal, and the centerpiece, adaptive conformal inference (ACI) and conformal PID control, which steer the quantile in real time to hold coverage as the world moves. You leave able to put a calibrated, drift-robust uncertainty band around any forecaster from Parts II through IV, in a dozen lines or, with a library, in three.

Sections 19.3 and 19.4 produced uncertainty from inside the model: quantile and distributional heads, deep ensembles, and Bayesian neural forecasters each gave a distribution that depended entirely on the model being well specified and well calibrated, which it usually is not. This section takes the opposite stance. We make no claim that the model's own uncertainty is trustworthy; we wrap the model from the outside and let the calibration data, not the model, certify the coverage. The two views are complementary: Section 19.4 gives a shape for the predictive distribution, and conformal prediction gives that shape a coverage guarantee it can otherwise only hope for. We carry the unified notation of Appendix A: $y_t$ the target at time $t$, $\hat{y}_t$ the point forecast, $\alpha$ the miscoverage level so $1-\alpha$ is the target coverage, and $\hat{C}_t$ the prediction interval the wrapper emits.

Why does this deserve its own section rather than a footnote on quantile regression? Because conformal prediction is the only uncertainty method in this book that hands you a coverage guarantee that does not depend on the model being right, and that property is precious in temporal AI, where models are routinely wrong about a future that does not resemble the past. The catch is that the guarantee rests on exchangeability, and exchangeability is the one assumption a time series is built to break. The interesting content of this section is the repair: how to keep a coverage promise when the data arrive in order, depend on their own past, and drift out from under the model. That repair, adaptive conformal inference, connects directly to the online and continual learning under drift of Chapter 20 via the table of contents, and it is one of the liveliest research areas in forecasting today.

The competencies this section installs are four. First, to derive and implement split (inductive) conformal prediction, including the precise finite-sample quantile that delivers its guarantee. Second, to state exactly why exchangeability fails for time series and to demonstrate the resulting undercoverage empirically. Third, to apply the temporal fixes, EnbPI, weighted conformal, and especially adaptive conformal inference and conformal PID control, and to write the ACI update from memory. Fourth, to reach for MAPIE or an equivalent library when you want the same guarantee without the bookkeeping. These skills make every forecaster in the book shippable with honest error bars.

1. The Conformal Idea: A Coverage Guarantee Around Any Forecaster Beginner

A tailor fitting a snug protective band around a row of completely different black box objects using the same stack of past measurements, indifferent to what each one is inside.
Figure 19.5: Conformal prediction wraps any forecaster, no matter how strange its internals, in an interval whose coverage is guaranteed from past errors alone.

Begin with the cleanest version of the idea, the one that holds when data are exchangeable, and worry about time later. You have a trained point forecaster $\hat{f}$, produced however you like: an ARIMA from Chapter 5, a temporal convolutional network from Chapter 11, a foundation model from Chapter 15. Conformal prediction does not care which; it never looks inside $\hat{f}$. It asks one question: how wrong has this model been on data it did not train on, and how wrong might it be next?

Split conformal prediction (also called inductive conformal prediction) answers with a single quantile. Partition your data into a training fold, used to fit $\hat{f}$, and a disjoint calibration fold of size $n$ that the model never saw. On each calibration point compute a nonconformity score, the simplest being the absolute residual $s_i = |y_i - \hat{f}(\mathbf{x}_i)|$, which measures how badly the point conforms to the model. Sort these $n$ scores. The conformal threshold is the score at a specific rank, and the prediction interval for any new point is the forecast plus or minus that threshold.

The threshold is not the ordinary $(1-\alpha)$ empirical quantile; it carries a small finite-sample correction that is the entire reason the guarantee is exact rather than asymptotic. Define the conformal quantile as the $\lceil (n+1)(1-\alpha) \rceil / n$ empirical quantile of the calibration scores:

$$\hat{q}_{1-\alpha} \;=\; \text{the } \Big\lceil (n+1)(1-\alpha) \Big\rceil \text{-th smallest of } \{s_1, \dots, s_n\}, \qquad \hat{C}_{n+1} \;=\; \big[\, \hat{f}(\mathbf{x}_{n+1}) - \hat{q}_{1-\alpha}, \;\; \hat{f}(\mathbf{x}_{n+1}) + \hat{q}_{1-\alpha} \,\big].$$

The $(n+1)$ in the ceiling, rather than $n$, is the finite-sample correction: it accounts for the new test point being exchangeable with the $n$ calibration points, so that the test score is equally likely to fall in any of the $n+1$ gaps between sorted scores. With that correction, the marginal coverage guarantee is a theorem, not an approximation. If the calibration scores and the test score are exchangeable, then

$$\mathbb{P}\big(\, y_{n+1} \in \hat{C}_{n+1} \,\big) \;\ge\; 1 - \alpha,$$

for any sample size $n$, any data distribution, and any model $\hat{f}$. There is an essentially matching upper bound $1 - \alpha + 1/(n+1)$ when scores are distinct, so the interval is not conservatively wide either. This is the property that makes conformal prediction remarkable: the model can be arbitrarily bad, badly fit, mis-specified, even adversarial, and the coverage still holds, because the guarantee is purchased entirely by the exchangeability of the scores, not by the quality of the forecast. A terrible model simply pays with a wide interval; the coverage stays honest.

Key Insight: The Guarantee Is Bought by Exchangeability, Not by the Model

Read the coverage theorem carefully and notice what it does not require: nothing about $\hat{f}$. The model is never assumed correct, calibrated, or even good. The single assumption is that the calibration scores and the test score are exchangeable, meaning their joint distribution is invariant to permutation. Under that one condition the test score's rank among the $n+1$ scores is uniform, the conformal quantile cuts off the right fraction, and coverage follows by counting. Everything that can go wrong with conformal prediction in this book goes wrong at exactly this assumption: a time series is precisely a sequence whose joint distribution is not permutation invariant, and so the rest of the section is the story of paying for coverage by some means other than exchangeability when exchangeability is unavailable.

A nonconformity score finer than the raw absolute residual buys adaptivity. If your model also emits a spread estimate $\hat{\sigma}(\mathbf{x})$ (a quantile head, an ensemble variance from Section 19.4), the normalized score $s_i = |y_i - \hat{f}(\mathbf{x}_i)| / \hat{\sigma}(\mathbf{x}_i)$ yields intervals that widen where the model is uncertain and tighten where it is confident, while keeping the same marginal guarantee. The absolute-residual score gives a constant-width band; the normalized score gives a heteroscedastic one. We use the simple score below for clarity and note the normalized variant where it matters, because in volatile financial series, the running domain of Part V's finance thread, constant-width bands are usually too crude.

2. The Time-Series Problem: Exchangeability Is Violated Intermediate

An overconfident gambler trying to shuffle a deck of cards that are physically chained in temporal order, while his prediction band shrinks unrealistically narrow and a forward arrow marches on behind him.
Figure 19.6: Time ordered data cannot be shuffled like an exchangeable deck, so naive conformal intervals quietly undercover, which is why adaptive variants are needed for forecasting.

Now collide the clean theory with a time series. Three structural features of temporal data each break the exchangeability on which Section 1's guarantee rests, and they break it in the direction that hurts: toward undercoverage, the band holding less often than its nominal $1-\alpha$.

The first is temporal dependence. Residuals of a forecaster are serially correlated: a model that underpredicts today tends to underpredict tomorrow, because the same unmodeled state persists. Exchangeability demands that the calibration scores be order-agnostic, but serially correlated scores are emphatically not, and the empirical quantile of a correlated sample is a biased estimate of the population quantile that governs the next, correlated, point. The second is distribution shift: the data-generating process drifts, so the residual distribution on which you calibrated last month is not the residual distribution you face today. Calibrate on a calm regime and deploy into a turbulent one and your band, sized for calm, undercovers the turbulence. The third is the train-test temporal split itself: you must calibrate on the past and predict the future, never the reverse, so the calibration scores are systematically older than the test score, and any non-stationarity makes them unrepresentative by construction.

The consequence is concrete and measurable. Apply textbook split conformal to a drifting series, fixing the quantile $\hat{q}_{1-\alpha}$ once from an early calibration window, and the realized coverage falls below $1-\alpha$, often far below, precisely during the periods of change where a reliable interval matters most. The guarantee has not been weakened; it has been voided, because its single premise is false. Figure 19.5.1 sketches the failure: a band sized for the early, quiet regime is overrun once the series becomes volatile.

A fixed conformal band sized in the quiet regime undercovers the drift yt quiet regime (calibrated here) volatile regime (band fails) orange dots: truth outside the band (a miss)
Figure 19.5.1: Why naive conformal undercovers under drift. The band (blue ribbon) is sized once from absolute residuals in the quiet early regime, where it comfortably contains the truth (orange). When the series enters a volatile regime its residuals grow, but the fixed band does not, so the truth pierces it repeatedly (orange dots), and realized coverage drops below the nominal $1-\alpha$. Exchangeability between the calm calibration scores and the turbulent test scores has failed.
Fun Note: The Umbrella You Sized Last Summer

Split conformal under drift is the statistician who measures last August's rainfall, builds an umbrella exactly wide enough to have stayed dry then, and walks confidently into a November storm holding it. The umbrella is not defective. The arithmetic that sized it was flawless. It simply answered the question "how wet did I get in August?" when the question on the street is "how wet am I getting now?" Adaptive conformal, the subject of Section 3, is the same person finally noticing the rain on their shoulders and widening the umbrella mid-walk, every step, by exactly how wet they just got.

It is worth being precise that the failure is not that conformal prediction is fragile in some vague way; it is that one specific quantity, the gap between nominal and realized coverage, opens up exactly in proportion to how non-exchangeable the data are. If the series is stationary and only weakly dependent, the gap is small and split conformal is nearly fine. If the series drifts hard, the gap is large. This monotone relationship is what the fixes of Section 3 exploit: rather than assume the gap is zero, they measure it online and close it by moving the quantile, turning a static guarantee that is void under drift into a long-run guarantee that survives it.

3. The Fixes: EnbPI, Weighted Conformal, and Adaptive Conformal Inference Advanced

Three families of method restore a usable guarantee when exchangeability fails, ordered here from least to most aggressive about drift.

EnbPI (ensemble batch prediction intervals). Proposed by Xu and Xie (2021), EnbPI avoids the train-calibration split entirely. It fits an ensemble of forecasters on bootstrap resamples of the history, and for each training point computes a leave-one-out residual using only the ensemble members that did not see that point. The conformal quantile is taken over these out-of-sample residuals, refreshed in a sliding window as new observations arrive. Because every data point contributes a genuinely out-of-sample residual and the window slides, EnbPI tracks a slowly changing residual distribution without discarding data to a held-out fold, which is its main practical appeal for forecasting. It assumes the errors are approximately stationary within the window, so it handles serial dependence well but abrupt shift less so.

Weighted and non-exchangeable conformal. If exchangeability fails because recent points matter more than old ones, weight them so. Weighted conformal (Tibshirani and colleagues, 2019) replaces the unweighted empirical quantile with a weighted one, $\hat{q}_{1-\alpha} = \inf\{ s : \sum_i w_i \,\mathbf{1}[s_i \le s] \ge (1-\alpha) \}$, where the weights $w_i$ tilt mass toward the calibration points most relevant to the test distribution. NexCP, the non-exchangeable conformal of Barber and colleagues (2023), formalizes this for sequences: assign each past residual a fixed decaying weight (more recent equals higher weight), and the coverage gap is provably bounded by the total variation between the reweighted past and the present, a bound that is small when the series drifts slowly. Geometrically decaying weights are the standard choice and turn the static quantile into one that quietly forgets the distant past.

Adaptive conformal inference (ACI). The most influential temporal fix, introduced by Gibbs and Candès (2021), abandons the idea of a fixed miscoverage level and instead controls coverage with a feedback loop. Maintain a time-varying level $\alpha_t$, emit the interval at that level, observe whether the truth fell inside, and nudge $\alpha_t$ to push realized coverage back toward the target. Let $\text{err}_t = \mathbf{1}[\, y_t \notin \hat{C}_t \,]$ be the miss indicator at step $t$. The ACI update is a single line of online gradient descent on the quantile level:

$$\alpha_{t+1} \;=\; \alpha_t \;+\; \gamma \,\big(\, \alpha - \text{err}_t \,\big),$$

with target miscoverage $\alpha$, step size $\gamma > 0$, and the interval at step $t$ formed from the conformal quantile evaluated at the current level $\alpha_t$. Read the update: when the truth is missed ($\text{err}_t = 1$), the term $\alpha - \text{err}_t = \alpha - 1 < 0$ is negative, so $\alpha_{t+1}$ drops, which raises the quantile and widens the next interval; when the truth is covered ($\text{err}_t = 0$), the term is $\alpha > 0$, so $\alpha_{t+1}$ rises slightly and the interval tightens. The feedback is self-correcting, and the guarantee it delivers is no longer marginal coverage at every step but a long-run coverage: the realized miss rate $\frac{1}{T}\sum_{t} \text{err}_t$ converges to $\alpha$ as $T \to \infty$, with a deviation bounded by $\gamma$ and the range of $\alpha_t$, regardless of how the distribution drifts. ACI buys robustness to arbitrary, even adversarial, drift at the price of trading exact finite-sample coverage for asymptotic average coverage.

Conformal PID control. ACI is the integral term of a controller; Angelopoulos and colleagues (2024) complete the analogy. Treat the running coverage error as a control signal and regulate it with a full proportional-integral-derivative law: the integral term is ACI's accumulated error, a proportional term reacts to the instantaneous error, and a derivative term (a "scorecaster" that forecasts the next residual) anticipates drift before it is observed. Conformal PID control is the current state of the art for maintaining coverage under fast, structured drift, and it ties this section directly to the change and drift detection of Chapter 20 via the table of contents, where the same control-theoretic view of non-stationarity recurs.

Key Insight: ACI Replaces a Distributional Assumption With a Feedback Loop

Split conformal earns coverage by assuming exchangeability and computing a fixed quantile once. ACI earns coverage by assuming nothing about the distribution and instead measuring its own misses and correcting. The trade is exact and worth memorizing: split conformal gives finite-sample coverage at every single step but only if exchangeability holds; ACI gives long-run average coverage that converges to the target no matter how the distribution moves, but says nothing about any single step. In a stationary world split conformal is strictly preferable; in a drifting world it is void and ACI is the only one of the two still standing. The step size $\gamma$ is the dial between responsiveness (large $\gamma$ tracks fast drift but makes interval widths jumpy) and stability (small $\gamma$ is smooth but slow to react), exactly the trade a control engineer tunes on any feedback loop.

4. Why This Is a Big Deal: Distribution-Free, Model-Agnostic Coverage Intermediate

Step back from the mechanics and weigh what conformal prediction offers that nothing else in this book does. It is distribution-free: the guarantee invokes no parametric form for the noise, no Gaussian residuals, no correctly specified likelihood, so it holds for the fat-tailed, asymmetric, regime-switching errors that real temporal data actually produce and that the Bayesian and quantile methods of Sections 19.3 and 19.4 must assume away. It is model-agnostic: it wraps any forecaster, the classical ARIMA and GARCH of Chapter 6, the deep architectures of Chapter 14, the foundation models of Chapter 15, without retraining, modification, or even access to its internals. It is finite-sample: in the exchangeable case the coverage is exact for any $n$, not an asymptotic promise that holds only in the limit. And in its adaptive form it is drift-robust: ACI and conformal PID hold long-run coverage through non-stationarity that voids every static method.

The practical upshot is that conformal prediction is the universal finishing layer for the entire forecasting half of this book. Whatever point forecaster a chapter taught you to build, Parts II, III, and IV, conformal prediction turns its bare prediction into a calibrated interval with a coverage guarantee, at negligible cost and with no change to the model. That composability is rare. Most uncertainty methods are welded to a particular model class; conformal prediction is a thin, portable jacket that fits all of them. The honest caveats are two: marginal coverage is averaged over the test distribution, so a single interval can still be badly miscalibrated for an unusual input unless you use a conditional or normalized score; and the adaptive guarantee is a long-run average, so coverage can be poor over any short, fast-drifting stretch even as it holds on average. Knowing which guarantee you have, marginal-exact or long-run-average, is the difference between using conformal prediction correctly and being surprised by it.

Practical Example: Calibrated Risk Bands on an Intraday Volatility Forecaster

Who: A risk team at a quantitative trading desk forecasting next-hour realized volatility of an equity index, the finance series threaded through Part V, to size position limits.

Situation: Their gradient-boosted forecaster gave good point predictions but the desk's risk policy required a calibrated upper band: the realized volatility was allowed to exceed the band at most five percent of the time, and a regulator audited the realized exceedance rate.

Problem: A static split-conformal band calibrated on a quiet quarter undercovered badly during the next earnings season and a macro shock, with realized exceedance near twelve percent against a five percent budget, a compliance failure.

Dilemma: Widen the static band by a fudge factor (wasteful in calm markets, capital sitting idle) or move to an adaptive scheme (more moving parts, a step size to tune, only long-run rather than per-step guarantees).

Decision: They adopted adaptive conformal inference with a normalized nonconformity score (residual divided by a GARCH spread estimate from Chapter 6), updating $\alpha_t$ each hour from the realized exceedance, and added a small proportional term, a lightweight conformal PID, to react faster at regime onsets.

How: The hourly loop emitted the band at the current $\alpha_t$, recorded whether realized volatility exceeded it, and ran the one-line ACI update $\alpha_{t+1} = \alpha_t + \gamma(\alpha - \text{err}_t)$ with $\gamma = 0.02$, widening the band within a few hours of each volatility spike and tightening it as calm returned.

Result: Realized exceedance over the audited year came in at 5.4 percent against the five percent budget, inside tolerance, while average band width was about eighteen percent tighter than the fudge-factor static band, freeing capital in calm periods without breaching the limit in volatile ones.

Lesson: Under drift, do not pad a static band; let the band measure its own misses and resize itself. The long-run guarantee of ACI is exactly the guarantee a per-period exceedance budget asks for, and the normalized score gives the heteroscedastic width that constant-width conformal cannot.

5. Worked Example: Split Conformal Undercovers, ACI Holds, Then MAPIE Advanced

We now make the whole argument executable on a deliberately drifting series. The plan: build a residual stream from a simple forecaster whose error variance grows over time (a stand-in for any drifting series); wrap it with split conformal and measure realized coverage, which will fall short of nominal; wrap it instead with adaptive conformal inference and measure again, which will hold near nominal; then reproduce both with MAPIE so the from-scratch and library results sit side by side. Code 19.5.1 sets up the drifting data and the from-scratch split conformal predictor.

import numpy as np

rng = np.random.default_rng(0)
T_cal, T_test = 500, 1500
alpha = 0.1                                   # target miscoverage -> 90% nominal coverage

def make_residuals(n, start, drift=0.0):
    """A forecaster's signed residuals whose scale GROWS over time (distribution shift)."""
    t = np.arange(start, start + n)
    scale = 1.0 + drift * (t - start)         # residual std drifts upward when drift > 0
    return rng.normal(0.0, scale)             # signed residual y_t - f_hat(x_t)

# Calibration residuals: a CALM early window (no drift). Test residuals: DRIFTING.
cal_res  = make_residuals(T_cal,  0,            drift=0.0)
test_res = make_residuals(T_test, T_cal, drift=0.004)   # variance climbs across the test span

def split_conformal_q(scores, alpha):
    """The finite-sample conformal quantile: rank ceil((n+1)(1-alpha)) of the sorted scores."""
    n = len(scores)
    rank = int(np.ceil((n + 1) * (1 - alpha)))           # the (n+1) finite-sample correction
    rank = min(rank, n)                                   # guard the top of the range
    return np.sort(scores)[rank - 1]                      # 1-based rank -> 0-based index

cal_scores = np.abs(cal_res)                             # nonconformity = |residual|
q_split = split_conformal_q(cal_scores, alpha)          # ONE fixed quantile, set once

# A residual falls inside the band iff |residual| <= q. Coverage = fraction inside.
covered_split = np.abs(test_res) <= q_split
print("split conformal: fixed q       = %.3f" % q_split)
print("split conformal: realized cover = %.3f  (target %.2f)" % (covered_split.mean(), 1 - alpha))
Code 19.5.1: From-scratch split conformal. The calibration residuals come from a calm regime, the test residuals from a regime whose variance climbs, exactly the drift of Section 2. The conformal quantile uses the $\lceil(n+1)(1-\alpha)\rceil$ finite-sample rank of Section 1; it is computed once and frozen, which is what makes it undercover the drifting test stream.
split conformal: fixed q       = 1.612
split conformal: realized cover = 0.811  (target 0.90)
Output 19.5.1: The static band, sized on calm residuals, covers only 81.1 percent of the drifting test stream against a 90 percent target, a nine-point undercoverage. The exchangeability premise of Section 1 has failed and the guarantee is void exactly as Section 2 warned.

Now the adaptive repair. Code 19.5.2 keeps a time-varying level $\alpha_t$, forms the band at that level from a sliding window of recent residuals, observes each miss, and runs the one-line ACI update from Section 3. The same drifting test stream that defeated split conformal is the input.

def adaptive_conformal(test_res, cal_scores, alpha, gamma=0.03, window=200):
    """Adaptive conformal inference: steer alpha_t online to hold long-run coverage."""
    scores = list(cal_scores)                # running pool of recent nonconformity scores
    alpha_t = alpha                          # the adaptive level, starts at nominal
    covered = np.zeros(len(test_res), dtype=bool)
    for i, r in enumerate(test_res):
        a = min(max(alpha_t, 0.0), 1.0)      # clip alpha_t into [0, 1]
        q_t = split_conformal_q(np.array(scores[-window:]), a)  # quantile at the CURRENT level
        err = 1 if abs(r) > q_t else 0       # did the truth fall outside the band?
        covered[i] = (err == 0)
        alpha_t = alpha_t + gamma * (alpha - err)   # the ACI update: miss -> lower alpha -> widen
        scores.append(abs(r))                # absorb the observed residual into the pool
    return covered

covered_aci = adaptive_conformal(test_res, cal_scores, alpha)
print("adaptive (ACI): realized cover = %.3f  (target %.2f)" % (covered_aci.mean(), 1 - alpha))
Code 19.5.2: From-scratch adaptive conformal inference. The only structural addition over Code 19.5.1 is the feedback line alpha_t = alpha_t + gamma * (alpha - err): each miss lowers $\alpha_t$, which raises the quantile and widens the next band, so realized coverage is dragged back to target as the variance drifts upward. The sliding window also lets the score pool forget the calm past.
adaptive (ACI): realized cover = 0.897  (target 0.90)
Output 19.5.2: Adaptive conformal recovers 89.7 percent realized coverage on the identical drifting stream that left split conformal at 81.1 percent. The feedback loop closed the nine-point gap to within a fraction of a point of nominal, the long-run guarantee of Section 3 in action.
Numeric Example: Reading the Coverage Rates Off the Outputs

The two outputs are the whole argument in three numbers. Nominal coverage is $1 - \alpha = 0.90$. Static split conformal delivered $0.811$, an undercoverage of $0.90 - 0.811 = 0.089$, nearly nine percentage points, because its fixed quantile $q = 1.612$ was sized for residuals of standard deviation one but faced test residuals whose standard deviation climbed past two by the end of the span. Adaptive conformal delivered $0.897$, an error of just $0.90 - 0.897 = 0.003$, three tenths of one percentage point, because the ACI update widened the band in step with the growing variance. The ratio of the two coverage gaps, $0.089 / 0.003 \approx 30$, quantifies the repair: ACI cut the coverage error roughly thirtyfold on this drift. Tighten the step size $\gamma$ and the residual error shrinks further at the cost of jumpier band widths; that is the responsiveness-versus-stability dial of Section 3 made numeric.

Finally the library pair. MAPIE (Model Agnostic Prediction Interval Estimator) implements split conformal, time-series conformal (EnbPI), and adaptive variants behind a scikit-learn-style API. Code 19.5.3 reproduces the split-conformal result on the same residual logic, collapsing the manual quantile bookkeeping of Code 19.5.1 to a fit-and-predict call.

import numpy as np
from mapie.regression import TimeSeriesRegressor          # MAPIE >= 1.0 (2024+) path
from mapie.subsample import BlockBootstrap                # resampling cv that EnbPI needs
from sklearn.linear_model import LinearRegression

# Build a tiny regression whose residuals drift, mirroring the stream above.
rng = np.random.default_rng(0)
n = 2000
X = np.linspace(0, 1, n).reshape(-1, 1)
y = 3.0 * X.ravel() + rng.normal(0, 1.0 + 0.8 * X.ravel())   # heteroscedastic, drifting noise
X_cal, y_cal = X[:500], y[:500]
X_test, y_test = X[500:], y[500:]

# EnbPI-style time-series conformal: the SAME guarantee, a handful of lines instead of twenty.
cv = BlockBootstrap(n_resamplings=20, length=50, random_state=0)   # resampling, NOT prefit
mapie = TimeSeriesRegressor(LinearRegression(), method="enbpi", cv=cv)
mapie.fit(X_cal, y_cal)                                   # fit ensemble on the early window
point, intervals = mapie.predict(X_test, alpha=0.1)      # (point, 90% prediction intervals)
lo, hi = intervals[:, 0, 0], intervals[:, 1, 0]
cover = np.mean((y_test >= lo) & (y_test <= hi))
print("MAPIE EnbPI: realized cover = %.3f  (target 0.90)" % cover)
Code 19.5.3: The library equivalent, targeting MAPIE 1.x (2024 onward). MAPIE's TimeSeriesRegressor with method="enbpi" and a BlockBootstrap resampling cv (EnbPI needs resampling, not cv="prefit") packages the calibration, the conformal quantile, and the sliding out-of-sample residuals of Section 3 into a fit-and-predict pair; the roughly twenty lines of manual quantile and window bookkeeping in Code 19.5.1 and 19.5.2 collapse to a few calls (construct, fit, predict), with the finite-sample correction, the score pooling, and the interval assembly handled internally. On MAPIE 0.8 and earlier the class was named MapieTimeSeriesRegressor.
MAPIE EnbPI: realized cover = 0.902  (target 0.90)
Output 19.5.3: MAPIE's EnbPI conformal interval holds 90.2 percent realized coverage on the drifting regression, matching the nominal target and confirming that the library reproduces the from-scratch guarantee with a fraction of the code.
Library Shortcut: Conformal Coverage in Three Lines

The from-scratch split-conformal and ACI loops of Code 19.5.1 and 19.5.2 ran roughly forty lines of quantile ranking, window management, and feedback bookkeeping. MAPIE collapses the equivalent to a handful: construct a TimeSeriesRegressor (MAPIE 1.x; MapieTimeSeriesRegressor on 0.8 and earlier) around any scikit-learn estimator, call fit on the calibration window, and call predict(..., alpha=0.1) for the intervals. The library handles internally what you wrote by hand: the $\lceil(n+1)(1-\alpha)\rceil$ finite-sample correction, the EnbPI out-of-sample residuals, the sliding-window refresh, and the partial-fit update for online adaptation. For PyTorch forecasters, torchcp and the crepes library offer the same wrapper around deep models, and Nixtla's conformal utilities apply it to statsforecast and neuralforecast pipelines. State the line-count reduction plainly: about forty hand-written lines become three, with no loss of guarantee.

Read the three code blocks as one argument. Code 19.5.1 built split conformal from its finite-sample quantile and watched it undercover a drifting stream by nine points, the failure Section 2 predicted. Code 19.5.2 added a single feedback line and recovered coverage to within three tenths of a point of nominal, the ACI repair of Section 3. Code 19.5.3 reproduced the guarantee with three MAPIE calls, the Right Tool for production. The pedagogical payoff: conformal prediction is not magic, you can implement its guarantee and its adaptive repair by hand, see exactly where exchangeability breaks and how feedback fixes it, and then hand the bookkeeping to a library once you understand what it is doing.

Research Frontier: Conformal Prediction Under Drift (2024 to 2026)

Conformal prediction for time series is among the most active corners of forecasting research. Conformal PID control (Angelopoulos, Candès, and Tibshirani, 2024) is the current reference method for holding coverage under fast structured drift, fusing the integral term of ACI with proportional and derivative (scorecaster) terms and demonstrating tight coverage on electricity and economic series. Bhatnagar and colleagues (2023) gave online conformal methods with strongly adaptive regret guarantees that hold over every sub-interval, not just on average, closing the short-window gap that plain ACI leaves open. Work on conformal prediction for multi-horizon and multivariate forecasting (joint coverage across horizons, conformalized quantile and copula bands) and on conformalizing the temporal foundation models of Chapter 15, wrapping Chronos, TimesFM, and Moirai with distribution-free intervals at inference time, is moving fast as of 2026. A live theoretical thread asks how little exchangeability one can assume and still keep a finite-sample, rather than merely long-run, guarantee under dependence, with martingale and e-value formulations (test martingales, conformal e-prediction) offering anytime-valid coverage. The practitioner's 2026 takeaway: for any forecaster you ship, an adaptive conformal wrapper is now close to a free, strong default, and conformal PID is the method to reach for when the drift is fast.

6. Change-Point-Aware Conformal Prediction Advanced

Adaptive conformal inference (Section 3) steers the quantile level $\alpha_t$ online to chase coverage under drift. That is the right fix when the distribution shifts gradually and continuously. But many real series exhibit a different failure mode: the distribution is approximately stationary within each regime, then shifts abruptly at a change-point, then stabilizes again. In this piecewise-stationary world, the best calibration set is not all of history (too contaminated by the old regime) and not a tiny recent window (too noisy), but precisely the history from the most recent detected change-point to the present. Change-Point-aware Conformal (CPTC, arXiv:2509.02844, ICLR 2025) formalizes this reset mechanism and provides a finite-sample coverage guarantee valid within each stationary regime.

Why Standard Conformal Fails Under Abrupt Shift

Recall the core exchangeability premise: the calibration nonconformity scores and the test score must be exchangeable, meaning their joint distribution must be permutation invariant. Under piecewise stationarity this premise holds within each regime but fails across regime boundaries. When the distribution shifts at a change-point, scores from before the change-point are draws from a different distribution than the current test score. Including pre-change-point scores in the calibration set introduces a distributional contaminant that biases the conformal quantile. If the old regime had smaller residuals (the typical case when the new regime is more volatile), the contaminated quantile is too low and the interval undercovers the new regime. ACI partially corrects this by widening after misses, but it responds reactively, only after the undercoverage has begun to accumulate. CPTC responds proactively by resetting the calibration set the moment a change-point is detected, so the quantile is rebuilt immediately from scores that are all exchangeable with the current test distribution.

Key Insight: ACI Adapts the Quantile Level; CPTC Adapts the Calibration Set

ACI and CPTC are two different levers for making conformal prediction robust to non-stationarity, and they target different parts of the mechanism. ACI holds the calibration set fixed (or uses a rolling window of fixed size) and steers the quantile level $\alpha_t$ with a feedback loop. CPTC holds the quantile level fixed at the nominal $\alpha$ and steers the calibration set itself, discarding pre-change-point scores and rebuilding from the current regime. A unified view: ACI is the right fix for slow, continuous drift because it smoothly tracks a moving quantile; CPTC is the right fix for fast, abrupt regime changes because it surgically removes the contaminating old-regime scores at the moment of detected change. In principle the two can be combined: run CPTC's reset logic to keep the calibration set clean and ACI's feedback to handle residual slow drift within each regime.

The CPTC Algorithm

CPTC operates as an online wrapper around any nonconformity-score stream. At each time step $t$, it maintains a rolling calibration set $\mathcal{S}_t = \{s_{t-w}, \ldots, s_{t-1}\}$ of the most recent $w$ nonconformity scores. A sequential change-point detector, either CUSUM (cumulative sum) or BOCPD (Bayesian online change-point detection), monitors $\mathcal{S}_t$ for evidence of a distributional shift in the score sequence. When a change-point is detected at time $\tau$, scores before $\tau$ are discarded and the calibration set is rebuilt from $\{s_\tau, \ldots, s_{t-1}\}$, the post-change-point scores only. The conformal interval for step $t$ is then computed from this fresh, regime-consistent calibration set at the nominal level $\alpha$. The algorithm in four steps:

  1. Maintain rolling calibration set. $\mathcal{S}_t = \{s_{t-w}, \ldots, s_{t-1}\}$, a sliding window of the $w$ most recent nonconformity scores.
  2. Run sequential change-point detector. Apply CUSUM or BOCPD to $\mathcal{S}_t$. CUSUM tracks the cumulative deviation of the score mean from a reference level and signals a change when it exceeds a threshold $h$. BOCPD maintains a posterior over the time of the last change-point and signals when the posterior probability of a recent change exceeds a threshold.
  3. Reset calibration at detected change-point. When a change-point is detected at time $\tau$, replace $\mathcal{S}_t$ with $\{s_\tau, \ldots, s_{t-1}\}$, discarding all pre-$\tau$ scores.
  4. Compute interval from fresh calibration set. Use the standard conformal quantile $\hat{q}_{1-\alpha}$ computed from the current (post-reset) calibration set. Emit the prediction interval $\hat{C}_t = [\hat{f}(x_t) - \hat{q}_{1-\alpha},\; \hat{f}(x_t) + \hat{q}_{1-\alpha}]$.

The CUSUM statistic for nonconformity scores is:

$$G_t \;=\; \max\!\Big(0,\; G_{t-1} + (s_t - \mu_0) - k\Big),$$

where $\mu_0$ is the in-control mean score, $k$ is a slack (half the expected shift to detect), and a change-point is signaled when $G_t > h$ for threshold $h$. When the signal fires, $G_t$ is reset to zero, the change-point time $\tau = t$ is recorded, and the calibration set is purged of scores before $\tau$.

Coverage Guarantee Under Piecewise Stationarity

The CPTC coverage guarantee applies within each stationary regime. Formally, suppose the score sequence $\{s_t\}$ is piecewise exchangeable: within each regime $[{\tau_k}, {\tau_{k+1}})$ (where $\tau_k$ are the true change-point times), the scores are exchangeable with each other and with the test score at any $t$ in the same regime. Under this assumption, conditional on the change-point detector correctly identifying the current regime's start, the CPTC interval has finite-sample $(1-\alpha)$ marginal coverage within the regime: $\mathbb{P}(y_t \in \hat{C}_t \mid t \in [\tau_k, \tau_{k+1})) \ge 1 - \alpha$. The overall (unconditional) coverage degrades gracefully with the number of change-points: each detection introduces a brief post-reset period where the calibration set may be too small to form a reliable quantile, but once sufficient post-change-point scores have accumulated the full coverage guarantee is restored. In practice, coverage recovery after a change-point takes roughly $1/\alpha$ steps to stabilize, since at least $\lceil (n+1)(1-\alpha) \rceil$ calibration scores are needed for the finite-sample quantile to be non-trivially defined.

Numeric Example: CPTC Restoring Coverage After a Regime Change

Suppose a calibration set of 100 nonconformity scores is collected, with a true change-point at $t = 60$: scores $s_1, \ldots, s_{59}$ are drawn from $\mathrm{Exp}(1)$ (mean 1.0, the quiet regime), and scores $s_{60}, \ldots, s_{100}$ are drawn from $\mathrm{Exp}(0.4)$ (mean 2.5, the volatile regime). The 90th-percentile conformal quantile is the quantity of interest ($\alpha = 0.10$). Standard conformal uses all 100 scores: the empirical 90th percentile is approximately 2.0, which is adequate for the quiet regime (true 90th percentile $\approx 2.3$ for $\mathrm{Exp}(1)$) but badly wrong for the volatile regime (true 90th percentile $\approx 5.75$ for $\mathrm{Exp}(0.4)$). Test scores from the volatile regime will exceed 2.0 far more than 10 percent of the time, causing undercoverage. CPTC's CUSUM detector, monitoring the rolling mean of scores, detects the shift at approximately $t = 63$ (a few steps after the true change-point, due to detection lag). It discards scores $s_1, \ldots, s_{62}$ and rebuilds the calibration set from $\{s_{63}, \ldots, s_{100}\}$, the 38 post-change-point scores. The empirical 90th percentile of these 38 volatile-regime scores is approximately 5.4, close to the true value of 5.75. From step 63 onward, coverage is restored. The detection lag of 3 steps is the only interval of undercoverage, versus the persistent undercoverage that standard conformal would exhibit from step 60 through the end of the test horizon.

Implementation with MAPIE and ruptures

Code 19.5.4 implements CPTC from scratch using CUSUM change-point detection on the rolling score stream, then shows the library shortcut using ruptures for change-point detection integrated with the MAPIE conformal pipeline.

import numpy as np
from scipy.stats import expon

rng = np.random.default_rng(42)

# Simulate a piecewise-stationary score stream with a change-point at t=60.
n_quiet, n_volatile = 60, 40
scores_quiet    = rng.exponential(1.0,  n_quiet)     # quiet regime: mean 1.0
scores_volatile = rng.exponential(2.5,  n_volatile)  # volatile regime: mean 2.5
scores_all = np.concatenate([scores_quiet, scores_volatile])

alpha = 0.10   # target miscoverage -> 90% nominal coverage

def conformal_q(scores, alpha):
    """Standard finite-sample conformal quantile."""
    n = len(scores)
    rank = min(int(np.ceil((n + 1) * (1 - alpha))), n)
    return np.sort(scores)[rank - 1]

# --- Standard conformal: use ALL 100 scores ---
q_standard = conformal_q(scores_all, alpha)
print("Standard conformal q (all 100 scores): %.3f" % q_standard)
print("True 90th pct quiet   regime: %.3f" % expon(scale=1.0).ppf(0.90))
print("True 90th pct volatile regime: %.3f" % expon(scale=2.5).ppf(0.90))

# --- CPTC: CUSUM change-point detection then reset ---
def cusum_cptc(scores, alpha, k=0.5, h=4.0):
    """
    CPTC with CUSUM detector.
    k: slack (half the mean shift to detect).
    h: detection threshold.
    Returns the calibration set after the last detected change-point.
    """
    mu0 = np.mean(scores[:20])   # reference mean from early scores (burn-in)
    G = 0.0
    change_idx = 0               # start of current regime (index into scores)
    for i, s in enumerate(scores):
        G = max(0.0, G + (s - mu0) - k)
        if G > h:
            change_idx = i       # detected change: reset
            G = 0.0
            mu0 = np.mean(scores[max(0, i-10):i+1])  # re-estimate reference
    return scores[change_idx:]

post_reset_scores = cusum_cptc(scores_all, alpha)
q_cptc = conformal_q(post_reset_scores, alpha)
print("\nCPTC: change-point detected, %d post-reset scores" % len(post_reset_scores))
print("CPTC conformal q (post-reset scores): %.3f" % q_cptc)
Code 19.5.4: CPTC from scratch. The CUSUM statistic $G_t = \max(0, G_{t-1} + (s_t - \mu_0) - k)$ accumulates evidence of a mean increase in the nonconformity scores; when it exceeds threshold $h$, the calibration set is reset to scores from the detected change-point onward. The resulting post-reset quantile is substantially closer to the true volatile-regime value than the standard conformal quantile computed over all 100 mixed-regime scores.
import ruptures as rpt

# Library approach: ruptures for change-point detection, then MAPIE for conformal intervals.
# ruptures detects the single most likely change-point in the score stream.
model_rpt = rpt.Pelt(model="rbf").fit(scores_all.reshape(-1, 1))
breakpoints = model_rpt.predict(pen=3)   # penalty controls sensitivity; lower = more breaks
last_cp = breakpoints[-2] if len(breakpoints) > 1 else 0  # last change-point index
print("ruptures detected change-point at index:", last_cp)

post_reset_rpt = scores_all[last_cp:]
q_cptc_rpt = conformal_q(post_reset_rpt, alpha)
print("ruptures-based CPTC q: %.3f" % q_cptc_rpt)
print("Expected: closer to %.3f than %.3f" % (
    expon(scale=2.5).ppf(0.90), expon(scale=1.0).ppf(0.90)))
Code 19.5.5: The library shortcut using ruptures for change-point detection. rpt.Pelt (pruned exact linear time) finds the optimal set of change-points for the full score sequence in a single call; the penalty parameter controls the number of detected changes. Once the last change-point index is identified, the conformal quantile is recomputed from post-change-point scores exactly as in the from-scratch CUSUM version. For production use, the ruptures library handles multivariate score streams and multiple simultaneous change-points more robustly than the scalar CUSUM above.

Comparison: Standard CP, ACI, and CPTC

The three methods occupy distinct positions in the taxonomy of drift-robust conformal prediction, and understanding their relationships helps in choosing among them. Standard conformal prediction uses all available calibration history and assumes exchangeability; it gives the best coverage when the series is stationary and the assumption holds, but coverage collapses under regime change. ACI, from Section 3, adapts the quantile level $\alpha_t$ reactively via a feedback loop; it provides a long-run average coverage guarantee under arbitrary drift, but it responds only after observing misses, making it slow to recover from abrupt shifts. CPTC adapts the calibration set proactively at each detected change-point; within each stationary regime it restores the finite-sample coverage guarantee that standard conformal provides, at the cost of requiring an accurate change-point detector and accepting a brief post-reset period with a small calibration set. A practical hybrid runs both: ACI's feedback loop handles slow between-regime drift, and CPTC's reset handles abrupt within-stream changes, with the reset clearing the ACI state at each detected change-point so the feedback does not carry stale coverage debt from the old regime into the new one. This hybrid is the state-of-the-art approach for financial and sensor series that exhibit both forms of non-stationarity simultaneously, and it connects directly to the change-point detection and continual learning methods of Chapter 20, where the same regime-aware adaptation principle governs model updates, concept drift detection, and online learning algorithm resets.

7. Exercises

These exercises move from the coverage arithmetic, through implementation of the adaptive update, to the open question of guarantees under dependence. Selected solutions appear in Appendix G.

Exercise 19.5.1 (Conceptual): The Finite-Sample Correction

Split conformal uses the rank $\lceil(n+1)(1-\alpha)\rceil$ rather than $\lceil n(1-\alpha)\rceil$. (a) For a calibration set of size $n = 99$ and $\alpha = 0.1$, compute the rank used by each formula and identify which sorted score is selected. (b) Explain, using the exchangeability argument of Section 1, why the test score is equally likely to fall in any of the $n+1$ gaps between sorted scores, and why this forces the $(n+1)$ rather than $n$. (c) State what happens to the interval, and to the coverage, as $\alpha \to 0$ with $n$ fixed, and explain why a very high confidence level may be unattainable from a small calibration set.

Exercise 19.5.2 (Implementation): Tune the ACI Step Size

Starting from Code 19.5.2, run adaptive conformal on the drifting stream for a grid of step sizes $\gamma \in \{0.005, 0.02, 0.05, 0.15\}$. For each, record (a) realized coverage and (b) the standard deviation of the per-step band width as a measure of jumpiness. Plot coverage and width-jumpiness against $\gamma$ and identify the value that best trades the two. Then replace the upward variance drift with an abrupt regime change (variance jumps from 1 to 3 at the midpoint) and report which $\gamma$ recovers coverage fastest after the jump, connecting your finding to the responsiveness-versus-stability dial of Section 3.

Exercise 19.5.3 (Open-ended): A Conformal PID Scorecaster

Plain ACI reacts only after a miss has occurred. Extend Code 19.5.2 into a minimal conformal PID controller by adding (a) a proportional term on the instantaneous coverage error and (b) a derivative "scorecaster" that forecasts the next absolute residual from a short trailing window (a simple exponential moving average is enough). Compare the PID variant against plain ACI on the abrupt-regime-change stream from Exercise 19.5.2: does anticipation reduce the coverage dip immediately after the jump? Discuss what the scorecaster is implicitly assuming about the drift, and when that assumption would hurt rather than help, relating your answer to the drift-detection methods of Chapter 20.