Indicators
An indicator script is a small module — a config object plus one calculation function — that plots on the chart exactly like a built-in, and is callable from strategies too. This page covers the contract, plots/panes/params, using a script on the chart and from a strategy, and how built-ins fit in.
The contract
An indicator script exports two things: config (an IndicatorScriptConfig — name, pane,
params, plots) and calculate(data, params), which takes the full bar history plus the current
parameter values and returns an object of named (number | null)[] series, one per declared
plot key. See the Indicator script API reference for the exact
types.
export const config: IndicatorScriptConfig = {
name: 'My Indicator',
pane: 'separate',
params: { period: { default: 14, min: 1, max: 500, step: 1 } },
plots: [{ key: 'values', kind: 'line' }]
};
export function calculate(data: OHLCV[], params: Record<string, number>) {
const period = params.period;
const values: (number | null)[] = [];
let sum = 0;
for (let i = 0; i < data.length; i++) {
sum += data[i].close;
if (i >= period) sum -= data[i - period].close;
values.push(i >= period - 1 ? sum / period : null);
}
return { values };
}
This is the starter template the Editor seeds when you create a new indicator script — a simple moving average, ready to rename and modify.
Plots, panes, params
pane—'overlay'draws directly on the price pane (like a moving average);'separate'gets its own pane below (like an oscillator).plots— each entry declares akey, akind('line','histogram', or'dots'), and optionally a hexcolorand linewidth. Every plot'skeymust match a key incalculate()'s returned object — declare a plot for every series you return, and return a series for every plot you declare.params—Record<string, { default, min?, max?, step? }>; each entry becomes a numeric parameter with a default value and an optional range/step for the chart's settings UI.
export const config: IndicatorScriptConfig = {
name: 'Close vs SMA %',
pane: 'separate',
params: { period: { default: 20, min: 2, max: 500, step: 1 } },
plots: [
{ key: 'pct', kind: 'histogram' },
{ key: 'zero', kind: 'line', color: '#8a8781' }
]
};
export function calculate(data: OHLCV[], params: Record<string, number>) {
const period = params.period;
const pct: (number | null)[] = [];
const zero: (number | null)[] = [];
let sum = 0;
for (let i = 0; i < data.length; i++) {
sum += data[i].close;
if (i >= period) sum -= data[i - period].close;
if (i >= period - 1) {
const sma = sum / period;
pct.push(((data[i].close - sma) / sma) * 100);
zero.push(0);
} else {
pct.push(null);
zero.push(null);
}
}
return { pct, zero };
}
Two plots, two output keys — a histogram for the percent deviation, plus a zero line for a visual reference, each with its own key in the returned object.
On the chart
Once saved, an indicator script renders through the same generic plot adapter every built-in
indicator uses: your calculate() output feeds straight into the line/histogram/dots
drawing code, and your params become the row's settings fields, in the order you declared them
in config.params. Add it to a chart row by typing its name — an upper-cased version of its
slug — just like typing a built-in's name (e.g. RSI(14)).
From a strategy
A saved indicator script is also callable from a strategy via
ctx.indicator('<slug>', {...}), exactly like a
built-in type. The slug is the lower-cased alphanumeric form of the script's name — spaces and
punctuation are stripped, not spelled out, so "Close vs SMA %" becomes closevssma. A slug that
collides with a built-in indicator type is rejected — built-ins always win — so pick a name that
doesn't shadow one of the entries in the indicator catalogue.
Built-ins
Every indicator listed in the indicator catalogue is available to chart
rows and ctx.indicator() without writing anything — no Editor tab required. Built-in
indicators have no script source (they're platform code, not scripts), so unlike built-in
strategies they don't appear in the sidebar or open in the Editor at all; there's nothing to
mark read-only. The sidebar's Indicators folder holds only My Indicators — every indicator
script there is one you wrote or saved, and it's yours to edit outright. (Built-in strategies do
follow the locked, "Save a copy" flow — see Scripting.)