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:
| Declaration | Why it matters |
|---|---|
| Signal ID and version | Identify the exact calculation used by a Strategy or result. Keep the ID stable; make a new version when semantic behavior changes. |
| Name and description | Explain the calculation to Library users. |
| Category, group, and labels | Organize the Library without changing the calculation. |
| Required inputs | Declare every market field or upstream Signal output the plugin reads. |
| Parameters | Give each adjustable value a type, default, and supported bounds or choices. |
| Output columns | Declare each produced column, its value type, and whether it can be used as a Signal output. |
| Data requirements and context | State required data shape and facts such as timezone, tick size, point value, or session calendar when the calculation needs them. |
| History and streaming support | Declare 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.
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_signalRead it from top to bottom:
required_inputsmakesclosean 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=1describes the bounded prior value needed by this calculation. It is not a general lookback setting for all Signals.stream_factorysupplies 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_pluginbinding is what discovery loads from a plugin'ssignal.pyentry 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:
- create a dedicated local plugin folder with a
signal.pyentry point; - implement the declaration and the batch calculation;
- write small deterministic tests for normal data, warm-up behavior, missing values, boundary parameters, and output schema;
- when the Signal has an incremental path, test batch and stream results against the same input; and
- 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.