timesfm-forecasting
git clone https://github.com/google-research/timesfm
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"
timesfm-forecasting/SKILL.mdTimesFM 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
(TimesFM 2.5 +forecast_with_covariates()
)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
for a full example.examples/anomaly-detection/
⚠️ 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:
- Available RAM — warns if below 4 GB, blocks if below 2 GB
- GPU availability — detects CUDA/MPS devices and VRAM
- Disk space — verifies room for the ~800 MB model download
- Python version — requires 3.10+
- Existing installation — checks if
andtimesfm
are installedtorch
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
| Model | Parameters | RAM (CPU) | VRAM (GPU) | Disk | Context |
|---|---|---|---|---|---|
| TimesFM 2.5 (recommended) | 200M | ≥ 4 GB | ≥ 2 GB | ~800 MB | up to 16,384 |
| TimesFM 2.0 (archived) | 500M | ≥ 16 GB | ≥ 8 GB | ~2 GB | up to 2,048 |
| TimesFM 1.0 (archived) | 200M | ≥ 8 GB | ≥ 4 GB | ~800 MB | up 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)
| Severity | Condition | Interpretation |
|---|---|---|
| Normal | Inside 80% CI | Expected behavior |
| Warning | Outside 80% CI | Unusual but possible |
| Critical | Outside 90% CI | Statistically rare (< 10% probability) |
See
for a complete worked example with visualization.examples/anomaly-detection/
📊 Understanding the Output
TimesFM returns
(point_forecast, quantile_forecast):
: shapepoint_forecast
— the median (0.5 quantile)(batch, horizon)
: shapequantile_forecast
— ten quantile slices:(batch, horizon, 10)
| Index | Quantile | Use |
|---|---|---|
| 0 | Mean | Average prediction |
| 1 | 0.1 | Lower bound of 80% PI |
| 2 | 0.2 | Lower bound of 60% PI |
| 5 | 0.5 | Median (= ) |
| 8 | 0.8 | Upper bound of 60% PI |
| 9 | 0.9 | Upper 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) )
| Parameter | Default | When to Change |
|---|---|---|
| 0 | Set to match your longest historical window |
| False | Always set True |
| False | Set True for calibrated PIs |
| True | Set False for series that can be negative |
| False | Set 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
scripts/check_system.pyMandatory 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:
- Available RAM — warns if below 4 GB, blocks if below 2 GB
- GPU availability — detects CUDA/MPS devices and VRAM
- Disk space — verifies room for the ~800 MB model download
- Python version — requires 3.10+
- Existing installation — checks if
andtimesfm
are installedtorch - Dataset fit (NEW) — estimates memory for your specific dataset and warns if it won't fit
scripts/forecast_csv.py
scripts/forecast_csv.pyEnd-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
| File | Contents |
|---|---|
| Hardware tiers, GPU/CPU selection, memory estimation |
| Full docs, output shapes, model options |
| Input formats, NaN handling, CSV loading, covariate setup |
🧪 Examples
| Example | Directory | What It Demonstrates |
|---|---|---|
| Global Temperature Forecast | | Basic , CSV → PNG → GIF pipeline |
| Anomaly Detection | | Two-phase detrend + Z-score + quantile PI, 2-panel viz |
| Covariates (XReg) | | , 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
| Example | Key output files | Acceptance criteria |
|---|---|---|
| global-temperature | , | has 12 values; PNG shows context + forecast + PI bands |
| anomaly-detection | , | Sep 2023 flagged CRITICAL (z ≥ 3.0) |
| covariates-forecasting | , | 108 rows (3 stores × 36 weeks); distinct price arrays per store |
Model Versions
| Version | Params | Context | Status | HuggingFace checkpoint |
|---|---|---|---|---|
| 2.5 | 200M | 16,384 | Latest | |
| 2.0 | 500M | 2,048 | Archived | |
| 1.0 | 200M | 2,048 | Archived | |
- TimesFM 1.0/2.0: must pass
for monthly datafreq=[0] - TimesFM 2.5: no frequency flag — it was removed
Resources
- Paper: A Decoder-Only Foundation Model for Time-Series Forecasting (ICML 2024)
- HuggingFace: https://huggingface.co/collections/google/timesfm-release-66e4be5fdb56e960c1e482a6
- Google Blog: https://research.google/blog/a-decoder-only-foundation-model-for-time-series-forecasting/
- BigQuery Integration: https://cloud.google.com/bigquery/docs/timesfm-model
Quality Checklist
Run after every TimesFM task before declaring success:
- Output shape —
ispoint_fc
,(n_series, horizon)
isquant_fc(n_series, horizon, 10) - Quantile indices — index 0 = mean, 1 = q10 ... 9 = q90. NOT 0 = q0.
- Frequency flag — TimesFM 1.0/2.0: pass
for monthly. TimesFM 2.5: omit.freq=[0] - Series length — context must be ≥ 32 data points.
- No NaN —
must be False.np.isnan(point_fc).any() - Axes — multiple panels sharing data must use
.sharex=True -
— before any pyplot import when running headless.matplotlib.use('Agg') -
— set False for temperature, financial returns, negatives.infer_is_positive
Common Mistakes
-
Quantile index off-by-one —
is the mean, not q0. q10 = index 1, q90 = index 9. Define:quant_fc[..., 0]
.IDX_Q10, IDX_Q90 = 1, 9 -
Variable shadowing in covariate loops — don't use the outer loop variable as a comprehension variable when building per-series covariate dicts.
-
Wrong CSV column name — global-temperature CSV uses
, notanomaly_canomaly
first.df.columns -
TimesFM 2.5 required for
— TimesFM 1.0 does NOT have this method.forecast_with_covariates() -
Future covariates must span the full horizon — dynamic covariates need values for BOTH context AND forecast windows.
-
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')"