Intro to rollcast

Giancarlo Vercellino

“All models are wrong, but some are useful.” – George E. P. Box, statistician and Box-Jenkins time-series coauthor

“Be approximately right rather than exactly wrong.” – John W. Tukey, statistician and pioneer of exploratory data analysis

“The purpose of computing is insight, not numbers.” – Richard W. Hamming, numerical analyst and Turing Award winner

What you can do with rollcast

rollcast builds probabilistic forecasts for one numeric time series by turning rolling statistics into forecast anchors, learning which anchors are useful in the current state, and simulating future paths from the resulting mixture.

Instead of asking one model to explain everything, rollcast keeps a small bench of candidate locations:

Then a proper-score gate assigns state-dependent weights, optional conditional residual sampling adds local dispersion, and recursive simulation turns the one-step mixture into marginal and joint predictive distributions.

The rollcast package works this way:

Five anchors walk into a forecast. The gate checks the room, turns the lights up on the useful ones, and lets the simulation take it from there.

1) Fit rollcast, aka anchor first, argue later

We will use a synthetic series with a gentle drift and enough noise to keep things honest. Scalar hyperparameters mean “use this value”; vectors mean “try these candidates with causal validation.”

library(rollcast)

set.seed(1)
y <- 100 + cumsum(rnorm(300, mean = 0.03, sd = 0.8))

fit <- rollcast(
  y,
  window = 60,
  tau = 0.25,
  lambda = 0.01,
  conditional_k = 40,
  state_bw = 1,
  residual_bw = 0.35,
  error_scale = 0.25,
  residual_smoothing = 0.03,
  rho_min = 0.05,
  rho_max = 0.90,
  rho_decay = 1
)

fit

The printed model shows the selected anchors, gate settings, adaptive persistence range, and current anchor probabilities. That last part is the quick read: which rolling summaries are currently getting the most probability.

2) Peek at the anchor mix

The current probabilities live on the fitted object. A simple sort gives a small dashboard for the latest state.

sort(fit$current$probabilities, decreasing = TRUE)

The anchor names are deliberately plain. If regression_forecast is high, the recent direction is doing work. If median or inner quantiles dominate, the series is behaving more like a stable level. If extremes get weight, the model is seeing a state where boundary anchors helped historically.

3) Forecast paths, not just a line

predict() recursively simulates a predictive mixture. The summary gives means, medians, standard deviations, and requested quantiles by horizon.

pred <- predict(
  fit,
  horizon = 20,
  nsim = 3000,
  seed = 123
)

head(pred$summary)

Forecasts should come with distribution handles, so the prediction object also returns four small functions:

pred$dfun(100, h = 1)
pred$pfun(100, h = 1)
pred$qfun(c(0.05, 0.50, 0.95), h = 1)
pred$rfun(5, h = 1)

Calling rfun() without h returns coherent recursive paths rather than independent marginal draws:

paths <- pred$rfun(100)
dim(paths)

That is the difference between “twenty separate one-step stories” and “one hundred possible futures that know their own history.”

4) Try fixed mode versus search mode

The interface has one main rule:

scalar = fixed; vector = search.

For a fast fixed run, give scalar values throughout:

fixed <- rollcast(
  y,
  window = 60,
  tau = 0.25,
  lambda = 0.01,
  conditional_k = 40,
  state_bw = 1,
  residual_bw = 0.35,
  error_scale = 0.25,
  residual_smoothing = 0.03,
  rho_min = 0.05,
  rho_max = 0.90,
  rho_decay = 1
)

To let the model choose among a few sensible options, pass vectors:

tuned <- rollcast(
  y,
  window = c(40, 60, 90),
  tau = c(0.15, 0.25, 0.40),
  lambda = c(0.001, 0.01, 0.05),
  conditional_k = 40,
  state_bw = 1,
  residual_bw = c(0.20, 0.35, 0.55),
  error_scale = c(0, 0.25, 0.50),
  residual_smoothing = 0.03,
  rho_min = 0.05,
  rho_max = 0.90,
  rho_decay = 1,
  verbose = TRUE
)

tuned$hyperparameter_search

The search is a cached coordinate search on common causal validation origins, not a full Cartesian grid. Translation: it tries to be useful without turning your check run into a long lunch.

5) Residuals: pure anchors or local texture

Set error_scale = 0 for a pure weighted-anchor forecast:

anchor_only <- rollcast(
  y,
  window = 60,
  tau = 0.25,
  lambda = 0.01,
  conditional_k = 40,
  state_bw = 1,
  residual_bw = 0.35,
  error_scale = 0,
  residual_smoothing = 0.03,
  rho_min = 0.05,
  rho_max = 0.90,
  rho_decay = 1
)

Use error_scale between zero and one when the anchors should keep their location role but local historical residuals should add shape around them. Zero is crisp. One is full residual correction. The middle is often where the forecast behaves like it has both a steering wheel and suspension.

6) Minimal plots, tiny but telling

The default plot is a compact diagnostic dashboard:

plot(pred)

Use the fan-only view when all you need is the forecast envelope:

plot(pred, type = "fan")

The diagnostic view combines four checks:

  1. predictive fan;
  2. absolute uncertainty width and transition horizon;
  3. adaptive gating persistence;
  4. median forecast drift from the last observation.

The transition calculation fits the simple segmented shape

\[ W_h = a + b_1 h + \Delta b (h-h^*)_+, \]

where \(W_h\) is a predictive interval width and \(h^*\) is the horizon where the rate of uncertainty expansion changes.

Conclusion

rollcast is a small forecasting control room. Rolling anchors suggest where the next value might land, the gate decides who deserves attention, residuals add local texture when requested, and recursive simulation turns the result into full predictive paths. Fit, inspect, simulate, plot. Then let the distribution do the talking.

Enzoi!