X is a separate block handed
to the calibration, letting interval width vary with the step being
predicted rather than resting only on the size of recent errors.
This page threads a feature block through calibration and prediction,
and shows what SPCI does with it.
The shape
X is 4-D: (n_series, n_windows | n_samples, horizon, n_features).
One feature vector per predicted step, not per observation. If you
are forecasting 12 months ahead with 3 features, the last two axes are
(12, 3): the features that describe each of those twelve future
months.
What is supported in v0.1 Exogenous support is narrower than the array shape suggests:So feeding
calibrate_cv(X=...)always raisesNotImplementedError. Cross-validated calibration with exogenous features is not implemented. Use the explicitcalibrate(forecaster, histories, truths, X=...)loop, which is what this page does.- CQR rejects
Xentirely — bothcalibrate(X=...)andpredict(X=...)raise, because the quantile path has nowhere to put it.- All three Nixtla adapters carry
Xthroughpredict, given atrain_dfholding the regressors’ history. StatsForecast and MLForecast opt in withexog_features=[...]; NeuralForecast reads the list off the model’sfutr_exog_list, since a model built that way requires those values. But that is the forecaster using its own features, which is a different thing from the calibration seeingX. It is not a lesser thing: a model that reads its regressors makes sharper point forecasts, which shrinks the calibration residuals and so the interval, for every method. What it does not give you is width that varies with the step being predicted.refitand quantile methods are unavailable on an exogenous adapter. A history panel is target-only, so a refit would drop the regressors, andpredict_quantileshas noXin its contract to feed them.- SPCI reads
Xatcalibratetime, not atpredicttime. Itsinterval()ignores theXyou pass and queries with the most recent calibration row instead, so future features shape the regressors it fits, not the query it answers.Xto the calibration still meansCallableAdapter. That is what the rest of this page uses.
Features from the calendar
AirPassengers carries no exogenous variables, so build them from its own index: a trend and two Fourier pairs for the yearly cycle. These are known in advance, which is what makes them legitimate: at prediction time you can state the features of a month that has not happened yet.A forecaster that takes X
CallableAdapter wraps any function of the history. If the function
declares an X keyword, the adapter forwards the exogenous block to it;
if it does not, X is ignored. That is the whole contract.
Calibrating with X
The explicit loop needs three aligned things: a list of histories, the
truths that followed each one, and the features of those truths’
timestamps.
X into the feature vector of each cell’s quantile
regressor, alongside the previous window_size residuals. So the width
it predicts for a step can depend on which step it is — which month of
the year, how far into the trend — and not only on how large the recent
errors were.
That happens at fit time. The fitted object’s interval() discards
the X handed to it and queries with the most recent calibration row,
so the “with X” numbers below differ through the regressors it fitted
and the point forecast it was given — not through an interval query
conditioned on the future features.
Split conformal ignores X even when given it: its
score is a plain absolute residual with nowhere to put a feature.
Passing X to a method that cannot use it is not an error, and not
useful either.
Predicting with X
At prediction time the features describe the steps you are about to
forecast.

What the method actually gains
Held out over the last two years, with and without the feature block.evaluatedoes not threadXdiagnostics.evaluatetakes(fitted, histories, truths)and no exogenous argument, so an X-aware holdout has to be rolled by hand. It is six lines, and it also makes thepredict/updateorder explicit — which is worth seeing once.
X is
wider than SPCI without it (61.3 against 55.2) and covers worse (58.3%
against 62.5%), and both sit far under the 90% target while plain split
conformal — which cannot read X at all — covers best at 83.3%.
That is a real result, not a broken example, and it has two causes worth
separating:
- The features are redundant. A trend and two Fourier pairs describe where in the year each step falls. SPCI’s residual window already carries that information implicitly, because the last 24 residuals of a seasonal series encode the season. Giving a model a feature it can already infer adds variance, not signal. Exogenous features pay off when they carry information the residual history cannot contain — a scheduled promotion, a known outage, a holiday calendar that does not repeat annually.
- The horizon. SPCI under-covers at multi-step horizons on real
series; see the warning on the SPCI page for the
measurement and the reason. Both SPCI columns inherit that, with or
without
X.
X is a hypothesis about what drives your
uncertainty, and diagnostics is how you find out whether
it was right.
Next
- SPCI — the method that consumes
X, in full. - Adapters — writing your own, including the
Xkeyword. - Diagnostics — measuring whether the features earned their place.

