Custom indicators

The Research-tier path for your own Python indicator: what the contract requires, what the admission pipeline checks, and what it cannot check.

On the Research tier you can supply your own indicator in Python. It is the one escape hatch from the library, and it exists because arbitrary strategy-level code — the obvious alternative — would break the guarantee the leak audit makes.

What you submit

A pure function, def name(bars, params) -> pd.Series: no file or network I/O, no clock reads, no unseeded randomness, and the same output every time for the same input. Helper functions and classes in the same file are fine — the purity and import rules apply to the whole source, but only the declared entry point is ever called. bars has a plain integer index; a bar’s timestamp is bars["time"] in epoch seconds.

Alongside the code you set the function name (it follows your def line until you change it), the causal window (the furthest back it reads — the look-ahead check feeds it only that many bars), the output type (a number series, a true/false series, or a small set of named series), and the example params every check runs it with. A description, how it behaves during warm-up and the imports it uses are optional notes. The output has to be one value per bar — there is no way to admit something that draws zones or annotations.

The AI draft option on the page can adapt a script you found elsewhere: it strips what can’t be admitted, checks the rest, and fills in the form for you to review — it never admits anything on its own.

What the checks do

They run in this order and stop at the first failure, which names the specific problem.

  1. Plan — admitting an indicator needs the Research plan.
  2. Code safety — valid Python with one top-level function of your function name taking (bars, params), importing only allowed modules: numpy, pandas (minus its I/O submodules), math, statistics, datetime (minus the clock methods), collections, itertools, and selected scipy submodules. Network, filesystem, process, dynamic import and deserialization modules are rejected.
  3. Runs on sample data — runs on a platform-built OHLCV series with your example params and has to return a series of the right length and type without erroring.
  4. Same result every run — two identical runs must produce bit-identical output.
  5. No look-ahead — the function is run once on the full series and again on progressively truncated windows, and every bar has to match. This is the same discipline as the strategy-level leak audit, one level down.
  6. Speed and memory — completes within a time and memory budget on a long series, and does not scale super-linearly with history length.
What the contract cannot check
  • Whether your formula computes what you meant it to.
  • Whether a parameter is used the way its description says — if the code reads further back than the declared causal window, the extra history just comes through as NaN.
  • Whether the indicator is any use. A causally clean but statistically worthless indicator is fully admissible; the validation stack is where that shows up.

After admission

The indicator goes into your private library under its ID, and admitting the same ID again replaces it. If the whitelist or the contract changes later, an already-admitted indicator is not dropped — it is re-checked the next time you use it, and you are told the specific new requirement if it no longer passes.