Getting Started

QRT is a collection of quantitative-research tools with explicit boundaries between market data, measurements, model inputs, training transforms, investment intent, portfolio construction, simulation, and evaluation.

Review dependencies here: https://pydeps.com/project/pyqrt/

Warning

QRT is in alpha. Public APIs can still change while the architecture settles.

The research flow

research_flow data q.data Acquire observations calendar q.calendar Apply market-time semantics data->calendar indicator q.indicator Measure one instrument calendar->indicator cross_section q.cross_section Compare assets indicator->cross_section label q.label Construct targets cross_section->label dataset q.dataset Align ML data label->dataset transform q.transform Fit transformations dataset->transform model q.model Train models transform->model signal q.signal Express intent model->signal portfolio q.portfolio Construct positions signal->portfolio bt q.bt Simulate execution portfolio->bt stats q.stats / q.plot Analyze risk and results bt->stats

The flow describes ownership, not a requirement that every workflow invoke every namespace. Most blocks classify operations by what they do. q.dataset is the deliberate exception: it is the aligned object carrying features, targets, weights, event metadata, and split assignments across the fitted transformation and model-training boundary.

An SMA can become a stored model feature, a chart overlay, or an online rule input; it remains an indicator in all three cases.

Quickstart

uv add pyqrt

# Optional provider catalogs and PyTorch helpers
uv add "pyqrt[indicators]"
uv add "pyqrt[torch]"
import qrt as q

aapl = q.data.sources.yfinance.read(
    "AAPL", "2024-01-01", "2025-01-01", "1d"
)
source = aapl.rename_axis("datetime").reset_index().assign(symbol="AAPL")

# Calculate model inputs directly and keep their canonical row index.
model_inputs = aapl[["close"]].assign(
    sma_20=q.indicator.sma(aapl["close"], 20),
).dropna()
dataset = q.dataset.Dataset(X=model_inputs)

Namespace roles

q.data: acquire and persist observations

q.data owns data access, local file formats, vendor adapters, bundled sample datasets, and database connections. It should return canonical pandas objects without embedding trading logic or fitting model transformations.

Use it to read raw OHLCV, trades, fundamentals, labels, and already materialized features. Vendor credentials, retries, caching, and storage belong here.

Boundary: q.data does not decide what a market value means, whether it is a feature, or how a model should transform it.

q.calendar: define market time

q.calendar owns exchange sessions, closures, holidays, market hours, and timestamp alignment. It uses explicit exchange identifiers such as XNYS and never assumes one default market.

Use it whenever a calculation depends on trading sessions rather than generic wall-clock dates.

Boundary: generic timestamp parsing belongs in data preparation; exchange-aware semantics belong here.

q.indicator: measure one market series

q.indicator owns deterministic, normally stateless measurements such as moving averages, relative strength, and realized volatility. Native formulas are flat; provider formulas remain explicit under q.indicator.talib and q.indicator.pandas_ta.

Use indicators directly in notebooks, model-input frames, monitoring, rules, and backtests.

Boundary: indicators do not persist themselves, compare a whole asset universe, or express a buy/sell decision. Functions accept one instrument at a time and must not silently cross symbol boundaries.

q.cross_section: compare assets cross-sectionally

q.cross_section owns characteristics that require a universe snapshot or interactions between assets: ranks, sector neutralization, and relative rating models such as Elo.

Use factors when the value of one symbol depends on other symbols at the same point in time. Factor output can become a model-input column or be interpreted by q.signal.

Boundary: a factor scores or transforms assets; it does not choose portfolio weights. Feature-column selection is also not factor work.

q.label: construct future-aware model targets

q.label owns operations that deliberately inspect observations after an event: CUSUM event detection, fixed-horizon and triple-barrier labels, meta-labels, trend scanning, concurrency, average uniqueness, and sample weights. Label outputs retain event and outcome times so leakage-aware splitters can purge overlapping training examples.

Boundary: labels are supervised-learning targets, not point-in-time model inputs. Barrier widths may use volatility known at the event time, but the resulting barrier outcome must never be used as a contemporaneous predictor.

q.dataset: keep machine-learning rows aligned

q.dataset assembles features, labels, sample weights, and event metadata under one canonical index. Optional split schemes assign every row to arbitrary named partitions with explicit roles such as fit, evaluate, and holdout.

Partition access returns views over the aligned parent dataset. A scheme may contain multiple named folds for walk-forward or cross-validation workflows, and split metadata can record methods, boundaries, purge, and embargo policy.

Boundary: a dataset stores aligned values and split assignments; it does not acquire observations, calculate model inputs or labels, learn transformations, or train models. q.data.datasets remains the separate catalog of bundled sample market data.

q.transform: fit the training boundary

q.transform is reserved for imputation, scaling, outlier treatment, encoding, dimensionality reduction, and feature-column selection. Learned operations must follow fit/transform: fit only on fit partitions, retain fitted state, and transform every partition with that same state.

Boundary: globally fitting these operations before a train/test split leaks future information. Deterministic market calculations belong before this boundary; learned transformations belong after the split. Non-learning data cleaning and canonicalization remain in q.data.

q.model: train and inspect models

q.model owns model lifecycle helpers. Today it provides a sklearn-compatible position-array bridge under q.model.selection and lazy PyTorch helpers under q.model.torch. Aligned temporal and purged folds belong to q.dataset.

Boundary: q.dataset defines aligned row partitions; q.transform.selection selects feature columns. Model code consumes prepared features but does not fetch market data, define split metadata, or express trading intent.

q.ray: distribute research workloads

q.ray lazily exposes Ray for distributed data processing, model training, hyperparameter tuning, serving, and reinforcement learning. Importing QRT does not import Ray until the namespace is used.

Boundary: Ray controls execution and resource allocation. The owning QRT modules still define the financial calculation, aligned dataset, model, and evaluation semantics that run on that infrastructure.

q.signal: express investment intent

q.signal turns point-in-time indicators, factors, model predictions, or rules into directional or sizing intent. It provides canonical pandas validation, threshold and hysteresis rules, cross-sectional selection, explicit delay, decay, holding and cooldown policies, signal combination, turnover limiting, and bounded desired exposure.

Boundary: an indicator measures and a factor compares; a signal interprets. Unlike q.label targets, signals never inspect future outcomes. Signals still do not decide final portfolio weights or simulate fills.

q.portfolio: convert intent into positions

q.portfolio owns portfolio construction, constraints, weighting, exposure, attribution, and rebalance decisions.

Boundary: signals state preferences; portfolio logic resolves those preferences against capital, risk, and constraints to produce target positions.

q.bt: simulate decisions

q.bt owns event progression, order handling, fills, fees, slippage, and the historical state required to simulate a strategy without look-ahead.

Boundary: backtesting consumes data, signals, and portfolio decisions. It should not become the canonical implementation of indicators or statistics.

q.stats and q.plot: evaluate results

q.stats computes return-stream, loss-distribution, benchmark, rolling, trade, and factor analytics. Risk estimators use explicit historical or parametric names and return non-negative loss magnitudes. q.plot renders those results and market/trade visualizations.

Boundary: q.stats describes return distributions and realized results; holdings-aware risk contributions, stress scenarios, and portfolio responses belong to q.portfolio.

Boundary: these modules analyze or present outcomes. They do not mutate the strategy, refit its model, or create a new feature store as a side effect.

q.env and q.utils: support explicit execution

q.env handles environment files and runtime inspection. q.utils contains small shared facilities such as logging, reproducibility, caching, and console output.

Boundary: neither namespace is a dumping ground for domain logic. A helper with a stable financial or lifecycle meaning belongs in its owning domain module.

Batch and online parity

The same indicator or factor callable should be usable in historical batch computation and online inference. An online service may call the calculation directly, provided it supplies the same ordered history and availability rules.

Fitted transformation is different: online inference must load the exact artifact fitted on training data. It must never refit on a live window silently.

Development

make install   # sync core and development dependencies
make test      # run the full test suite
make stubs     # regenerate provider-wrapper type stubs
make datasets  # refresh bundled sample datasets and demo trades
make docs      # build references and serve docs locally

See the Roadmap for planned work and each namespace’s page for its current API.

Back to top