---
title: "Intro to rollcast"
author: "Giancarlo Vercellino"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Intro to rollcast}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE
)
```

> "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:

- rolling mean;
- rolling median;
- rolling minimum and maximum;
- rolling linear-regression endpoint;
- user-chosen rolling quantiles.

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:

- **anchor dictionary**: rolling summaries define plausible forecast locations.
- **state features**: anchors are compared with the latest level after robust
  scaling.
- **soft responsibilities**: recent outcomes tell the model which anchors were
  close, without forcing a winner-takes-all label.
- **proper-score gate**: a softmax model is trained by predictive log score,
  so the target is probabilistic calibration.
- **conditional residuals**: nearby historical states can donate residual
  shapes when `error_scale > 0`.
- **adaptive persistence**: anchor weights can move slowly in stable regimes
  and faster when the state shifts.
- **recursive mixture simulation**: every particle and every anchor contributes
  before resampling, so uncertainty can widen naturally with horizon.

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."

```{r}
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.

```{r}
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.

```{r}
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:

```{r}
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:

```{r}
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:

```{r}
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:

```{r}
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:

```{r}
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:

```{r}
plot(pred)
```

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

```{r}
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!
