← tickerbot.io
View as markdown

Custom signals

A custom signal is a signal you define: a named boolean SQL predicate over the other signals. Once you create one, it behaves like a built-in: reference it by name anywhere a WHERE clause is accepted, or use it as a signal name across the read and subscribe endpoints.

MethodPathSummary
POSThttps://api.tickerbot.io/v2/signalsSave a named boolean SQL predicate you can reference like a built-in signal.
PATCHhttps://api.tickerbot.io/v2/signals/{signal}Edit the expression or description of one of your custom signals.
DELETEhttps://api.tickerbot.io/v2/signals/{signal}Remove a custom signal. Refused by default if anything references it.

Where you can reference one

A custom signal is a boolean predicate: you match with it anywhere a filter goes, and read it as a per-bar boolean on series — but it can’t be a scan select/order target.

SurfaceWorks?How
Ticker subscribe conditionYesReferenced in the condition, then frozen (see below).
Signal name — /v2/signals/{signal} match, /v2/series columns valueYesPass the custom’s name as the signal. Indistinguishable from a built-in. (Events with kind=signal is the exception: firings are precomputed for built-ins only.)
Signal catalog (GET /v2/signals?kind=custom)YesListed alongside built-ins, with its expr returned.
Scan q (POST /v2/scan)YesName it in the WHERE clause; it expands to its stored SQL at request time.
Scan select / fields / orderNoThese select or sort signals; a custom is a boolean predicate — filter with it in q instead.
Series columns (GET /v2/series)YesCompiled per row like a built-in boolean; works as a transitions_only driver too.
Events grammar (/v2/events q / event_q)PartlyThe base grammar is a fixed four-column namespace, so custom names don’t resolve — except under join=state, where they inline like built-in state signals.
Webhook triggers (scan / ticker / signal)YesExpanded and frozen into the stored trigger at creation.

Signals can reference other signals

Compose small predicates into bigger ones — up to five levels deep.

A custom signal’s expression may reference other custom signals by name. References expand recursively at request time, up to five levels of nesting; a deeper chain — or a circular reference — returns 400 invalid_query.

Two guardrails are enforced when you create a signal, so a reference is always an unambiguous lookup: a signal can’t reference itself (rejected at create), and its name can’t collide with a built-in signal name (returns 409 name_collision).

Subscriptions freeze the definition

A webhook captures the SQL as it was the moment you created it.

When a custom signal is used in a webhook trigger, its SQL is expanded and stored on the subscription at creation time. Editing the signal later does not retroactively change existing webhooks — they keep firing on the definition they were created with. Re-create the subscription to pick up a new definition.

Keep them single-ticker for cross-endpoint use

Cross-ticker references work in scan, but not on the signal endpoints.

Scan rewrites cross-ticker references (e.g. spy.close) as it expands your query, so a custom signal that compares one ticker against another works in a /v2/scan q. The /v2/signals family (match, history, events, subscribe) does not perform that rewrite, so a custom that uses cross-ticker references will return a 400 there. Keep a signal single-ticker if you want to use it everywhere.

Two words

Two words, one meaning each: custom is what a user-authored signal is — the kind value (counterpart of builtin) and the word in field names and error codes — while expression refers only to the SQL text in the expr field. (kind=expression is accepted as a legacy filter alias.)