Signals
q.signal turns point-in-time indicators, factors, model predictions, or rules into investment intent. A signal says what the strategy would like to do using information available at that row. It does not describe what happened later.
The visual signal tutorial overlays scores, state changes, causal delay, and turnover-limited exposure on a bundled SPY price series.
Labels and signals are different
This distinction is a leakage boundary, not just a naming preference.
| Namespace | Question answered | May inspect future data? | Typical output |
|---|---|---|---|
q.label |
What subsequently happened? | Yes, when constructing targets | Class, realized return, event outcome, sample weight |
q.signal |
What do we want to do now? | No | Score, direction, conviction, desired exposure |
q.label supplies targets and training metadata. Target construction may follow prices after an event, so those outputs must never become contemporaneous model features or strategy inputs. q.signal consumes only measurements or predictions available at decision time and produces deployable intent.
Canonical objects
Signals are ordinary pandas objects with a strict orientation:
| Shape | Meaning |
|---|---|
Series |
One asset through ordered decision times |
DataFrame |
Ordered decision times as rows and assets as columns |
Indexes must be sorted and unique. Asset columns must be unique. Values are numeric; missing values are allowed, but infinities are rejected. q.signal.as_signal validates this contract and returns a float copy.
Function map
| Function | Purpose |
|---|---|
q.signal.as_signal |
Validate the canonical shape, labels, ordering, and numeric values. |
q.signal.threshold |
Convert scores into long, flat, and optionally short direction. |
q.signal.hysteresis |
Use separate entry and exit levels to prevent unstable threshold crossings. |
q.signal.normalize |
Normalize each time row by cross-sectional z-score or centered percentile rank. |
q.signal.select |
Select exact top and bottom asset counts with deterministic tie handling. |
q.signal.neutralize |
Remove static asset exposures from every row using q.cross_section.neutralize. |
q.signal.combine |
Take an aligned weighted mean of multiple signals, renormalizing around missing inputs. |
q.signal.delay |
Make row availability explicit; one observation is the safe default. |
q.signal.decay |
Exponentially smooth updates using only current and earlier rows. |
q.signal.hold |
Enforce a minimum number of observations between directional state changes. |
q.signal.cooldown |
Exit immediately, then require flat observations before re-entry. |
q.signal.limit_turnover |
Limit each asset’s change from its previously accepted target. |
q.signal.target_exposure |
Map normalized conviction to bounded per-asset desired exposure. |
Causal workflow
import pandas as pd
import qrt as q
scores = pd.Series(
[0.52, 0.66, 0.58, 0.43, 0.35],
index=pd.date_range("2026-01-05", periods=5, freq="B"),
name="model_score",
)
# Enter beyond 0.60/0.40, but wait for 0.50 before exiting either side.
intent = q.signal.hysteresis(
scores,
long_enter=0.60,
long_exit=0.50,
short_enter=0.40,
short_exit=0.50,
)
# A score observed at t becomes executable intent at t + 1.
available_intent = q.signal.delay(intent)
stable_intent = q.signal.cooldown(available_intent, periods=2)
exposure = q.signal.target_exposure(stable_intent, maximum=0.25)Pass periods=0 to delay only when the score is genuinely known before the same row’s execution decision. The function does not infer whether a close, barrier, vendor timestamp, or model feature was actually available.
For a cross-sectional strategy, normalize and select before applying timing and exposure controls:
normalized = q.signal.normalize(predictions, method="percentile")
direction = q.signal.select(normalized, long_count=10, short_count=10)
direction = q.signal.delay(direction)
exposure_preference = q.signal.target_exposure(direction, maximum=0.02)Ownership boundaries
q.indicatormeasures one instrument; it does not decide whether to trade.q.cross_sectioncompares assets.q.signal.normalizeandq.signal.neutralizeadapt those measurements to the canonical signal panel.q.labelconstructs future-aware targets and training metadata.q.signalinterprets available information as intent.q.portfolioresolves intent against capital, risk, leverage, net/gross, concentration, and rebalance constraints.q.btsimulates orders, fills, fees, slippage, and historical state.
target_exposure is deliberately not a portfolio optimizer. It maps a per-asset conviction in [-1, 1] to a bounded desired exposure and leaves cross-asset capital constraints to q.portfolio.