Skip to main content
Four of the six methods keep learning after calibration. This page is the loop they learn in: predict, observe, update, repeat — plus what goes wrong when you skip a step.

Which methods are online

IS_ONLINE is a ClassVar — class metadata rather than a config field, so you can ask before you build anything, and it never shows up in a constructor or in model_json_schema().
Offline methods still have an update method. It lives on the fitted object that calibrate returns, and delegates to an _update hook, which the offline fitted classes leave as the inherited no-op and the four online ones override. Calling it on an offline method is harmless, which means a generic pipeline can call update unconditionally without branching on the method type.

The loop

calibrate_cv returns a fitted object, and that object is what learns: each update mutates it in place and returns None.
truth must have the forecast’s shape update takes (prediction, truth) with both shaped (n_series, 1, horizon) — the same 3-D Forecast shape predict returns, not the flat (n_series,) slice you took out of your panel. The panel[:, t : t + 1][:, None, :] above is that reshape: one series axis, one sample, one horizon step.
Snapshots Because update mutates the fitted object, keep a copy if you need the state from before a stream — say, to replay a different stream from the same starting point: snapshot = fitted.model_copy(deep=True). The copy shares nothing with the original, so later updates never reach it.

What updating buys

Run the same stream several ways: three online methods, one online method that is calibrated but never fed, and split conformal for reference.
The second row is the one to look at, and it is not the failure mode most people expect. A never-updated ACI does not degrade gracefully into split conformal. It freezes at whatever alpha_t the calibration pass happened to leave behind — 0.016 here, not the 0.1 that was asked for — and then holds that forever. The result is a 100% covering band, 40% wider than split conformal, aimed at a confidence level nobody chose. So an online method that is not fed is worse than an offline one: it is a fixed interval at an arbitrary level, carrying the overhead of a controller that never runs. If your pipeline cannot deliver truths back, pick an offline method deliberately rather than inheriting one by accident.

Online state survives calibration

calibrate_cv does not merely collect residuals for an online method — it rolls the method’s state forward through every calibration window. By the time the stream starts, alpha_t has already moved.
This is why n_observations after the 48-month stream above reads 96 rather than 48, and why an online method’s diagnostics are cumulative across both phases.

Updating out of order

update assumes the truth you hand it is the one that followed the prediction you hand it. Nothing checks that — the arrays have no timestamps — so an out-of-order or duplicated update corrupts the state silently.
Three rules for a production loop
  1. Update once per observation. Replaying the same truth twice tells the controller it missed (or hit) twice.
  2. Update in time order. NexCP weights by position and SPCI regresses on a residual sequence; both read the order as time.
  3. Pair the truth with the prediction that preceded it, not with the newest forecast. Passing prediction.point straight through, as above, makes this automatic.

Next

  • ACI — the controller, in detail.
  • AgACI — several controllers, aggregated.
  • Diagnostics — rolling_coverage and the per-method state helpers, including method_state used above.