anofox-forecast 0.5.6

Time series forecasting library
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
# Model Selection Guide

Practical guidance for choosing the right forecasting model in `anofox-forecast`.

## Quick Reference Table

| Model | Best For | Trend | Seasonality | Speed | Use When... |
|-------|----------|-------|-------------|-------|-------------|
| **Naive** | Flat data | No | No | Instant | Baseline benchmark; data is a random walk |
| **HistoricAverage** | Stationary data | No | No | Instant | Mean-reverting series; another baseline |
| **WindowAverage** | Recent-level data | No | No | Instant | You want a smoothed recent average |
| **RandomWalkWithDrift** | Linear trend | Yes | No | Instant | Steady upward/downward drift |
| **SeasonalNaive** | Stable seasonality | No | Yes | Instant | Repeating pattern with little noise |
| **SeasonalWindowAverage** | Noisy seasonality | No | Yes | Instant | Seasonal pattern that benefits from averaging |
| **SES** | Level data | No | No | Fast | No trend or seasonality; adaptive smoothing |
| **HoltLinearTrend** | Trending data | Yes | No | Fast | Linear trend, no seasonality |
| **HoltLinearTrend (damped)** | Flattening trend | Yes | No | Fast | Trend that decays over the forecast horizon |
| **HoltWinters** | Trend + season | Yes | Yes | Fast | Both trend and seasonality present |
| **SeasonalES** | Per-slot smoothing | No | Yes | Fast | Independent seasonal slots (e.g., day-of-week) |
| **ETS** | General purpose | Configurable | Configurable | Fast | You know the error/trend/seasonal structure |
| **AutoETS** | General purpose | Auto | Auto | Medium | Let the library choose the best ETS spec |
| **ARIMA** | Autocorrelated data | Configurable | No | Medium | Known (p,d,q) order; correlated residuals |
| **SARIMA** | Seasonal + autocorrelation | Configurable | Yes | Medium | Known seasonal ARIMA orders |
| **AutoARIMA** | General purpose | Auto | Auto | Slow | Automatic ARIMA order selection |
| **Theta (STM)** | Competition-grade | Yes | Optional | Fast | M3-winning method; strong baseline |
| **OptimizedTheta (OTM)** | Optimized Theta | Yes | Optional | Medium | When STM defaults are not ideal |
| **DynamicTheta (DSTM/DOTM)** | Changing trends | Yes | Optional | Medium | Trend changes over time |
| **AutoTheta** | General purpose | Auto | Auto | Medium | Let the library pick the best Theta variant |
| **Croston** | Intermittent demand | No | No | Fast | Sparse demand with many zeros |
| **Croston SBA** | Intermittent demand | No | No | Fast | Bias-corrected Croston |
| **TSB** | Obsolescence risk | No | No | Fast | Intermittent demand that may go to zero permanently |
| **ADIDA** | Intermittent demand | No | No | Fast | Aggregate-disaggregate approach to sparse data |
| **IMAPA** | Intermittent demand | No | No | Medium | Most robust intermittent method (multi-aggregation) |
| **TBATS** | Complex seasonality | Yes | Multiple | Slow | Multiple seasonal periods; non-integer seasonality |
| **AutoTBATS** | Complex seasonality | Auto | Multiple | Very Slow | Automatic TBATS configuration selection |
| **MFLES** | Robust decomposition | Yes | Yes | Medium | Gradient-boosted decomposition; robust to outliers |
| **MSTLForecaster** | Multiple seasonality | Yes | Multiple | Medium | MSTL decomposition + trend forecasting |
| **GARCH** | Volatility | No | No | Medium | Financial data; variance changes over time |
| **AutoForecast** | Unknown data | Auto | Auto | Slow | No idea which model to use; compares families |
| **AutoEnsemble** | Best accuracy | Auto | Auto | Very Slow | Maximum accuracy; combines top models |
| **Ensemble** | Custom combination | Varies | Varies | Varies | You want to combine specific models |


## Decision Flowchart

```
Start: What does your data look like?
 |
 +-- Is demand intermittent (many zeros)?
 |    |
 |    +-- YES --> Is demand potentially obsolescent (trending to zero)?
 |    |            |
 |    |            +-- YES --> TSB
 |    |            +-- NO  --> Do you want maximum robustness?
 |    |                         |
 |    |                         +-- YES --> IMAPA
 |    |                         +-- NO  --> Croston SBA
 |    |
 |    +-- NO --> Continue below
 |
 +-- Does the data have seasonality?
 |    |
 |    +-- YES --> Multiple seasonal periods (e.g., hourly + daily)?
 |    |            |
 |    |            +-- YES --> Is the seasonality complex or non-integer?
 |    |            |            |
 |    |            |            +-- YES --> TBATS or AutoTBATS
 |    |            |            +-- NO  --> MSTLForecaster or MFLES
 |    |            |
 |    |            +-- NO (single period) --> Does it have a trend?
 |    |                                       |
 |    |                                       +-- YES --> AutoETS, HoltWinters, or AutoForecast
 |    |                                       +-- NO  --> SeasonalNaive, SeasonalES, or AutoETS
 |    |
 |    +-- NO --> Does the data have a trend?
 |               |
 |               +-- YES --> Is the trend linear and persistent?
 |               |            |
 |               |            +-- YES --> HoltLinearTrend or RandomWalkWithDrift
 |               |            +-- NO (decaying/changing) --> HoltLinearTrend(damped) or AutoETS
 |               |
 |               +-- NO --> Is there autocorrelation in residuals?
 |                           |
 |                           +-- YES --> ARIMA or AutoARIMA
 |                           +-- NO  --> SES or Naive
 |
 +-- Not sure / want best accuracy? --> AutoForecast or AutoEnsemble
```


## Model Families

### 1. Baseline Models

Simple methods that serve as benchmarks. Always compare your chosen model against these.

**Naive** -- Repeats the last observed value. Use as a lower bound for any forecasting task.

**HistoricAverage** -- Predicts the mean of all historical data. Good for stationary, mean-reverting series.

**WindowAverage** -- Predicts the mean of the last N observations. Smooths out recent noise.

**RandomWalkWithDrift** -- Last value plus a constant drift (average historical change). The go-to baseline for trending data.

**SeasonalNaive** -- Repeats the last complete seasonal cycle. The standard baseline for seasonal data.

**SeasonalWindowAverage** -- Averages same-season values across multiple cycles. Smooths noisy seasonal patterns compared to SeasonalNaive.

```rust
use anofox_forecast::models::baseline::{
    Naive, HistoricAverage, WindowAverage, RandomWalkWithDrift,
    SeasonalNaive, SeasonalWindowAverage,
};
use anofox_forecast::models::Forecaster;

// Flat baseline
let mut model = Naive::new();
model.fit(&ts).unwrap();

// Trending baseline
let mut model = RandomWalkWithDrift::new();
model.fit(&ts).unwrap();

// Seasonal baseline (period=12 for monthly data)
let mut model = SeasonalNaive::new(12);
model.fit(&ts).unwrap();

// Smoothed seasonal baseline (average last 3 years)
let mut model = SeasonalWindowAverage::new(12, 3);
model.fit(&ts).unwrap();

let forecast = model.predict(12).unwrap();
```

### 2. Exponential Smoothing (ETS Family)

Adaptive methods that weight recent observations more heavily.

**SES (SimpleExponentialSmoothing)** -- Single smoothing parameter (alpha). Use for data with no trend or seasonality. A fixed alpha gives you control; `SES::optimized()` finds the best alpha automatically.

**HoltLinearTrend** -- Adds a trend component (alpha + beta). Use `.damped()` when the trend should flatten over the forecast horizon -- this is often the safest default for trending data.

**HoltWinters** -- Full triple exponential smoothing (alpha + beta + gamma). Choose additive seasonality when seasonal swings are constant in absolute terms; choose multiplicative when they scale with the level.

**SeasonalES** -- Applies SES independently to each seasonal slot. Different from Holt-Winters: it does not model trend, but captures per-slot dynamics well.

**ETS** -- The full state-space framework with 30 possible model specifications (Error, Trend, Seasonal). Use when you know the structure: `ETSSpec::ann()` for SES, `ETSSpec::aaa()` for additive Holt-Winters, `ETSSpec::mam()` for multiplicative, etc.

**AutoETS** -- Automatically selects the best ETS specification using information criteria (AICc by default). This is the recommended starting point for most forecasting tasks.

```rust
use anofox_forecast::models::exponential::{
    SimpleExponentialSmoothing, HoltLinearTrend, HoltWinters, SeasonalType,
    ETS, ETSSpec, AutoETS,
};

// Simple exponential smoothing with optimized alpha
let mut model = SimpleExponentialSmoothing::optimized();
model.fit(&ts).unwrap();

// Damped trend (safe default for trending data)
let mut model = HoltLinearTrend::auto_damped();
model.fit(&ts).unwrap();

// Holt-Winters with multiplicative seasonality
let mut model = HoltWinters::auto(12, SeasonalType::Multiplicative);
model.fit(&ts).unwrap();

// Specific ETS model: ETS(A,Ad,M) = additive error, damped trend, multiplicative seasonal
let mut model = ETS::new(ETSSpec::new(
    anofox_forecast::models::exponential::ErrorType::Additive,
    anofox_forecast::models::exponential::TrendType::AdditiveDamped,
    anofox_forecast::models::exponential::ETSSeasonalType::Multiplicative,
), 12);
model.fit(&ts).unwrap();

// Automatic ETS selection (recommended starting point)
let mut model = AutoETS::with_period(12);
model.fit(&ts).unwrap();

let forecast = model.predict_with_intervals(12, 0.95).unwrap();
```

**Parameter guidance:**
- Alpha (level): Higher values (0.5-0.9) react quickly to changes; lower values (0.01-0.2) produce smoother forecasts.
- Beta (trend): Keep lower than alpha. Values above 0.3 rarely help.
- Gamma (seasonal): Typically 0.01-0.3. Higher values allow seasonal patterns to evolve quickly.
- Phi (damping): Range 0.8-1.0. Values near 0.98 provide mild damping. Use `auto_damped()` to optimize.

### 3. ARIMA Family

Best when the data has significant autocorrelation structure that ETS does not capture well.

**ARIMA** -- Specify (p, d, q) orders manually. Use d=1 for trending data (first differencing makes it stationary). AR terms (p) capture momentum; MA terms (q) capture shocks.

**SARIMA** -- Extends ARIMA with seasonal (P, D, Q)[s] components. Use when both non-seasonal and seasonal autocorrelation are present.

**AutoARIMA** -- Searches over (p, d, q) and optionally (P, D, Q)[s] orders to find the best fit by AIC. Supports stepwise search (fast) and exhaustive search (thorough). Use `with_true_stepwise()` for faster hill-climbing search.

```rust
use anofox_forecast::models::arima::{ARIMA, ARIMASpec, SARIMA, SARIMASpec, AutoARIMA};

// Manual ARIMA(1,1,1)
let mut model = ARIMA::new(ARIMASpec::new(1, 1, 1));
model.fit(&ts).unwrap();

// Seasonal ARIMA(1,1,1)(1,1,1)[12]
let mut model = SARIMA::new(SARIMASpec::new(1, 1, 1, 1, 1, 1, 12));
model.fit(&ts).unwrap();

// Automatic selection (non-seasonal)
let mut model = AutoARIMA::new();
model.fit(&ts).unwrap();

// Automatic selection (seasonal, with stepwise search)
let mut model = AutoARIMA::seasonal(12);
model.fit(&ts).unwrap();

let forecast = model.predict(12).unwrap();
```

**When ARIMA beats ETS:**
- Data has strong autocorrelation (e.g., AR(2) structure).
- Residuals from ETS show significant autocorrelation patterns.
- The data has been differenced and the differenced series is well-behaved.
- You need exogenous regressors (ARIMAX). Both ARIMA and SARIMA support exogenous variables.

### 4. Theta Family

The Theta method won the M3 forecasting competition. It decomposes the series into "theta lines" and combines SES with a linear trend.

**Theta (STM)** -- Standard Theta Model with fixed alpha=0.1 and theta=2.0. Fast and surprisingly effective.

**OptimizedTheta (OTM)** -- Optimizes both alpha and theta parameters. Better than STM when defaults are suboptimal.

**DynamicTheta (DSTM/DOTM)** -- Updates linear coefficients dynamically, adapting to changing trends. DOTM (dynamic + optimized) was a component of the M4 competition winner.

**AutoTheta** -- Fits all four variants (STM, OTM, DSTM, DOTM) and selects the best by in-sample MSE.

All Theta models support seasonal decomposition (multiplicative by default, matching statsforecast).

```rust
use anofox_forecast::models::theta::{
    Theta, OptimizedTheta, DynamicTheta, DynamicOptimizedTheta, AutoTheta,
};

// Standard Theta (fast, good default)
let mut model = Theta::new();
model.fit(&ts).unwrap();

// Seasonal Theta (multiplicative decomposition by default)
let mut model = Theta::seasonal(12);
model.fit(&ts).unwrap();

// Optimized Theta
let mut model = OptimizedTheta::new();
model.fit(&ts).unwrap();

// Auto Theta (recommended -- picks best variant)
let mut model = AutoTheta::new();
model.fit(&ts).unwrap();

// Seasonal Auto Theta
let mut model = AutoTheta::seasonal(12);
model.fit(&ts).unwrap();

let forecast = model.predict(12).unwrap();
```

**Strengths:** Excellent out-of-the-box performance; robust to noise; fast.
**Limitations:** Assumes a simple decomposition structure; may struggle with complex non-linear patterns.

### 5. Intermittent Demand Models

For time series where demand is sporadic -- many zero values interspersed with occasional non-zero demand. Standard models break down on this pattern.

**Croston (Classic)** -- Separately smooths demand sizes and inter-arrival intervals. The original intermittent demand method.

**Croston SBA** -- Syntetos-Boylan Approximation. Applies bias correction to classic Croston. Generally preferred over classic Croston.

**TSB (Teunter-Syntetos-Babai)** -- Models demand probability and demand size separately. Handles obsolescence: if demand probability trends to zero, forecasts naturally decline. Use when items may become obsolete.

**ADIDA** -- Aggregate-Disaggregate Intermittent Demand Approach. Aggregates the series to reduce intermittency, applies SES, then disaggregates. Simple and effective.

**IMAPA** -- Tests multiple aggregation levels and averages forecasts. Most robust intermittent demand method.

```rust
use anofox_forecast::models::intermittent::{Croston, CrostonVariant, TSB, ADIDA, IMAPA};

// Croston SBA (bias-corrected, recommended default)
let mut model = Croston::new().sba();
model.fit(&ts).unwrap();

// TSB for potential obsolescence
let mut model = TSB::new();
model.fit(&ts).unwrap();

// ADIDA with automatic aggregation
let mut model = ADIDA::new();
model.fit(&ts).unwrap();

// IMAPA (most robust)
let mut model = IMAPA::new();
model.fit(&ts).unwrap();

let forecast = model.predict(12).unwrap();
```

**How to detect intermittent demand:** If more than ~30% of observations are zero and non-zero demands are separated by irregular intervals, use an intermittent demand model.

### 6. TBATS

For time series with complex seasonal patterns that simpler models cannot handle.

**TBATS** handles:
- Multiple seasonal periods simultaneously (e.g., hourly data with daily + weekly patterns).
- Non-integer seasonality (e.g., 365.25 days/year).
- Box-Cox variance stabilization.
- ARMA errors for residual autocorrelation.

Uses Fourier (trigonometric) terms to represent seasonality, which is much more parsimonious than explicit seasonal states.

**AutoTBATS** automatically selects the best TBATS configuration by comparing models with/without Box-Cox, with/without trend, with/without damping, and different Fourier harmonic counts.

```rust
use anofox_forecast::models::tbats::{TBATS, AutoTBATS};

// Single seasonality (e.g., daily pattern in hourly data)
let mut model = TBATS::new(vec![24]);
model.fit(&ts).unwrap();

// Multiple seasonalities (e.g., daily + weekly in hourly data)
let mut model = TBATS::new(vec![24, 168]);
model.fit(&ts).unwrap();

// Automatic configuration selection
let mut model = AutoTBATS::new(vec![24, 168]);
model.fit(&ts).unwrap();

let forecast = model.predict(48).unwrap();
```

### 7. MFLES and MSTLForecaster

Decomposition-based approaches for complex data.

**MFLES (Median Fourier Linear Exponential Smoothing)** -- Gradient-boosted decomposition that iteratively fits median, Fourier (seasonal), linear trend, and exponential smoothing components. Robust to outliers. Supports exogenous regressors.

**MSTLForecaster** -- Uses MSTL (Multiple Seasonal-Trend decomposition using LOESS) to decompose the series, then forecasts each component separately. Supports multiple seasonal periods and configurable trend/seasonal forecast methods.

```rust
use anofox_forecast::models::MFLES;
use anofox_forecast::models::MSTLForecaster;

// MFLES with monthly seasonality
let mut model = MFLES::new(vec![12]);
model.fit(&ts).unwrap();

// MSTLForecaster with multiple seasonalities
let mut model = MSTLForecaster::new(vec![24, 168]);
model.fit(&ts).unwrap();

let forecast = model.predict(24).unwrap();
```

### 8. GARCH

For financial time series where volatility (variance) changes over time.

**GARCH(p,q)** models conditional variance. It does not forecast the level of the series -- it forecasts how volatile the series will be. Use `forecast_variance()` for analytical variance forecasts.

```rust
use anofox_forecast::models::GARCH;

let mut model = GARCH::new(1, 1);  // GARCH(1,1)
model.fit(&ts).unwrap();

// Forecast future variance
let variance_forecast = model.forecast_variance(10).unwrap();
```


## Automatic Selection

When you are unsure which model to use, the library offers three levels of automatic selection.

### AutoETS -- Best for: Single-family selection with ETS

Searches all valid ETS(Error, Trend, Seasonal) combinations and selects by AICc. Fast and reliable for most standard time series.

```rust
use anofox_forecast::models::exponential::AutoETS;

let mut model = AutoETS::with_period(12);
model.fit(&ts).unwrap();
```

### AutoARIMA -- Best for: Single-family selection with ARIMA

Searches ARIMA orders using stepwise or exhaustive search. Preferred when autocorrelation is the dominant feature.

```rust
use anofox_forecast::models::arima::AutoARIMA;

let mut model = AutoARIMA::seasonal(12);
model.fit(&ts).unwrap();
```

### AutoTheta -- Best for: Single-family selection with Theta

Compares STM, OTM, DSTM, and DOTM variants. Fast and effective, especially for competition-style forecasting.

```rust
use anofox_forecast::models::theta::AutoTheta;

let mut model = AutoTheta::seasonal(12);
model.fit(&ts).unwrap();
```

### AutoForecast -- Best for: Cross-family comparison

Fits AutoARIMA, AutoETS, and AutoTheta, then selects the single best model by in-sample MSE or cross-validation error. Use this when you have no prior knowledge about your data.

```rust
use anofox_forecast::models::auto_forecast::{AutoForecast, AutoForecastConfig, SelectionStrategy};

// Default: compares all three families by in-sample MSE
let mut model = AutoForecast::new();
model.fit(&ts).unwrap();
println!("Selected: {}", model.selected_model_name().unwrap());

// Seasonal, with cross-validation (more robust, slower)
let config = AutoForecastConfig::with_period(12)
    .with_selection(SelectionStrategy::CrossValidation);
let mut model = AutoForecast::with_config(config);
model.fit(&ts).unwrap();
```

### When to use which Auto model

| Scenario | Recommended |
|----------|-------------|
| Quick exploration, general data | `AutoForecast` |
| You suspect ETS will work well | `AutoETS` |
| Data has strong autocorrelation | `AutoARIMA` |
| Competition-style forecasting | `AutoTheta` |
| Maximum accuracy, cost is not an issue | `AutoEnsemble` |
| Cross-validation-based selection | `AutoForecast` with `SelectionStrategy::CrossValidation` |


## Ensemble Methods

Combining multiple models often produces more accurate and robust forecasts than any single model.

### Manual Ensemble

Combine any set of models with a chosen combination method.

```rust
use anofox_forecast::models::ensemble::{Ensemble, CombinationMethod};
use anofox_forecast::models::exponential::AutoETS;
use anofox_forecast::models::arima::AutoARIMA;
use anofox_forecast::models::theta::AutoTheta;

let ensemble = Ensemble::new(vec![
    Box::new(AutoETS::with_period(12)),
    Box::new(AutoARIMA::seasonal(12)),
    Box::new(AutoTheta::seasonal(12)),
]).with_method(CombinationMethod::WeightedMSE);
```

**Combination methods:**
- `Mean` -- Simple average. Most robust when you have no reason to prefer one model.
- `Median` -- Robust to outlier models. Good when one model might fail badly.
- `WeightedMSE` -- Weights models by inverse in-sample MSE. Gives more weight to better-fitting models.
- `Custom` -- Provide your own weights via `.with_weights(vec![...])`.

### AutoEnsemble

Automatically fits AutoARIMA, AutoETS, and AutoTheta, ranks by in-sample MSE, and combines the top-K models.

```rust
use anofox_forecast::models::ensemble::{AutoEnsemble, AutoEnsembleConfig};

// Default: top 3 models, weighted by inverse MSE
let mut model = AutoEnsemble::new();
model.fit(&ts).unwrap();

// Seasonal, top 2 models
let config = AutoEnsembleConfig::with_period(12).with_top_k(2);
let mut model = AutoEnsemble::with_config(config);
model.fit(&ts).unwrap();

let forecast = model.predict(12).unwrap();
```

**When ensembles help most:**
- You do not know which model family is best for your data.
- Forecast accuracy matters more than speed or interpretability.
- Models disagree on the forecast (high model uncertainty).

**When ensembles may not help:**
- One model clearly dominates (adding worse models hurts the average).
- Speed is critical (ensembles fit multiple models).
- You need interpretable model parameters.


## Common Patterns

### Pattern: Stationary data (no trend, no seasonality)
**Try:** Naive, SES, HistoricAverage
**Auto:** AutoETS (will likely select ETS(A,N,N) or ETS(M,N,N))

### Pattern: Linear trend, no seasonality
**Try:** RandomWalkWithDrift, HoltLinearTrend, HoltLinearTrend(damped)
**Auto:** AutoETS, AutoForecast

### Pattern: Monthly data with yearly seasonality
**Try:** SeasonalNaive, HoltWinters(period=12), AutoETS(period=12)
**Auto:** AutoForecast::seasonal(12)

### Pattern: Hourly data with daily + weekly seasonality
**Try:** TBATS(vec![24, 168]), MSTLForecaster(vec![24, 168]), MFLES(vec![24])
**Auto:** AutoTBATS(vec![24, 168])

### Pattern: Sparse/intermittent demand (many zeros)
**Try:** Croston SBA, TSB, IMAPA
**Do not use:** ETS, ARIMA, Theta (these assume continuous demand)

### Pattern: Financial returns / volatility
**Try:** GARCH(1,1) for variance forecasting
**Combine with:** ARIMA for level forecasting + GARCH for variance

### Pattern: Data with exogenous regressors
**Try:** ARIMA/SARIMA (supports ARIMAX), ETS (via AutoETS), Theta, MFLES, Naive
**Note:** Fit with a `TimeSeries` that has regressors attached, then use `predict_with_exog()`.

### Pattern: Short series (< 30 observations)
**Try:** Naive, SES, Theta (STM), RandomWalkWithDrift
**Avoid:** Complex models (TBATS, SARIMA with many parameters) -- they need more data to estimate reliably.

### Pattern: Unknown data, want best result
**Try:** AutoForecast or AutoEnsemble
**Validate with:** Cross-validation (`cross_validate` from `utils`)