How to Use TimesFM with Exogenous Variables (Covariates): A Complete Guide

Use the forecast_with_covariates method in TimesFM 2.5 after compiling the model with return_backcast=True, providing dynamic and static covariates that span both the historical context and the forecast horizon to generate adjusted point and quantile forecasts.

TimesFM 2.5, the open-source time series foundation model from Google Research, supports exogenous variables (covariates) through its forecast_with_covariates API. This functionality enables you to incorporate external factors like pricing, promotions, or weather data to improve forecast accuracy beyond the base model's zero-shot capabilities. In this guide, you will learn how to structure your covariates, choose between regression modes, and implement the full pipeline using the actual source code from the google-research/timesfm repository.

Setting Up Your Environment and Model

Before using covariates, you must install the optional XReg dependencies and configure the model to return backcast values required for the regression pipeline.

Install the package with XReg support:

pip install "timesfm[xreg]"

Load and compile the TimesFM 2.5 checkpoint with the return_backcast flag enabled. According to the implementation in src/timesfm/timesfm_2p5/timesfm_2p5_base.py, this flag is mandatory for forecast_with_covariates to access the historical predictions needed for residual modeling:

import timesfm
from timesfm import TimesFmHparams, TimesFmCheckpoint

hparams = TimesFmHparams(backend="cpu", per_core_batch_size=8, horizon_len=12)
ckpt = TimesFmCheckpoint(huggingface_repo_id="google/timesfm-2.5-200m-pytorch")
model = timesfm.TimesFm(hparams=hparams, checkpoint=ckpt)

# Required for covariate support

model.forecast_config.return_backcast = True
model.compile()

Understanding Covariate Types

The forecast_with_covariates method in src/timesfm/timesfm_2p5/timesfm_2p5_base.py accepts four distinct covariate categories. All covariates must be provided as lists of sequences, where each inner sequence corresponds to a single time series and includes values for both the context window and the forecast horizon.

Dynamic Numerical Covariates

These are continuous values that change over time, such as temperature, price, or sensor readings. You must know these values for the future horizon at inference time.

Dynamic Categorical Covariates

These represent discrete, time-varying states like promotion flags, holiday indicators, or day-of-week encodings. The system automatically applies one-hot encoding to these variables internally using utilities in src/timesfm/utils/xreg_lib.py.

Static Covariates

These are per-series attributes that remain constant over time, such as store type, region, or product category. Provide these as simple 1-D lists matching the number of input series.

The forecast_with_covariates API

The primary entry point for covariate-enhanced forecasting is the forecast_with_covariates method defined in src/timesfm/timesfm_2p5/timesfm_2p5_base.py. This method orchestrates the in-context linear regression and merges results with base TimesFM predictions.

Key parameters include:

  • inputs: List of 1-D numpy arrays containing the historical target values (context only).
  • dynamic_numerical_covariates: Dictionary mapping feature names to lists of arrays covering context + horizon.
  • dynamic_categorical_covariates: Dictionary mapping category names to lists of arrays covering context + horizon.
  • static_categorical_covariates: Dictionary mapping static feature names to lists of values (one per series).
  • xreg_mode: Either "xreg + timesfm" (default) or "timesfm + xreg".
  • normalize_xreg_target_per_input: Boolean flag (default True) to standardize each series before regression.

XReg Modes: How Covariates Integrate with TimesFM

The integration of covariates relies on ridge-regularized linear regression handled by BatchedInContextXRegLinear in src/timesfm/utils/xreg_lib.py. You can control the modeling order via the xreg_mode parameter.

"xreg + timesfm" (Default)

In this mode, the system first fits a linear regression directly on the raw targets using the provided covariates, then subtracts these predictions to create a residual series. TimesFM forecasts these residuals, and the final output adds the linear predictions back to the TimesFM residual forecasts.

"timesfm + xreg"

This alternative mode reverses the order:

  1. TimesFM generates base forecasts for the targets.
  2. The system computes residuals (target minus forecast).
  3. A linear regression is fitted to predict these residuals using the covariates.
  4. The regression predictions are added back to the original TimesFM forecasts.

Choose "xreg + timesfm" when you believe the covariates capture the trend and TimesFM should model the remaining pattern. Choose "timesfm + xreg" when you want TimesFM to capture the base signal first, using covariates only to adjust the errors.

Complete Implementation Examples

Minimal Example with Synthetic Data

This example demonstrates the core API using synthetic sine waves with price and promotion covariates:

import numpy as np

# Prepare two time series, 30 time steps each

inputs = [
    np.sin(np.linspace(0, 3*np.pi, 30)) + np.random.randn(30) * 0.1,
    np.cos(np.linspace(0, 3*np.pi, 30)) + np.random.randn(30) * 0.1,
]

# Dynamic numerical: price (must include 12-step horizon, total 42 steps)

price = [
    np.linspace(10, 12, 42),
    np.linspace(8, 9, 42),
]

# Dynamic categorical: promotion flag (0 or 1)

promotion = [
    np.random.choice([0, 1], size=42, p=[0.8, 0.2]),
    np.random.choice([0, 1], size=42, p=[0.7, 0.3]),
]

# Static categorical: store type

store_type = ["premium", "standard"]

# Generate forecasts

point_fc, quant_fc = model.forecast_with_covariates(
    inputs=inputs,
    dynamic_numerical_covariates={"price": price},
    dynamic_categorical_covariates={"promotion": promotion},
    static_categorical_covariates={"store_type": store_type},
    xreg_mode="xreg + timesfm",
    normalize_xreg_target_per_input=True,
)

print(f"Point forecasts shape: {np.array(point_fc).shape}")      # (2, 12)

print(f"Quantile forecasts shape: {np.array(quant_fc).shape}") # (2, 12, 10)

Real-World Example: Loading from CSV

Assume a sales.csv file with columns: date, store_id, sales, price, promotion, holiday, covering multiple stores:

import pandas as pd

df = pd.read_csv("sales.csv", parse_dates=["date"])
df["day_of_week"] = df["date"].dt.dayofweek

store_ids = df["store_id"].unique()
inputs = []
price = []
promotion = []
holiday = []
dow = []
store_type = []

horizon_len = 12

for sid in store_ids:
    sub = df[df["store_id"] == sid].sort_values("date")
    sales_series = sub["sales"].values
    
    # Context only

    inputs.append(sales_series)
    
    # Build covariates covering context + horizon (future values must be known)

    price.append(
        np.concatenate([sub["price"].values,
                       np.full(horizon_len, sub["price"].iloc[-1])])
    )
    promotion.append(
        np.concatenate([sub["promotion"].values,
                       np.zeros(horizon_len, dtype=int)])
    )
    holiday.append(
        np.concatenate([sub["holiday"].values,
                       np.zeros(horizon_len, dtype=int)])
    )
    dow.append(
        np.concatenate([sub["day_of_week"].values,
                       np.full(horizon_len, sub["day_of_week"].iloc[-1])])
    )
    
    # Static attribute

    store_type.append("premium" if sid.startswith("A") else "standard")

# Forecast

point_fc, quant_fc = model.forecast_with_covariates(
    inputs=inputs,
    dynamic_numerical_covariates={"price": price},
    dynamic_categorical_covariates={
        "promotion": promotion,
        "holiday": holiday,
        "day_of_week": dow,
    },
    static_categorical_covariates={"store_type": store_type},
    xreg_mode="xreg + timesfm",
)

Technical Deep Dive: The XReg Pipeline

Behind the API, the covariate handling is implemented in src/timesfm/utils/xreg_lib.py. The BatchedInContextXRegLinear class (a subclass of BatchedInContextXRegBase) performs several critical operations:

  1. Shape validation: Ensures covariate sequences match the expected length (context + horizon).
  2. Encoding: Converts categorical variables to one-hot representations.
  3. Normalization: When normalize_xreg_target_per_input=True, standardizes each series' regression target using normalize utilities in xreg_lib.py, then renormalizes predictions after fitting.
  4. Regression: Fits ridge-regularized ordinary least squares (OLS) to solve the in-context regression problem.
  5. Forecast merging: Combines linear model predictions with TimesFM forecasts according to the selected xreg_mode.

The forecast_with_covariates method in src/timesfm/timesfm_2p5/timesfm_2p5_base.py orchestrates this pipeline, first validating inputs, then delegating to the XReg utilities, and finally returning point forecasts and quantile forecasts that incorporate the covariate effects.

Visualizing Covariate Impact

The repository includes a demonstration script at timesfm-forecasting/examples/covariates-forecasting/demo_covariates.py that generates synthetic retail data and visualizes how each covariate influences the forecast.

To explore this functionality:

from timesfm_forecasting.examples.covariates_forecasting.demo_covariates import (
    generate_sales_data, create_visualization
)

data = generate_sales_data()
create_visualization(data)  # Saves to output/covariates_data.png

Running this script produces a 2×2 visualization panel showing sales trends, price effects, and the decomposition of covariate contributions, stored alongside sales_with_covariates.csv and covariates_metadata.json in the output directory. This demonstrates the interpretability advantage of the XReg approach: you can isolate and quantify the specific impact of price changes or promotional events on your forecasts.

Summary

  • Enable backcast support: Set model.forecast_config.return_backcast = True and call model.compile() before using covariates.
  • Provide complete covariate sequences: All dynamic covariates must include values for both the historical context and the future forecast horizon.
  • Choose your integration mode: Use "xreg + timesfm" (default) to fit linear trends first, or "timesfm + xreg" to use covariates for residual correction.
  • Let the library handle preprocessing: The BatchedInContextXRegLinear class in src/timesfm/utils/xreg_lib.py automatically handles one-hot encoding, normalization, and ridge regression.
  • Access via forecast_with_covariates: This method in src/timesfm/timesfm_2p5/timesfm_2p5_base.py returns point and quantile forecasts adjusted for your exogenous variables.

Frequently Asked Questions

What versions of TimesFM support exogenous variables?

Exogenous variable support is available in TimesFM 2.5 and later versions. The forecast_with_covariates method is implemented in the TimesFM class within src/timesfm/timesfm_2p5/timesfm_2p5_base.py. Earlier versions of the model do not expose this API.

Do I need to normalize covariates manually?

No. The BatchedInContextXRegLinear utility in src/timesfm/utils/xreg_lib.py automatically handles normalization of numerical covariates and one-hot encoding of categorical variables. You can control target normalization (standardizing the time series values before regression) using the normalize_xreg_target_per_input parameter, which defaults to True.

Can I use TimesFM with covariates for probabilistic forecasting?

Yes. The forecast_with_covariates method returns both point forecasts and quantile forecasts. The quantile forecasts incorporate uncertainty estimates that account for the covariate-adjusted residuals. The output shape is (num_series, horizon_len, num_quantiles), allowing you to construct prediction intervals alongside your point estimates.

What is the difference between static and dynamic covariates?

Dynamic covariates change over time and require a value for every timestep in both the context window and forecast horizon (e.g., daily temperature or promotional flags). Static covariates are per-series attributes that remain constant across all timesteps (e.g., store region or product category). Dynamic covariates are passed as lists of arrays, while static covariates are passed as simple lists with one value per input series.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →