dfm-python
Dynamic factor models in Python.
- DFM: linear, mixed-frequency Dynamic Factor Model estimated by EM with Kalman filtering and smoothing.
- DDFM: Deep Dynamic Factor Model with a nonlinear (PyTorch) encoder.
- iVDFM: Identifiable Variational Dynamic Factor Model.
- AFM: the Attention Factor Model of Epstein, Wang, Choi and Pelger (2025), a cross-sectional conditional factor model whose residuals are traded portfolios, with PCA and Avellaneda-Lee benchmarks.
Mixed frequencies are handled with a clock: latent factors evolve at one frequency (for example monthly) and lower-frequency series load on them through tent-kernel aggregation in the observation equation. Series faster than the clock are not supported.
Install
pip install dfm-python # or: uv add dfm-python
Requires Python 3.10 or later; PyTorch is a required dependency.
DFM
Pass preprocessed data; the model standardizes it internally.
import numpy as np
import pandas as pd
from dfm_python import DFM, DFMDataset
from dfm_python.config import DFMConfig
rng = np.random.default_rng(0)
cols = ["s0", "s1", "s2", "s3"]
df = pd.DataFrame(rng.standard_normal((120, 4)), columns=cols)
df["date"] = pd.date_range("2010-01-31", periods=120, freq="ME")
config = DFMConfig(
blocks={"global": {"num_factors": 1, "series": cols}},
frequency={c: "m" for c in cols},
clock="m",
max_iter=50,
)
dataset = DFMDataset(config=config, data=df, time_index="date")
model = DFM(dataset=dataset, config=config)
model.fit() # EM + Kalman smoother
X_forecast, Z_forecast = model.predict(horizon=6)
print(X_forecast.shape, Z_forecast.shape) # (6, 4) series, (6, state dim)
DDFM (from dfm_python import DDFM, DDFMDataset) and iVDFM
(from dfm_python.models.ivdfm import iVDFM) follow the same
dataset-then-model pattern. Hydra YAML configurations for each model are in
config/ and
end-to-end scripts in
tutorial/.
AFM
AFM takes a returns panel (T, N) and, for learned attention factors, firm
characteristics (T, N, M). It is trained on an after-cost Sharpe ratio plus an
explained-variance term (lambda_var).
import numpy as np
import torch
from dfm_python import AFM, AFMDataset
from dfm_python.models.afm.factors import residuals
rng = np.random.default_rng(0)
T, N, M = 120, 20, 8 # dates, assets, characteristics
returns = 0.01 * rng.standard_normal((T, N))
characteristics = rng.standard_normal((T, N, M))
ds = AFMDataset(returns=returns, characteristics=characteristics)
model = AFM(dataset=ds, n_factors=3, hist_len=20, max_epochs=20, seed=0)
model.fit()
out = model.predict()
out["residuals"].shape # (1, T, N) residual returns
out["weights"].shape # (1, T - hist_len, N) portfolio weights
out["book_returns"].shape # (1, T - hist_len) after-cost returns
result = model.get_result()
print(result.sharpe, result.explained_variance)
# Residuals from the fitted factor block directly
R, X = ds.get_tensors(model.device)
with torch.no_grad():
omega_f, beta_t = model.factor_block(X) # (T, K, N), (T, N, K)
eps = residuals(omega_f, beta_t, R) # (T, N)
# Benchmarks: PCA factors need no characteristics
pca = AFM(dataset=AFMDataset(returns=returns), n_factors=3, hist_len=20,
factor_model="pca", trading="ou") # or trading="longconv"
pca.fit()
Layout
src/dfm_python/
models/ dfm/, ddfm/, ivdfm/, afm/ model classes
dataset/ DFMDataset, DDFMDataset, iVDFMDataset, AFMDataset
config/ model configs (DFMConfig, ..., AFMConfig), YAML adapter, results
ssm/ state-space form and Kalman filtering
functional/ EM algorithm
numeric/, layer/, utils/, logger/ shared numerics, network layers, helpers
src/test/ pytest suite
config/ example Hydra YAML configurations
tutorial/ end-to-end scripts per model and dataset
Tests, from a clone: uv sync --group dev && uv run pytest src/test -q.
The paper repository minkeymouse/nfe uses AFM as a baseline.
License
MIT. See LICENSE.
Metadata
Release files for dfm-python 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| dfm_python-0.7.0.tar.gz | 304.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dfm_python-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 588.5 kB
Release files / dfm_python-0.7.0.tar.gz
| Download URL | dfm_python-0.7.0.tar.gz |
|---|---|
| Size | 304.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
54ef0ca51a1b791702e74f4275b34f43574cef4aed5b040ed36a3298bd2b2f78
|
|
BLAKE2b-256 checksum How to use checksums |
e1a73b17b0bc4b761ef5140034b73392988aad615f933edafb6286951884c9b0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.7
|
Release files / dfm_python-0.7.0-py3-none-any.whl
| Download URL | dfm_python-0.7.0-py3-none-any.whl |
|---|---|
| Size | 283.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
05dc39b2c73a05fe0bd95339023c74dc2d331c9cb38063c07b173ce91bad47e0
|
|
BLAKE2b-256 checksum How to use checksums |
5b7c72c42b8058d305dc1e81e8a8172e305e17d4eb7fea472790e1a18d2a74b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.7
|
Security for dfm-python 0.7.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.