Start here
How a Flowscope script is organised, what happens when it starts, and when each handler runs.
Every Flowscope script has two kinds of content. Declarations describe what the script contains: its settings, its data, its outputs and its reusable logic. Handlers describe what happens when market data changes.
You do not need the whole language to build something useful. If you are new, read this page and then build your first indicator. If you already write TradingView Pine Script, start with coming from Pine Script. Pick up the guides as individual topics become relevant.
How a script is organised
A script is a single file. The first line is the header script "Name". Every line after it belongs to a top-level declaration. Loose statements between declarations are not allowed; executable code lives inside a function or a handler.
| Declaration | What it defines |
|---|---|
input | A setting shown in the script’s settings panel. |
data | A market-data subscription and the name you read it by. |
state | A value that is initialised once and kept across all handler calls. |
setup | A value computed once at start, or a family of values whose shape is fixed at start. |
pane | A sub-pane below the price chart. |
window | A separate window with its own panels: charts, tables or heatmaps. |
plot | A visual output, such as a line or a histogram, that handlers feed. |
fill | A shaded area between two plots. |
alert | A named alert that handlers can trigger on live data. |
strategy | Turns the script into a strategy and sets backtest properties. See strategies. |
type | A record with named fields and optional methods. |
fn | A reusable calculation or action. It runs only when called. |
on | A handler that runs for one event of one subscription. |
Grouped declarations
input, data, state, setup, plot, fill, pane and alert have a grouped form. Put one declaration per line inside parentheses:
input (
length = input.int(20, title: "Length", min: 1)
upColor = input.color(color.up, title: "Up")
downColor = input.color(color.down, title: "Down", sameLine: true)
)This is exactly the same as writing three separate input lines. Grouping only helps you keep related declarations together. script, strategy, type, fn, window and on are always written on their own.
One statement goes on one line, and there are no semicolons. To split a long expression, break the line after an operator such as +, && or ?.
A complete script shape
The script below draws the previous four-hour range on any chart. It uses most kinds of declaration at least once.
script "Previous 4h range"
type Range {
high: float
low: float
fn mid() -> float {
return (self.high + self.low) / 2.0
}
fn width() -> float {
return self.high - self.low
}
}
fn rangeOf(high: float?, low: float?) -> Range? {
if high == null || low == null {
return null
}
return Range { high: high, low: low }
}
input (
edgeColor = input.color(color.blue, title: "Range edges")
showMid = input.bool(true, title: "Show midpoint")
)
data (
chart = subscribe(data.ohlcv)
h4 = subscribe(data.ohlcv, timeframe: 4h)
)
state last: Range? = null
plot (
top = plot.line(title: "4h high", color: edgeColor, style: linestyle.step)
middle = plot.line(title: "4h mid", color: color.gray, style: linestyle.dotted)
bottom = plot.line(title: "4h low", color: edgeColor, style: linestyle.step)
)
on h4.close {
last = rangeOf(h4.high, h4.low)
}
on chart.update {
let r = last
if r != null {
top.plot(r.high)
middle.plot(showMid ? r.mid() : null)
bottom.plot(r.low)
}
}How the parts fit together:
Rangegroups two prices that always travel together and adds two methods.rangeOfis a plain function. Declaring it does nothing; it runs only whenon h4.closecalls it.- Two inputs let you pick the edge colour and hide the midpoint.
- Two subscriptions follow the chart’s candles and four-hour candles on the same market.
state lastremembers the most recent confirmed four-hour range between handler calls.- The three plots declare their default look.
on h4.closeruns once per confirmed four-hour bar and stores the range.on chart.updateruns on every chart update and draws whatever range is stored.
last is a state value, which is a field-like storage slot. The handler copies it to the local r before the null check, because null checks only narrow locals. The missing values guide explains why.
What happens at start
When you add a script to a chart, Flowscope prepares it in this order:
- Compile. The source is parsed and type-checked. Errors appear with a code you can look up in diagnostics.
- Inputs. Each input takes its saved value, or its default on first use.
- Setup.
setupexpressions, panes, windows, plots, fills and alerts are created. Setup can call setup-safe built-ins and readcontextand inputs, but cannot call your ownfns. - State. Every
stateinitialiser runs exactly once. - History. The loaded history of every subscription is replayed bar by bar, in time order, through your handlers.
- Live. As new data arrives, the chart runs the script again over its history, so the newest bars go through the same handlers.
Because history and live data go through the same handlers, an indicator you see on old bars behaves the same way on new ones. If you change an input, the script restarts from step 2 and recomputes history.
A script may take at most 10 million steps in one update. A script that exceeds it stops with an error instead of slowing the terminal. See performance.
Handlers and when they run
Period-based subscriptions, data.ohlcv, data.oi, data.vd, data.cvd and data.stat, support three handlers:
| Handler | When it runs | Typical use |
|---|---|---|
on x.open | Once when a new period of x starts. | Reset per-bar counters, start a new zone. |
on x.update | Every time x changes, including many times while a live bar is forming. | Live values, drawings that follow price. |
on x.close | Once when a period of x is confirmed. | Final values, confirmed signals, strategy orders. |
A data.trades subscription uses a single handler, on t.trade(tr) { }. It runs once for every print. tr is a Trade record with time, price, size and isBuy.
data.book and data.volume are snapshot sources. They have no handlers of their own; you read them inside a handler on another subscription.
A script can have as many handlers as it needs, but only one for each subscription and event pair. Handlers communicate through state: one handler writes a value, a later call of another handler reads it.
During history replay, a bar triggers open, then update, then close. On a chart, new data re-runs the script over the loaded history, at most every 0.75 s: confirmed bars go through close, the forming bar through open and update.
Continue with your first indicator, or read data and update events for the full rules on what each subscription returns inside each handler.