Skip to main content
Contents
6 min read

Signals

Signal SDK

Author, test, version, and sync a reusable local Signal plugin.

App path

  • Local Signal Library -> signal.py
  • Signals -> Signal Library -> Sync

What the SDK is for

Use the Signal SDK when a calculation should become a reusable input for Strategies rather than a one-off notebook experiment. The SDK is a Python code-authoring path: you write a local Python plugin that declares what it needs, what it produces, and how it calculates those outputs. The Signal Library then makes that declaration available for review and controlled use.

It is best for a calculation with clear inputs, stable parameter meanings, and a reason to reuse it across strategies or studies. Keep order logic, sizing, and complete entry/exit decisions in a Strategy instead. If you do not want to write Python, use Author with AI Companion as the no-code path for brainstorming and specifying the idea; it does not publish a Signal without your review.

Decide what to build

Start with a short specification before writing code:

  • Purpose: What market condition or measurement does it express?
  • Inputs: Which fields or upstream Signal outputs does it need?
  • Timing: At what point is each value known? A Signal must not use later information to calculate an earlier value.
  • Parameters: What can change, what unit does each setting use, and what values are reasonable to test?
  • Outputs: What columns will downstream work read, and what type does each column have?
  • History and state: How much prior data does it require, and does it need bounded incremental state?
  • Edge cases: How should missing input, warm-up rows, session boundaries, zero volume, and invalid parameter values behave?

If these answers are still exploratory, start in a notebook. Promote the idea to a Signal once its meaning is stable enough to reuse.

What a Signal plugin declares

The SDK exposes a Signal definition with the following operator-facing parts:

DeclarationWhy it matters
Signal ID and versionIdentify the exact calculation used by a Strategy or result. Keep the ID stable; make a new version when semantic behavior changes.
Name and descriptionExplain the calculation to Library users.
Category, group, and labelsOrganize the Library without changing the calculation.
Required inputsDeclare every market field or upstream Signal output the plugin reads.
ParametersGive each adjustable value a type, default, and supported bounds or choices.
Output columnsDeclare each produced column, its value type, and whether it can be used as a Signal output.
Data requirements and contextState required data shape and facts such as timezone, tick size, point value, or session calendar when the calculation needs them.
History and streaming supportDeclare bounded prior history and, where implemented, an incremental stream that agrees with the batch calculation.

The batch builder receives only the bound inputs, reviewed parameter values, and declared context. It should return exactly the output columns it promised, with the same number of rows as its input. Do not hide file reads, network requests, credentials, or undeclared configuration inside a Signal.

Worked example: the bundled Cumulative Return Signal

The bundled cumulative_return plugin is a compact example of a Signal with one required input, no adjustable parameters, one output, and an incremental path. Its installed signal.py exposes the pre-shipped definition from signal_library.families.statistics. The relevant definition is shown below; the helper imports are the shared Library helpers used by the bundled plugin.

CODE
import numpy as np
import pyarrow as pa

from arizmic.signal_sdk import (
    SignalBuildContext,
    SignalCategory,
    SignalDefinition,
)
from arizmic.strategy_sdk import signal_kernels
from signal_library.families.shared import (
    _buffered_stream_factory,
    _float_column,
    _input,
    _output,
    _ParamMap,
)


def _cumulative_return(
    inputs: pa.Table,
    params: _ParamMap,
    context: SignalBuildContext | None = None,
) -> pa.Table:
    del params
    close = _float_column(inputs, "close")
    previous = signal_kernels.shift(close, 1)
    gross = np.divide(
        close,
        previous,
        out=np.ones(close.shape, dtype=float),
        where=~np.isnan(previous) & (previous != 0.0),
    )
    cumulative = signal_kernels.cumulative_product(gross)
    return pa.table({"cumulative_return": pa.array(cumulative - 1.0)})


cumulative_return_signal = SignalDefinition(
    signal_id="cumulative_return",
    name="Cumulative Return",
    signal_version="seed1",
    category=SignalCategory.MARKET_STATE,
    description="Compounded cumulative return of close from the first bar.",
    parameters=(),
    required_inputs=(_input("close", "close"),),
    output_columns=(_output("cumulative_return"),),
    streamable=True,
    state_bound_bars=1,
    builder=_cumulative_return,
    stream_factory=_buffered_stream_factory(
        _cumulative_return,
        ("close",),
        ("cumulative_return",),
    ),
    backend_kind="signal_kernel_v1",
)

signal_plugin = cumulative_return_signal

Read it from top to bottom:

  • required_inputs makes close an explicit dependency; the calculation does not reach into a Dataset by itself.
  • parameters=() tells users that this version has no operator-adjustable settings.
  • The builder preserves input row alignment and returns exactly one declared output column.
  • state_bound_bars=1 describes the bounded prior value needed by this calculation. It is not a general lookback setting for all Signals.
  • stream_factory supplies the incremental path for this definition. A Signal that declares a stream should be tested so its batch and incremental results agree on the same input.
  • The final signal_plugin binding is what discovery loads from a plugin's signal.py entry point.

Do not copy the bundled seed1 identity for a new idea. Give your own Signal a new ID, version, description, tests, and output names that match its actual meaning. The bundled example demonstrates the declaration shape; it is not a template for a trading rule or a claim about future performance.

Build and test locally

Use the installed SDK scaffold and its exact imports for the workstation version you are running. The ordinary authoring loop is:

  1. create a dedicated local plugin folder with a signal.py entry point;
  2. implement the declaration and the batch calculation;
  3. write small deterministic tests for normal data, warm-up behavior, missing values, boundary parameters, and output schema;
  4. when the Signal has an incremental path, test batch and stream results against the same input; and
  5. run the SDK validation tools supplied with the development environment.

A good test checks the values you expect, not merely that the function returns an array. It should also confirm that the plugin does not emit extra columns, change row alignment, or accept parameters outside its declared range.

Add it to Signal Library

Put the reviewed plugin in the configured local Signal Library, then open Signals -> Signal Library.

  • Select Sync on an existing Signal when you are updating that one plugin.
  • Select Sync Plugins when you intentionally want to rescan the local library.

After sync, open the Signal in the Inspector and verify its name, version, inputs, parameters, outputs, data requirement, and labels. If the declaration does not match what you wrote, fix the plugin and sync again; do not use catalog metadata as a substitute for correcting code.

Use and evolve a custom Signal

Choose Use in Strategy from the Library to bind a reviewed output to a new Strategy draft. The later Strategy and Study validation still decide whether the chosen Dataset, time window, dependencies, and requested operation can support that use.

When you change the calculation's meaning—its formula, input semantics, output meaning, timing, or parameter interpretation—publish a new Signal version. Keep prior versions available when older Strategies or results need to remain understandable. A cosmetic change to a name, label, or group does not need a new semantic version.

Important boundaries

Installing or syncing a Signal does not certify a strategy, produce a result, or make a calculation suitable for every data source or execution path. It only makes a declared local calculation available for inspection and later validation.

Treat third-party plugins like any other local Python code: review them, keep credentials out of the plugin, and test them on representative data before including them in an important workflow.

You can use Author with AI Companion to develop a clearer specification or review a draft before taking ownership of the code.