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.
| Method | Path | Summary |
|---|---|---|
| POST | https://api.tickerbot.io/ | Save a named boolean SQL predicate you can reference like a built-in signal. |
| PATCH | https://api.tickerbot.io/ | Edit the expression or description of one of your custom signals. |
| DELETE | https://api.tickerbot.io/ | 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.
| Surface | Works? | How |
|---|---|---|
Ticker subscribe condition | Yes | Referenced in the condition, then frozen (see below). |
Signal name — /v2/signals/{signal} match, /v2/series columns value | Yes | Pass 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) | Yes | Listed alongside built-ins, with its expr returned. |
Scan q (POST /v2/scan) | Yes | Name it in the WHERE clause; it expands to its stored SQL at request time. |
Scan select / fields / order | No | These select or sort signals; a custom is a boolean predicate — filter with it in q instead. |
Series columns (GET /v2/series) | Yes | Compiled per row like a built-in boolean; works as a transitions_only driver too. |
Events grammar (/v2/events q / event_q) | Partly | The 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) | Yes | Expanded 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.)