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 |
|
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 |
Agent dry run | The same server run over a preset market, storing nothing. |
Two consequences:
Declare
warmupwhen 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,
onBarre-runs for the forming bar on every tick. The chart rolls back what the previous tick wrote toctx.accum, but an emit on the forming bar can appear and then vanish as the bar moves. Emit only whenctx.barConfirmedis notfalse. When a definition runs over history, and in the agent's dry run, every bar is closed andctx.barConfirmedistrue.
if (ctx.barConfirmed === false) return; // act on closed bars onlyChecklist
metawithkind: "flow". See Meta.Declare events, outputs and dependencies at the top level. See Events and snapshots.
Keep cross-bar state in
ctx.accum, and declarewarmupwhen you do.Draw with
ctx.plot,ctx.markand the other verbs. See Drawing on the chart.Emit with a
label. See Emit and labels.For sessions and other timeframes, use
ctx.session(opts)andtimeframe(...)handles.
Definition-only ctx members
FlowCtx is the indicator context plus three members.
Member | Role |
|---|---|
| Record an event. Requires |
| Record a snapshot for the builder's Snapshots tab. |
| A plain object for the whole run, cleared between runs. Prefer |
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 |
|---|---|---|
| error |
|
| error |
|
| warn | Day rollover worked out by hand, such as |
| 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
letorvarfor bar state. Usectx.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. Lintflow/upstream-lifecycle-reimplementationfails when a definition declares an indicator and keeps its ownliveZonesinvalidation machinery.ctx.log.info,.warnand.errorwrite to the builder's Logs tab only.Layers work in definitions exactly as in indicators, declared with
output.layer(...). See Layers & rows.
