ChartnautDocs

onBar

A definition's onBar(ctx) runs once per bar, oldest first, exactly as an indicator's does. The difference is the goal: a definition calls ctx.emit when its condition is true, and painting is optional.

Bars, drawing and dependencies are documented in Shared runtime. This page covers what matters for definitions.

Where onBar runs

Where

What happens

Chart and builder preview

onBar runs over the loaded bars, then again on every tick of the forming bar at the right edge.

Running over history

Chartnaut runs the same script on the server over the range you ask for and stores every accepted emit. A long range is split into chunks, and each chunk starts warmup bars early with fresh state.

Agent dry run

The same server run over a preset market, storing nothing.

Two consequences:

  • Declare warmup when you keep state. Each chunk starts from fresh state, so a definition that remembers across bars must say how far back. See Meta.

  • Guard the forming bar. On the chart, onBar re-runs for the forming bar on every tick. The chart rolls back what the previous tick wrote to ctx.accum, but an emit on the forming bar can appear and then vanish as the bar moves. Emit only when ctx.barConfirmed is not false. When a definition runs over history, and in the agent's dry run, every bar is closed and ctx.barConfirmed is true.

if (ctx.barConfirmed === false) return; // act on closed bars only

Checklist

  1. meta with kind: "flow". See Meta.

  2. Declare events, outputs and dependencies at the top level. See Events and snapshots.

  3. Keep cross-bar state in ctx.accum, and declare warmup when you do.

  4. Draw with ctx.plot, ctx.mark and the other verbs. See Drawing on the chart.

  5. Emit with a label. See Emit and labels.

  6. For sessions and other timeframes, use ctx.session(opts) and timeframe(...) handles.

Definition-only ctx members

FlowCtx is the indicator context plus three members.

Member

Role

ctx.emit(id, payload, opts)

Record an event. Requires opts.label.

ctx.snapshot(id, payload)

Record a snapshot for the builder's Snapshots tab.

ctx.store

A plain object for the whole run, cleared between runs. Prefer ctx.accum for per-bar state.

Everything else is shared with indicators: OHLCV, ctx.time, ctx.params, ctx.indicators, ctx.accum, every drawing verb including ctx.note and ctx.trendline, ctx.session, ctx.summary, ctx.instrument, ctx.history and ctx.log.

ctx.isLastBar is always false in a definition, on the chart too. A definition's events are stored and stitched together from runs over different ranges, so no bar in them is reliably the newest. See Price and bar data.

Sessions

Call ctx.session({ period, open?, close?, tz? }) and gate on the returned didOpen, didClose, isOpen and prev. Sessions end by the clock, never by a bar count.

Rule

Severity

Trigger

flow/session-legacy-api

error

ctx.session.isStart, .window or .boundsOf in a definition

flow/session-bar-count-gate

error

length or tf passed to ctx.session

flow/hand-rolled-session-counter

warn

Day rollover worked out by hand, such as Math.floor(t / 86400)

flow/session-bucket-param

warn

A setting that looks like a bar-count session knob

Other timeframes

Declare timeframe("1h") handles at module scope and read handle.last, handle.forming, handle.state and handle.bars(n) in onBar. A handle's own handler must not emit or paint (timeframe/no-output-in-handler). To fire once per higher-timeframe close, emit from onBar gated on handle.closedThisBar.

A selectable timeframe is an input.timeframe handle passed to timeframe(...), never ctx.params.<id>: there is no ctx at module scope.

Rules

  • No module-level let or var for bar state. Use ctx.accum.

  • No hand-rolled emit dedupe. Use oncePer.

  • If a dependency already computes the zones or levels you need, read its lifecycle() instead of rebuilding them. Lint flow/upstream-lifecycle-reimplementation fails when a definition declares an indicator and keeps its own liveZones invalidation machinery.

  • ctx.log.info, .warn and .error write to the builder's Logs tab only.

  • Layers work in definitions exactly as in indicators, declared with output.layer(...). See Layers & rows.