Overlay vs pane
meta({...}) is the first call in every indicator. It names the indicator, decides where it draws and what data it reads. Call it exactly once: a second call is an error, and an unknown key is an error that lists the valid ones.
Signature
meta({
shortName: string; // required: the legend label
kind: "overlay" | "pane" | "chart"; // required
dataSource?: { type: "candles" }
| { type: "volume-candles"; settings: { purgeStrategy: "floor-aware" | "max-bars" | "session-daily" } }
| "candles" | "volume_candles";
category?: string; // library grouping, e.g. "custom"
runOn?: string[]; // allowed chart timeframes
runOnReason?: string; // shown when the chart timeframe is blocked
window?: { on?: ["barClose"] }; // when the script runs
mode?: "continuous" | "once";
runWindow?: "visible" | "loaded" | { lookback: string } | { bars: number } | { from: number; to: number };
}): voidkind
| Draws |
|---|---|
| On the price pane, over the candles (moving averages, VWAP, bands) |
| In the indicator's own subpane (RSI, MACD, ATR) |
| As the chart itself: the candles are hidden and the indicator appears in the chart type picker. Needs at least one |
kind only sets the default. pane({ id }) declares named subpanes that any output can be sent to, so an overlay indicator can own subpanes too. Routing, pane options and price scales are on Panes & scales.
meta({ shortName: "RSI", kind: "pane" });How much history the script needs
History is declared with two top-level calls placed after the inputs, so they can read input handles. meta holds none of it.
warmup(...) is how far back the script's state remembers. The runtime walks that much history before the range you look at, so accumulated state has settled by the first visible bar and does not change as you scroll.
State | Warmup |
|---|---|
A lookback of N bars (SMA, Donchian, rolling max) |
|
An EMA |
|
Wilder smoothing (RSI, ATR, ADX, RMA) |
|
Resets every session or day (a session VWAP) |
|
Never forgets (a running sum, a count, a trend flag) |
|
meta({ shortName: "RSI", kind: "pane" });
const length = input.number({ id: "length", label: "Length", default: 14, min: 1 });
warmup((w) => w.wilder(length));A script that keeps state in ctx.accum and declares no warmup fails lint with window/warmup-required. A script that keeps no state declares nothing. Declaring more than your plan's indicator memory is clamped, with a window/clamped-to-cap warning. Every term, and how each resolves per timeframe, is on Warmup and memory.
retain(...) is how many buckets stay readable through a timeframe handle's bars(n) (64 by default). Raise it when the script reads a span rather than accumulating over one, for example retain({ "1d": 250 }) or retain((r) => r.span("20d")). The full rules are in Timeframe handles and runOn.
dataSource
Candles (OHLCV) by default. Footprint and volume profile scripts that read ctx.volumePriceLevels declare the volume-at-price tape:
meta({
shortName: "Footprint",
kind: "overlay",
dataSource: { type: "volume-candles", settings: { purgeStrategy: "floor-aware" } },
});Without it ctx.volumePriceLevels is an empty array. The object form needs settings.purgeStrategy, or lint fails with meta/volume-candles-purge-required. Write "floor-aware": the three values behave the same today. See Price and bar data.
runOn
runOn: ["5m", "15m"] limits the chart timeframes the indicator runs on; runOnReason is the message shown on any other timeframe. It is a timeframe list, not a cadence. See Timeframe handles and runOn.
mode: "once"
mode: "once" runs an onRun(ctx) function one time over a fixed bar window chosen by runWindow (default "visible"), then freezes. It never recalculates and never appears in the indicator legend. The Agent uses it for one-off analysis on a chart. It requires kind: "overlay", and a script exports either onBar or onRun, never both.
Not definition meta
Definitions use kind: "flow" and their own fields. See Meta.
