Skip to main content
Know what may go where in a script and the order its handlers run in. The editor enforces these rules as you type, each with a diagnostic code.

The shape of a script

A script is one TypeScript file with no imports. Its globals are the API. The top level runs once, when the script loads (setup). It may contain only:
  • indicator(name, options), first;
  • const declarations whose values come from the API’s constructors (input.*, bars(), trades() and the other streams, state.*, series, tally, tallyBy, ta.*, plot.*, alert, draw.table), from literals, or from other constants and helper functions;
  • plot.level(...) calls;
  • function declarations: pure helpers, or factories called at setup;
  • handler registrations: stream.onBar(fn), stream.onClose(fn), trades.onTrade(fn), liquidations.onLiquidation(fn).
Handlers hold the logic and run per bar or per event. A local variable in a handler starts fresh on every run. Anything that persists is declared state.

What the compiler enforces

  • Constructors only at the top level; handlers registered only at the top level, unconditionally.
  • Each state object, series and indicator belongs to the one stream whose handler updates it.
  • Event handlers (onTrade, onLiquidation) may change only tallies and live state.
  • Tallies are filled only by event handlers.
  • Plots, markers and drawings are set only in the chart’s own bars() handlers.
  • No import or export, eval, with, async, generators, classes, top-level let or var, top-level collections that change, dynamic indexing of API objects, Date, timers, fetch or other globals.
  • Math is the engine’s own: exp, log, pow, the trigonometric functions and the rest give the same bits in every browser, which can differ in the last bit from a browser console. There is no Math.random: use random(seed).

The order things run in

For chart bar i, in history:
  1. Everything that happened before the bar closed, merged by time: trades and liquidations with a time before its close, and other bar streams’ bars that close at or before it (their onBar with confirmed true, onClose, then their commit). At equal times a bar close comes first, and a longer interval before a shorter one.
  2. The chart’s onBar, with confirmed true, then its onClose.
  3. The commit. Tallies empty for the next bar.
Live, the forming bar runs again from the last commit on every update, with confirmed false. Only the latest update matters, so updates can be skipped without changing the result.
Another stream read from a handler gives its last closed value, “as of” that moment: there is no lookahead. .forming on a stream reads its developing bar live, which repaints; on history it is the last closed bar. See data and timeframes.
An indicator advances only when update runs, so update it on every bar. Missing numbers are na: arithmetic with na is na, and comparisons with it are false. See missing values.

When a script runs again

The script runs again from setup, over the whole history, when older history is loaded, the market or interval changes, or an input changes. That takes a few tens of milliseconds per 100,000 bars for a typical script. Values near the oldest loaded bar may change, as on TradingView.

Next step

Keep values between bars with state.
Last modified on October 16, 2026