Skip to content

SDK reference

The authoring API (signalquarry.sdk) is the stable surface: additive changes only in 0.x.

Strategy authoring API (stable).

A strategy is one pure function::

from signalquarry.sdk import Ctx, Decision, Field, Params, strategy, ta

class P(Params):
    symbol: str = "SPY"
    period: int = Field(200, ge=20, le=400)

@strategy(params=P, lookback=lambda p: p.period)
def decide(ctx: Ctx, p: P) -> Decision:
    close = ctx.bars(p.symbol).close
    if close[-1] > ta.sma(close, p.period)[-1]:
        return Decision.target({p.symbol: "0.95"}, "PRICE_ABOVE_SMA")
    return Decision.target({}, "PRICE_BELOW_SMA")

Params

Bases: BaseModel

Base class for strategy parameters. Field bounds double as the sweep space.

Ctx dataclass

Everything a strategy may use for one decision.

Bars dataclass

Completed daily bars for one symbol, oldest first; index -1 is the last completed session.

Arrays are read-only float64 views, point-in-time split-adjusted as of the information cutoff. Missing sessions are NaN.

Decision dataclass

A strategy's output. Build it with :meth:target, :meth:hold or :meth:unavailable.

hold(*reason_codes, state=None) classmethod

Keep current holdings unchanged.

target(weights, *reason_codes, state=None) classmethod

Hold these portfolio weights (fractions of equity). Symbols not listed are sold.

unavailable(*reason_codes) classmethod

No decision can be made (e.g. missing data). Holdings and state stay unchanged.

FactorCtx dataclass

A read-only window ending strictly before the decision session.

Columns follow universe order. Missing observations are NaN in numeric panels and false in present. Prices are raw, without lifecycle adjustment. The engine constructs this from a verified, truncated panel and an explicit universe; its membership provenance is checked separately.

FactorDef dataclass

definition_of(obj)

factor(*, params, lookback)

Declare a factor returning finite scores for covered universe symbols.

factor_definition_of(obj)

Indicators (signalquarry.sdk.ta)

Technical-analysis helpers over completed-bar arrays. Pure numpy, no look-ahead.

Each function returns an array aligned with its input; positions without enough history are NaN. Use [-1] for the value as of the last completed bar.

ema(values, period)

Exponential moving average seeded with the first period-bar SMA.

returns(values, periods=1)

Simple returns over periods bars.

rolling_std(values, period)

Rolling sample standard deviation.

rsi(values, period=14)

Wilder's relative strength index.

sma(values, period)

Simple moving average.

zscore(values, period)

Distance from the rolling mean in rolling standard deviations.

Cross-sectional research (signalquarry.sdk.xs)

factor is a pure research API. It requires a separately supplied universe and does not produce an evidence grade or authorize paper orders. Panel prices are raw; corporate-action adjustment and historical universe provenance remain required before point-in-time factor claims.

from signalquarry.sdk import FactorCtx, Params, factor, xs

class Momentum(Params):
    lookback: int = 21

@factor(params=Momentum, lookback=lambda p: p.lookback)
def momentum(ctx: FactorCtx, p: Momentum) -> dict[str, float]:
    close = ctx.panel("close")
    change = close[-1] / close[0] - 1
    scores = xs.rank(change)
    return {
        symbol: float(score)
        for symbol, score in zip(ctx.universe, scores, strict=True)
        if score == score  # NaN means insufficient coverage
    }

Run sqy check --factor your_package.momentum --params-json '{"lookback":21}' from the project to check one decorated factor on synthetic panels. The module must live under the project and define exactly one factor. The check reports import policy, score contract, determinism, and future-bar perturbation results. It imports and runs the authored module, so use an isolated development environment for untrusted code. It does not establish historical data or universe provenance.

For a project factor that an agent can discover by ID, add its module to signalquarry.toml:

[factors]
modules = ["your_package.momentum.factor"]

Place factor.yaml beside factor.py:

schema: signalquarry.factor/v1
id: price-momentum
family: momentum
version: "1"
hypothesis:
  statement: Past prices predict next returns.
  falsification: Rank IC is nonpositive out of sample.
params:
  lookback: 21
evaluation:
  horizons: [1, 5, 21]
  chronological_blocks: 6
  cost_bps: 10
  capital: 1000000
  trial_budget: 50
  holdout:
    months: 12

sqy factor ls lists the factor's code-tree and configuration hashes. sqy check --factor-id price-momentum runs the synthetic checks with the declared parameters. Editing code, metadata, or parameters changes the configuration identity. Listing and checking do not record a trial or grant an evidence grade. The pure factor-trial key binds evaluation settings, data, dated universe, labels, decision sessions and accepted-factor comparisons.

To calculate descriptive metrics from locally cached data, provide the dataset ID and one --universe-manifest for each dated membership decision:

sqy factor evaluate --factor price-momentum --dataset-id DATASET \
  --universe-manifest data/universe/builds/first.json \
  --universe-manifest data/universe/builds/next.json --json

The command re-hashes the selected dataset pages and replays each selected universe manifest from its cached inputs. Its output has data.scope set to unverified; consistency checks do not establish provider completeness, complete corporate-action history, ticker continuity, or delisting outcomes. It does not append a strategy or factor trial, calculate significance or a grade, or access a holdout. A controlled real-data evidence path still needs those controls. All three commands import authored factor code, so inspect untrusted project code before running it outside an isolated development environment. The code hash covers the factor's full top-level project package, including shared helpers; edits elsewhere in that package also change its identity. The import policy checks that complete package as well.

Pure cross-sectional operators. NaN marks unavailable observations.

neutralize(values, exposures)

OLS residuals against numeric exposure columns and an intercept.

Rows missing a score or any exposure stay NaN. Rank-deficient designs fail rather than silently returning an arbitrary decomposition.

rank(values)

Average-tie percentile ranks in (0, 1); missing values stay NaN.

zscore(values)

Population z-scores; zero cross-sectional dispersion is unavailable.