Developer guide#
This guide is for contributors: people who add a vendor, a dataset, a factor, a model head or a portfolio rule, or who change the machinery underneath. It assumes you know the pipeline from the user guide.
Architecture overview#
The architecture overview is a short illustrated tour of the layers, the panels they
exchange and the run records: architecture.pdf.
Panels#
Every layer exchanges xarray.Dataset panels on the dimensions (timestamp, symbol),
stored as Zarr and read by date range. DataFrames are used inside a layer (Polars factors,
Parquet raw tiers) but never between layers.
Layer order#
The layers form one chain, and a layer imports only the layers before it:
utils / enums -> core -> backend -> tracking -> execution -> runs -> universe -> dataset
-> config -> acquisition -> analysis -> factor -> label -> model -> risk -> portfolio
-> backtest -> api
The rule has no exceptions: an import inside a function counts too, and
tests/test_layer_layout.py fails on any import against the order. The same test pins
the two engines that have exactly one importer: vectorbt only in
quantlab/backtest/engine_vectorbt.py, and cvxpy only in
quantlab/portfolio/predefined/mean_variance.py.
Package layout#
Each layer keeps its root class in
<layer>/base.pyand its configs in<layer>/config.py. The implementations quantlab ships live in<layer>/predefined/, and a framework module never imports them.The public top-level entries of a layer are framework modules (
base.py,config.py, shared engines such asacquisition/registry.pyordataset/raw.py) or one entry per vendor or dataset, a package when it has several modules. Code that serves the layer but is not its public surface goes in<layer>/_support/.Layer and
_support/packages have empty__init__.pyfiles. Import every module by its full dotted path, and add no re-exports.quantlab/__init__.pyholds only__version__. Three packages are entry modules whose__init__.pyis the module itself:quantlab/acquisition/wrds/,quantlab/dataset/crsp/andquantlab/dataset/nbbo/. Internals explains what depends on this.Root modules load no torch; torch is imported only by the modules that need it.
Every component rebuilds from its recorded config through
quantlab.core.component.rebuild().The backtest reaches a model only through the
Predictorprotocol.
There is no command-line interface. examples/ holds self-contained, runnable
walkthroughs that import only quantlab.
Running the tests#
quantlab uses uv and Python 3.13 or later. Install every extra and the development group, then run the suite in parallel:
uv sync --all-extras
uv run pytest -n auto
The suite has about 5,300 tests and takes about five minutes. uv sync without
--all-extras uninstalls the extras, and the tests that need them are then skipped or
fail. CI runs uv sync --locked --dev --all-extras on Ubuntu and macOS.
A few tests guard the structure rather than behaviour: tests/test_layer_layout.py (the
layer order), tests/test_undefined_names.py (ruff F821 and F401),
tests/test_extensibility_contract.py (no market-specific names in a root class) and
tests/test_source_inspector.py (the credential-free read surface).
KunQuant compiles factor graphs at run time and needs a C++ compiler. On macOS, xgboost and
torch link different OpenMP runtimes, and a process that uses both can crash or hang;
tests/conftest.py sets OMP_NUM_THREADS=1 on macOS before any import, and a script that
mixes the two must do the same.
Building the documentation#
The site is built with Sphinx from MyST Markdown pages, and the API reference is generated from the numpydoc docstrings:
uv sync --all-extras --group docs
uv run sphinx-build -b html docs docs/_build/html
Open docs/_build/html/index.html in a browser.