ChartnautDocs

Meta

meta({...}) names the definition, gives it a stable slug, and sets kind: "flow" so it can emit events. Call it once, at the top of the entry file.

If you only want lines on the chart and no events, write an indicator instead.

Signature

meta({
  kind: "flow" | "indicator";
  name?: string;
  shortName?: string;
  slug?: string;
  version?: number;
  description?: string;
  category?: string;
  params?: Record<string, FlowParamDef>;
  dataSource?: IndicatorDataSource;
  runOn?: string[];
  runOnReason?: string;
  window?: { on?: Array<"barClose"> };   // when the definition runs
}): void

Field

Notes

kind

"flow" enables events.declare and ctx.emit. "indicator" is paint-only: ctx.emit throws.

slug

Stable id. Studies bind to it with flows.declare({ slug }), and other scripts with indicators.declare({ slug }). Renaming it breaks every consumer.

name / shortName

Library name and chart legend label. shortName falls back to name. Lint meta/missing-name warns when both are missing.

version

Defaults to 1.

category

Library grouping, for example "custom".

params

Legacy settings. Prefer input.* (see below).

dataSource

{ type: "volume-candles", settings: { purgeStrategy: "floor-aware" } } loads the volume tape. The object form fails lint with meta/volume-candles-purge-required without purgeStrategy. See Price and bar data.

runOn / runOnReason

Allowlist of chart timeframes the definition may run on, and the message shown when one is blocked. See Timeframe handles and runOn.

window

{ on: ["barClose"] }, the run trigger. "barClose" is the only value and the default. A warmup or retain key here is refused with window/declare-with-call: history is declared with the warmup(...) and retain(...) calls below.

meta.id is deprecated: use slug. window.timeframes and window.timeframe are deprecated and lint warns (meta/timeframes-deprecated, meta/timeframe-deprecated): declare timeframe("1h") handles instead.

Warmup is required when you keep state

If the script writes state with ctx.accum or a handle's state, deriving it fails with window/warmup-required until you say how far back that state remembers:

  • A lookback of N bars: warmup((w) => w.window(length)).

  • An EMA: warmup((w) => w.ema(length)). A Wilder or RMA smoother: warmup((w) => w.wilder(length)).

  • State that resets each session or day, such as a session's range: warmup((w) => w.day()). A weekly reset: w.span("7d").

  • State that never forgets (a running total, a count, a latched flag): warmup((w) => w.forever()).

Call warmup(...) once, at the top level after the inputs, so it can read their handles. warmup(200) declares a constant. The depth is capped at your plan's indicator memory, and lint warns with window/clamped-to-cap when it is. A stateless definition declares nothing.

retain(...) sets how many recent buckets handle.bars(n) can read, 64 by default: retain({ "1d": 250 }) reads a deep span of one timeframe, and retain((r) => r.span("20d")) keeps 20 days of whichever timeframe is read. Every term is on Warmup and memory.

Example

meta({
  name: "London open drive",
  kind: "flow",
  slug: "london-open-drive",
  shortName: "LOD",
  runOn: ["1m", "5m"],
  runOnReason: "Session logic is intraday",
});

Settings and profiles

Definitions have the same settings as indicators: dialog, every input.* kind including multiSelect, range and array, and layout with when. Read the values from ctx.params.<id> in onBar. Legacy meta.params still works for scalar dials and merges with input.*, but new scripts use input.*. See Settings inputs and dialog.

A definition that declares any input.* must also call dialog({ title }). Without it the inputs reach neither the settings panel nor ctx.params, and deriving fails with settings/dialog-required.

dialog({ title: "Wide bar settings" });
const minRange = input.number({ id: "minRange", label: "Min range", default: 5, min: 0 });

export function onBar(ctx) {
  if (ctx.high - ctx.low < ctx.params.minRange) return;
  // ...
}

A profile is a named set of values for these settings. The script's own defaults are the Default profile. Events are stored per set of settings, so each profile has its own events when you run the definition over history. See Versions and profiles.

Definitions also get pane() and scale(), which route outputs to subpanes and price scales exactly as they do for indicators. See Panes & scales.

Common mistakes

  • Omitting slug when a study needs to bind to the definition, or changing it after studies already do.

  • Using meta.id instead of slug.

  • Keeping state in ctx.accum without a warmup.

  • Writing chart-only math as a definition. Use an indicator.