timesfm-forecasting

install
source · Clone the upstream repo
git clone https://github.com/google-research/timesfm
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/google-research/timesfm "$T" && mkdir -p ~/.claude/skills && cp -r "$T/timesfm-forecasting" ~/.claude/skills/google-research-timesfm-timesfm-forecasting && rm -rf "$T"
manifest: timesfm-forecasting/SKILL.md
source content

TimesFM Forecasting

Overview

TimesFM (Time Series Foundation Model) is a pretrained decoder-only foundation model developed by Google Research for time-series forecasting. It works zero-shot — feed it any univariate time series and it returns point forecasts with calibrated quantile prediction intervals, no training required.

This skill includes a mandatory preflight system checker that verifies RAM, GPU memory, and disk space before the model is ever loaded so the agent never crashes the user's machine.

Key numbers: TimesFM 2.5 uses 200M parameters (~800 MB on disk, ~1.5 GB in RAM on CPU, ~1 GB VRAM on GPU). The archived v1/v2 500M-parameter model needs ~32 GB RAM. Always run the system checker first.

When to Use This Skill

Use this skill when:

  • Forecasting any univariate time series (sales, demand, sensor, vitals, price, weather)
  • You need zero-shot forecasting without training a custom model
  • You want probabilistic forecasts with calibrated prediction intervals (quantiles)
  • You have time series of any length (the model handles 1–16,384 context points)
  • You need to batch-forecast hundreds or thousands of series efficiently
  • You want a foundation model approach instead of hand-tuning ARIMA/ETS parameters
  • You need covariate forecasting with exogenous variables (price, promotions, holidays, day-of-week effects) → use
    forecast_with_covariates()
    (TimesFM 2.5 +
    pip install timesfm[xreg]
    )

Do not use this skill when:

  • You need classical statistical models with coefficient interpretation → use
    statsmodels
  • You need time series classification or clustering → use
    aeon
  • You need multivariate vector autoregression or Granger causality → use
    statsmodels
  • Your data is tabular (not temporal) → use
    scikit-learn
  • You cannot install optional dependencies → XReg requires scikit-learn and JAX

Note on Anomaly Detection: TimesFM does not have built-in anomaly detection, but you can use the quantile forecasts as prediction intervals — values outside the 90% CI (q10–q90) are statistically unusual. See

examples/anomaly-detection/
for a full example.

⚠️ Mandatory Preflight: System Requirements Check

CRITICAL — ALWAYS run the system checker before loading the model for the first time.

python scripts/check_system.py

This script checks:

  1. Available RAM — warns if below 4 GB, blocks if below 2 GB
  2. GPU availability — detects CUDA/MPS devices and VRAM
  3. Disk space — verifies room for the ~800 MB model download
  4. Python version — requires 3.10+
  5. Existing installation — checks if
    timesfm
    and
    torch
    are installed

Note: Model weights are NOT stored in this repository. TimesFM weights (~800 MB) download on-demand from HuggingFace on first use and cache in

~/.cache/huggingface/
.

flowchart TD
    start["🚀 Run check_system.py"] --> ram{"RAM ≥ 4 GB?"}
    ram -->|"Yes"| gpu{"GPU available?"}
    ram -->|"No (2-4 GB)"| warn_ram["⚠️ Warning: tight RAM<br/>CPU-only, small batches"]
    ram -->|"No (< 2 GB)"| block["🛑 BLOCKED<br/>Insufficient memory"]
    warn_ram --> disk
    gpu -->|"CUDA / MPS"| vram{"VRAM ≥ 2 GB?"}
    gpu -->|"CPU only"| cpu_ok["✅ CPU mode<br/>Slower but works"]
    vram -->|"Yes"| gpu_ok["✅ GPU mode<br/>Fast inference"]
    vram -->|"No"| cpu_ok
    gpu_ok --> disk{"Disk ≥ 2 GB free?"}
    cpu_ok --> disk
    disk -->|"Yes"| ready["✅ READY<br/>Safe to load model"]
    disk -->|"No"| block_disk["🛑 BLOCKED<br/>Need space for weights"]

Dataset Preflight (NEW)

Before loading your actual data, verify it will fit in memory:

# Quick estimate for your dataset
python scripts/check_system.py \
  --num-series 1000 \
  --context-length 1024 \
  --horizon 24 \
  --batch-size 32 \
  --estimate-only

This will show you the estimated memory requirements and warn if your dataset is too large.

Memory Estimation Formula:

RAM ≈ 0.8 GB (model) + 0.5 GB (overhead) + (0.2 MB × num_series × context_length / 1000)

Example Outputs:

Dataset Fits:

Total CPU memory: 2.34 GB
Total GPU memory: 2.15 GB

⚠️ Dataset Too Large:

Dataset requires ~12.5 GB RAM but system has 8.0 GB.
Try: context_length=512 or process in chunks of 50 series.

Hardware Requirements by Model Version

ModelParametersRAM (CPU)VRAM (GPU)DiskContext
TimesFM 2.5 (recommended)200M≥ 4 GB≥ 2 GB~800 MBup to 16,384
TimesFM 2.0 (archived)500M≥ 16 GB≥ 8 GB~2 GBup to 2,048
TimesFM 1.0 (archived)200M≥ 8 GB≥ 4 GB~800 MBup to 2,048

Recommendation: Always use TimesFM 2.5 unless you have a specific reason to use an older checkpoint. It is smaller, faster, and supports 8× longer context.

🔧 Installation

Step 1: Verify System (always first)

python scripts/check_system.py

Step 2: Install TimesFM

# Using uv (fast)
uv pip install timesfm[torch]

# Or using pip
pip install timesfm[torch]

# For JAX/Flax backend (faster on TPU/GPU)
uv pip install timesfm[flax]

Step 3: Install PyTorch for Your Hardware

# CUDA 12.1 (NVIDIA GPU)
pip install torch>=2.0.0 --index-url https://download.pytorch.org/whl/cu121

# CPU only
pip install torch>=2.0.0 --index-url https://download.pytorch.org/whl/cpu

# Apple Silicon (MPS)
pip install torch>=2.0.0  # MPS support is built-in

🎯 Quick Start

Minimal Example

import torch, numpy as np, timesfm

torch.set_float32_matmul_precision("high")

model = timesfm.TimesFM_2p5_200M_torch.from_pretrained(
    "google/timesfm-2.5-200m-pytorch"
)
model.compile(timesfm.ForecastConfig(
    max_context=1024, max_horizon=256, normalize_inputs=True,
    use_continuous_quantile_head=True, force_flip_invariance=True,
    infer_is_positive=True, fix_quantile_crossing=True,
))

point, quantiles = model.forecast(horizon=24, inputs=[
    np.sin(np.linspace(0, 20, 200)),  # any 1-D array
])
# point.shape == (1, 24)         — median forecast
# quantiles.shape == (1, 24, 10) — 10th–90th percentile bands

Forecast with Covariates (XReg)

TimesFM 2.5+ supports exogenous variables through

forecast_with_covariates()
. Requires
pip install timesfm[xreg]
.

point, quantiles = model.forecast_with_covariates(
    inputs=inputs,
    dynamic_numerical_covariates={"price": price_arrays},
    dynamic_categorical_covariates={"holiday": holiday_arrays},
    static_categorical_covariates={"region": region_labels},
    xreg_mode="xreg + timesfm",  # or "timesfm + xreg"
)

Anomaly Detection (via Quantile Intervals)

point, q = model.forecast(horizon=H, inputs=[values])

lower_90 = q[0, :, 1]  # 10th percentile
upper_90 = q[0, :, 9]  # 90th percentile

actual = test_values
anomalies = (actual < lower_90) | (actual > upper_90)
SeverityConditionInterpretation
NormalInside 80% CIExpected behavior
WarningOutside 80% CIUnusual but possible
CriticalOutside 90% CIStatistically rare (< 10% probability)

See

examples/anomaly-detection/
for a complete worked example with visualization.

📊 Understanding the Output

TimesFM returns

(point_forecast, quantile_forecast)
:

  • point_forecast
    : shape
    (batch, horizon)
    — the median (0.5 quantile)
  • quantile_forecast
    : shape
    (batch, horizon, 10)
    — ten quantile slices:
IndexQuantileUse
0MeanAverage prediction
10.1Lower bound of 80% PI
20.2Lower bound of 60% PI
50.5Median (=
point_forecast
)
80.8Upper bound of 60% PI
90.9Upper bound of 80% PI
point, q = model.forecast(horizon=H, inputs=data)

lower_80 = q[:, :, 1]  # 10th percentile
upper_80 = q[:, :, 9]  # 90th percentile
median   = q[:, :, 5]

🔧 ForecastConfig Reference

All forecasting behavior is controlled by

timesfm.ForecastConfig
:

timesfm.ForecastConfig(
    max_context=1024,                    # Max context window
    max_horizon=256,                     # Max forecast horizon
    normalize_inputs=True,               # RECOMMENDED — prevents scale instability
    per_core_batch_size=32,              # Tune for memory
    use_continuous_quantile_head=True,   # Better quantile accuracy for long horizons
    force_flip_invariance=True,          # Ensures f(-x) = -f(x)
    infer_is_positive=True,              # Clamp forecasts ≥ 0 when all inputs > 0
    fix_quantile_crossing=True,          # Ensure q10 ≤ q20 ≤ ... ≤ q90
    return_backcast=False,               # Return backcast (for covariate workflows)
)
ParameterDefaultWhen to Change
max_context
0Set to match your longest historical window
normalize_inputs
FalseAlways set True
use_continuous_quantile_head
FalseSet True for calibrated PIs
infer_is_positive
TrueSet False for series that can be negative
fix_quantile_crossing
FalseSet True for monotonic quantiles

See

references/api_reference.md
for the complete parameter reference.

📋 Common Workflows

Single Series Forecast

import torch, numpy as np, pandas as pd, timesfm, matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

torch.set_float32_matmul_precision("high")
model = timesfm.TimesFM_2p5_200M_torch.from_pretrained(
    "google/timesfm-2.5-200m-pytorch"
)
model.compile(timesfm.ForecastConfig(
    max_context=512, max_horizon=52, normalize_inputs=True,
    use_continuous_quantile_head=True, fix_quantile_crossing=True,
))

df = pd.read_csv("weekly_demand.csv", parse_dates=["week"])
values = df["demand"].values.astype(np.float32)

point, quantiles = model.forecast(horizon=52, inputs=[values])

fig, ax = plt.subplots(figsize=(12, 5))
ax.plot(values[-104:], label="Historical")
x_fc = range(len(values[-104:]), len(values[-104:]) + 52)
ax.plot(x_fc, point[0], label="Forecast", color="tab:orange")
ax.fill_between(x_fc, quantiles[0, :, 1], quantiles[0, :, 9],
                alpha=0.2, color="tab:orange", label="80% PI")
ax.legend(); ax.set_title("52-Week Demand Forecast")
plt.tight_layout(); plt.savefig("forecast.png", dpi=150)

Batch Forecasting (Many Series)

df = pd.read_csv("all_stores.csv", parse_dates=["date"], index_col="date")
inputs = [df[col].dropna().values.astype(np.float32) for col in df.columns]

point, quantiles = model.forecast(horizon=30, inputs=inputs)

import json
results = {col: {"forecast": point[i].tolist(),
                 "lower_80": quantiles[i, :, 1].tolist(),
                 "upper_80": quantiles[i, :, 9].tolist()}
           for i, col in enumerate(df.columns)}
with open("batch_forecasts.json", "w") as f:
    json.dump(results, f, indent=2)

Evaluate Forecast Accuracy

H = 24
train, actual = values[:-H], values[-H:]
point, quantiles = model.forecast(horizon=H, inputs=[train])
pred = point[0]

mae  = np.mean(np.abs(actual - pred))
rmse = np.sqrt(np.mean((actual - pred) ** 2))
mape = np.mean(np.abs((actual - pred) / actual)) * 100
coverage = np.mean((actual >= quantiles[0, :, 1]) & (actual <= quantiles[0, :, 9])) * 100

print(f"MAE: {mae:.2f} | RMSE: {rmse:.2f} | MAPE: {mape:.1f}% | 80% PI Coverage: {coverage:.1f}%")

⚙️ Performance Tuning

# Always set on Ampere+ GPUs (A100, RTX 3090+)
torch.set_float32_matmul_precision("high")

# Batch size guidelines:
# GPU 8 GB VRAM:  per_core_batch_size=64
# GPU 16 GB VRAM: per_core_batch_size=128
# CPU 8 GB RAM:   per_core_batch_size=8
# CPU 16 GB RAM:  per_core_batch_size=32

# Memory-constrained: process in chunks
CHUNK = 50
results = []
for i in range(0, len(inputs), CHUNK):
    p, q = model.forecast(horizon=H, inputs=inputs[i:i+CHUNK])
    results.append((p, q))

📚 Available Scripts

scripts/check_system.py

Mandatory preflight checker — run before first model load. Now includes dataset-aware memory estimation to prevent OOM errors before loading your data.

# Basic system check
python scripts/check_system.py

# Check if your specific dataset will fit
python scripts/check_system.py \
  --num-series 1000 \
  --context-length 1024 \
  --horizon 24 \
  --batch-size 32

# Quick memory estimate without system checks
python scripts/check_system.py \
  --num-series 5000 \
  --context-length 2048 \
  --estimate-only

What it checks:

  1. Available RAM — warns if below 4 GB, blocks if below 2 GB
  2. GPU availability — detects CUDA/MPS devices and VRAM
  3. Disk space — verifies room for the ~800 MB model download
  4. Python version — requires 3.10+
  5. Existing installation — checks if
    timesfm
    and
    torch
    are installed
  6. Dataset fit (NEW) — estimates memory for your specific dataset and warns if it won't fit

scripts/forecast_csv.py

End-to-end CSV forecasting CLI.

python scripts/forecast_csv.py input.csv \
    --horizon 24 \
    --date-col date \
    --value-cols sales,revenue \
    --output forecasts.csv

📖 Reference Documentation

FileContents
references/system_requirements.md
Hardware tiers, GPU/CPU selection, memory estimation
references/api_reference.md
Full
ForecastConfig
docs, output shapes, model options
references/data_preparation.md
Input formats, NaN handling, CSV loading, covariate setup

🧪 Examples

ExampleDirectoryWhat It Demonstrates
Global Temperature Forecast
examples/global-temperature/
Basic
model.forecast()
, CSV → PNG → GIF pipeline
Anomaly Detection
examples/anomaly-detection/
Two-phase detrend + Z-score + quantile PI, 2-panel viz
Covariates (XReg)
examples/covariates-forecasting/
forecast_with_covariates()
, 2×2 shared-axis viz
# Run all three examples:
cd examples/global-temperature && python run_forecast.py && python visualize_forecast.py
cd examples/anomaly-detection  && python detect_anomalies.py
cd examples/covariates-forecasting && python demo_covariates.py

Expected Outputs

ExampleKey output filesAcceptance criteria
global-temperature
output/forecast_output.json
,
output/forecast_visualization.png
point_forecast
has 12 values; PNG shows context + forecast + PI bands
anomaly-detection
output/anomaly_detection.json
,
output/anomaly_detection.png
Sep 2023 flagged CRITICAL (z ≥ 3.0)
covariates-forecasting
output/sales_with_covariates.csv
,
output/covariates_data.png
108 rows (3 stores × 36 weeks); distinct price arrays per store

Model Versions

VersionParamsContextStatusHuggingFace checkpoint
2.5200M16,384Latest
google/timesfm-2.5-200m-pytorch
2.0500M2,048Archived
google/timesfm-2.0-500m-pytorch
1.0200M2,048Archived
google/timesfm-1.0-200m-pytorch
  • TimesFM 1.0/2.0: must pass
    freq=[0]
    for monthly data
  • TimesFM 2.5: no frequency flag — it was removed

Resources

Quality Checklist

Run after every TimesFM task before declaring success:

  • Output shape
    point_fc
    is
    (n_series, horizon)
    ,
    quant_fc
    is
    (n_series, horizon, 10)
  • Quantile indices — index 0 = mean, 1 = q10 ... 9 = q90. NOT 0 = q0.
  • Frequency flag — TimesFM 1.0/2.0: pass
    freq=[0]
    for monthly. TimesFM 2.5: omit.
  • Series length — context must be ≥ 32 data points.
  • No NaN
    np.isnan(point_fc).any()
    must be False.
  • Axes — multiple panels sharing data must use
    sharex=True
    .
  • matplotlib.use('Agg')
    — before any pyplot import when running headless.
  • infer_is_positive
    — set False for temperature, financial returns, negatives.

Common Mistakes

  1. Quantile index off-by-one

    quant_fc[..., 0]
    is the mean, not q0. q10 = index 1, q90 = index 9. Define:
    IDX_Q10, IDX_Q90 = 1, 9
    .

  2. Variable shadowing in covariate loops — don't use the outer loop variable as a comprehension variable when building per-series covariate dicts.

  3. Wrong CSV column name — global-temperature CSV uses

    anomaly_c
    , not
    anomaly
    . Print
    df.columns
    first.

  4. TimesFM 2.5 required for

    forecast_with_covariates()
    — TimesFM 1.0 does NOT have this method.

  5. Future covariates must span the full horizon — dynamic covariates need values for BOTH context AND forecast windows.

  6. Context anomaly detection uses residuals — detrend first, then Z-score. Raw Z-scores mislead on trending data.

Validation & Verification

# Anomaly detection regression:
python -c "
import json
d = json.load(open('examples/anomaly-detection/output/anomaly_detection.json'))
assert d['context_summary']['critical'] >= 1, 'Sep 2023 must be CRITICAL'
print('Anomaly detection: PASS')"

# Covariates regression:
python -c "
import pandas as pd
df = pd.read_csv('examples/covariates-forecasting/output/sales_with_covariates.csv')
assert len(df) == 108, f'Expected 108 rows, got {len(df)}'
print('Covariates: PASS')"