Zero-shot time series forecasting with Google's TimesFM foundation model. Use this skill when forecasting ANY univariate time series — sales, sensor readings, stock prices, energy demand, patient vitals, weather, or scientific measurements — without training a custom model. Automatically checks system RAM/GPU before loading the model, supports CSV/DataFrame/array inputs, and returns point forecasts with calibrated prediction intervals. Includes a preflight system checker script that MUST be run before first use to verify the machine can load the model. For classical statistical time series models (ARIMA, SARIMAX, VAR) use statsmodels; for time series classification/clustering use aeon.
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Zero-shot time series forecasting with Google's TimesFM foundation model. Use this skill when forecasting ANY univariate time series — sales, sensor readings, stock prices, energy demand, patient vitals, weather, or scientific measurements — without training a custom model. Automatically checks system RAM/GPU before loading the model, supports CSV/DataFrame/array inputs, and returns point forecasts with calibrated prediction intervals. Includes a preflight system checker script that MUST be run before first use to verify the machine can load the model. For classical statistical time series models (ARIMA, SARIMAX, VAR) use statsmodels; for time series classification/clustering use aeon.
allowed-tools
Read Write Edit Bash
license
Apache-2.0 license
metadata
{"skill-author":"Clayton Young / Superior Byte Works, LLC (@borealBytes)","skill-version":"1.0.0"}
TimesFM Forecasting
Routing Boundary
Use this skill only for TimesFM, zero-shot forecasting, foundation forecasting, forecast horizons, prediction intervals, or TimesFM-specific time-series pipelines. Do not use it for generic business forecasting, ARIMA baselines, tabular regression, ordinary scikit-learn modeling, or exploratory time-series analysis without TimesFM/foundation-model signals.
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 wraps TimesFM for safe, agent-friendly local inference. It 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 a 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
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
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 the examples/anomaly-detection/ directory 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:
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 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/.
The preflight checker ensures sufficient resources before any download begins.
flowchart TD
accTitle: Preflight System Check
accDescr: Decision flowchart showing the system requirement checks that must pass before loading TimesFM.
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"]
classDef ok fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d
classDef warn fill:#fef9c3,stroke:#ca8a04,stroke-width:2px,color:#713f12
classDef block fill:#fee2e2,stroke:#dc2626,stroke-width:2px,color:#7f1d1d
classDef neutral fill:#f3f4f6,stroke:#6b7280,stroke-width:2px,color:#1f2937
class ready,gpu_ok,cpu_ok ok
class warn_ram warn
class block,block_disk block
class start,ram,gpu,vram,disk neutral
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 (recommended by this repo)
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
flowchart LR
accTitle: Quantile Forecast Anatomy
accDescr: Diagram showing how the 10-element quantile vector maps to prediction intervals.
input["📈 Input Series<br/>1-D array"] --> model["🤖 TimesFM<br/>compile + forecast"]
model --> point["📍 Point Forecast<br/>(batch, horizon)"]
model --> quant["📊 Quantile Forecast<br/>(batch, horizon, 10)"]
quant --> pi80["80% PI<br/>q[:,:,1] – q[:,:,9]"]
quant --> pi60["60% PI<br/>q[:,:,2] – q[:,:,8]"]
quant --> median["Median<br/>q[:,:,5]"]
classDef data fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
classDef model fill:#f3e8ff,stroke:#9333ea,stroke-width:2px,color:#581c87
classDef output fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d
class input data
class model model
class point,quant,pi80,pi60,median output
🔧 ForecastConfig Reference
All forecasting behavior is controlled by timesfm.ForecastConfig:
timesfm.ForecastConfig(
max_context=1024, # Max context window (truncates longer series)
max_horizon=256, # Max forecast horizon
normalize_inputs=True, # Normalize inputs (RECOMMENDED for stability)
per_core_batch_size=32, # Batch size per device (tune for memory)
use_continuous_quantile_head=True, # Better quantile accuracy for long horizons
force_flip_invariance=True, # Ensures f(-x) = -f(x) (mathematical consistency)
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
max_context
0
Set to match your longest historical window (e.g., 512, 1024, 4096)
max_horizon
0
Set to your maximum forecast length
normalize_inputs
False
Always set True — prevents scale-dependent instability
per_core_batch_size
1
Increase for throughput; decrease if OOM
use_continuous_quantile_head
False
Set True for calibrated prediction intervals
force_flip_invariance
True
Keep True unless profiling shows it hurts
infer_is_positive
True
Set False for series that can be negative (temperature, returns)
fix_quantile_crossing
False
Set True to guarantee monotonic quantiles
📋 Common Workflows
Workflow 1: Single Series Forecast
flowchart TD
accTitle: Single Series Forecast Workflow
accDescr: Step-by-step workflow for forecasting a single time series with system checking.
check["1. Run check_system.py"] --> load["2. Load model<br/>from_pretrained()"]
load --> compile["3. Compile with ForecastConfig"]
compile --> prep["4. Prepare data<br/>pd.read_csv → np.array"]
prep --> forecast["5. model.forecast()<br/>horizon=N"]
forecast --> extract["6. Extract point + PI"]
extract --> plot["7. Plot or export results"]
classDef step fill:#f3f4f6,stroke:#6b7280,stroke-width:2px,color:#1f2937
class check,load,compile,prep,forecast,extract,plot step
import pandas as pd, numpy as np
# Load wide-format CSV (one column per 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]
# Forecast all series at once (batched internally)
point, quantiles = model.forecast(horizon=30, inputs=inputs)
# Collect results
results = {}
for i, col inenumerate(df.columns):
results[col] = {
"forecast": point[i].tolist(),
"lower_80": quantiles[i, :, 1].tolist(),
"upper_80": quantiles[i, :, 9].tolist(),
}
# Exportimport json
withopen("batch_forecasts.json", "w") as f:
json.dump(results, f, indent=2)
print(f"Forecasted {len(results)} series → batch_forecasts.json")
Workflow 3: Evaluate Forecast Accuracy
import numpy as np
# Hold out the last H points for evaluation
H = 24
train = values[:-H]
actual = values[-H:]
point, quantiles = model.forecast(horizon=H, inputs=[train])
pred = point[0]
# Metrics
mae = np.mean(np.abs(actual - pred))
rmse = np.sqrt(np.mean((actual - pred) ** 2))
mape = np.mean(np.abs((actual - pred) / actual)) * 100# Prediction interval coverage
lower = quantiles[0, :, 1]
upper = quantiles[0, :, 9]
coverage = np.mean((actual >= lower) & (actual <= upper)) * 100print(f"MAE: {mae:.2f}")
print(f"RMSE: {rmse:.2f}")
print(f"MAPE: {mape:.1f}%")
print(f"80% PI Coverage: {coverage:.1f}% (target: 80%)")
⚙️ Performance Tuning
GPU Acceleration
import torch
# Check GPU availabilityif torch.cuda.is_available():
print(f"GPU: {torch.cuda.get_device_name(0)}")
print(f"VRAM: {torch.cuda.get_device_properties(0).total_mem / 1e9:.1f} GB")
elifhasattr(torch.backends, "mps") and torch.backends.mps.is_available():
print("Apple Silicon MPS available")
else:
print("CPU only — inference will be slower but still works")
# Always set this for Ampere+ GPUs (A100, RTX 3090, etc.)
torch.set_float32_matmul_precision("high")
Batch Size Tuning
# Start conservative, increase until OOM# GPU with 8 GB VRAM: per_core_batch_size=64# GPU with 16 GB VRAM: per_core_batch_size=128# GPU with 24 GB VRAM: per_core_batch_size=256# CPU with 8 GB RAM: per_core_batch_size=8# CPU with 16 GB RAM: per_core_batch_size=32# CPU with 32 GB RAM: per_core_batch_size=64
model.compile(timesfm.ForecastConfig(
max_context=1024,
max_horizon=256,
per_core_batch_size=32, # <-- tune this
normalize_inputs=True,
use_continuous_quantile_head=True,
fix_quantile_crossing=True,
))
Memory-Constrained Environments
import gc, torch
# Force garbage collection before loading
gc.collect()
if torch.cuda.is_available():
torch.cuda.empty_cache()
# Load model
model = timesfm.TimesFM_2p5_200M_torch.from_pretrained(
"google/timesfm-2.5-200m-pytorch"
)
# Use small batch size on low-memory machines
model.compile(timesfm.ForecastConfig(
max_context=512, # Reduce context if needed
max_horizon=128, # Reduce horizon if needed
per_core_batch_size=4, # Small batches
normalize_inputs=True,
use_continuous_quantile_head=True,
fix_quantile_crossing=True,
))
# Process series in chunks to avoid OOM
CHUNK = 50
all_results = []
for i inrange(0, len(inputs), CHUNK):
chunk = inputs[i:i+CHUNK]
p, q = model.forecast(horizon=H, inputs=chunk)
all_results.append((p, q))
gc.collect() # Clean up between chunks
🔗 Integration with Other Skills
With statsmodels
Use statsmodels for classical models (ARIMA, SARIMAX) as a comparison baseline:
CSV has 108 rows (3 stores x 36 weeks); stores have distinct price arrays
Quality Checklist
Run this checklist after every TimesFM task before declaring success:
Output shape correct -- point_fc shape is (n_series, horizon), quant_fc is (n_series, horizon, 10)
Quantile indices -- index 0 = mean, 1 = q10, 2 = q20 ... 9 = q90. NOT 0 = q0, 1 = q10.
Frequency flag -- TimesFM 1.0/2.0: pass freq=[0] for monthly data. TimesFM 2.5: no freq flag.
Series length -- context must be >= 32 data points (model minimum). Warn if shorter.
No NaN -- np.isnan(point_fc).any() should be False. Check input series for gaps first.
Visualization axes -- if multiple panels share data, use sharex=True. All time axes must cover the same span.
Generated renders stay untracked -- PNG / GIF / HTML example renders are local artifacts; regenerate them from the example scripts instead of committing them.
No large datasets committed -- any real dataset > 1 MB should be downloaded to tempfile.mkdtemp() and annotated in code.
matplotlib.use('Agg') -- must appear before any pyplot import when running headless.
infer_is_positive -- set False for temperature anomalies, financial returns, or any series that can be negative.
Common Mistakes
These bugs have appeared in this skill's examples. Learn from them:
Quantile index off-by-one -- The most common mistake. quant_fc[..., 0] is the mean, not q0. q10 = index 1, q90 = index 9. Always define named constants: IDX_Q10, IDX_Q20, IDX_Q80, IDX_Q90 = 1, 2, 8, 9.
Variable shadowing in comprehensions -- If you build per-series covariate dicts inside a loop, do NOT use the loop variable as the comprehension variable. Accumulate into separate dict[str, ndarray] outside the loop, then assign.
# WRONG -- outer `store_id` gets shadowed:
covariates = {store_id: arr[store_id] for store_id in stores} # inside outer loop over store_id# CORRECT -- use a different name or accumulate beforehand:
prices_by_store: dict[str, np.ndarray] = {}
for store_id, config in stores.items():
prices_by_store[store_id] = compute_price(config)
Wrong CSV column name -- The global-temperature CSV uses anomaly_c, not anomaly. Always print(df.columns) before accessing.
tight_layout() warning with sharex=True -- Harmless; suppress with plt.tight_layout(rect=[0, 0, 1, 0.97]) or ignore.
TimesFM 2.5 required for forecast_with_covariates() -- TimesFM 1.0 does NOT have this method. Install pip install timesfm[xreg] and use checkpoint google/timesfm-2.5-200m-pytorch.
Future covariates must span the full horizon -- Dynamic covariates (price, promotions, holidays) must have values for BOTH the context AND the forecast horizon. You cannot pass context-only arrays.
Anomaly thresholds must be defined once -- Define CRITICAL_Z = 3.0, WARNING_Z = 2.0 as module-level constants. Never hardcode 3 or 2 inline.
Context anomaly detection uses residuals, not raw values -- Always detrend first (np.polyfit linear, or seasonal decomposition), then Z-score the residuals. Raw-value Z-scores are misleading on trending data.
Validation & Verification
Use the example outputs as regression baselines. If you change forecasting logic, verify:
# Anomaly detection regression check:
python -c "
import json
d = json.load(open('examples/anomaly-detection/output/anomaly_detection.json'))
ctx = d['context_summary']
assert ctx['critical'] >= 1, 'Sep 2023 must be CRITICAL'
assert any(r['date'] == '2023-09' and r['severity'] == 'CRITICAL'
for r in d['context_detections']), 'Sep 2023 not found'
print('Anomaly detection regression: PASS')"# Covariates regression check:
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)}'
prices = df.groupby('store_id')['price'].mean()
assert prices['store_A'] > prices['store_B'] > prices['store_C'], 'Store price ordering wrong'
print('Covariates regression: PASS')"