Building Custom Indicators with Pine-Script-like Syntax

How our strategy lab's scripting layer borrows the readability of Pine Script to help you go from idea to plotted indicator in minutes.

If you’ve written a Pine Script indicator before, the strategy lab’s scripting layer should feel immediately familiar — that’s on purpose. Rather than inventing a new domain-specific language, we leaned on syntax and conventions traders already know, and compiled it down to something that runs safely inside our sandboxed executor.

Why not just use raw JavaScript?

We considered it. The problem is that raw JavaScript gives you too much rope: file system access, network calls, unbounded loops. A scripting surface meant for market logic should make the easy things easy — sma(), stdev(), crossover() — and make the dangerous things impossible, not just discouraged. Pine-Script-like syntax gets us both: a small, declarative vocabulary for describing a series, and a runtime that never has to trust the script.

A minimal example

study("Momentum Band", overlay = true)

length = input(20)
basis  = sma(close, length)
band   = stdev(close, length) * 2

plot(basis, color = color.accent)
plot(basis + band, color = color.muted)
plot(basis - band, color = color.muted)

That’s a full indicator: a 20-period moving average with a two-standard-deviation band around it, plotted directly on the price pane. Compile it, attach it to a chart, and it updates on every new candle the same way a built-in indicator would.

What happens after you hit compile

  1. The script is parsed and type-checked against the available series functions.
  2. It’s compiled into a bounded, callback-driven program the Node executor can run per-candle without holding onto state it shouldn’t.
  3. Successful compilations are persisted, so re-running the same strategy later doesn’t recompile from scratch.
  4. Output is streamed back over the run’s WebSocket channel as plot points, so the chart updates live while the run is in flight.

Where to go from here

Start from one of the built-in indicator templates in the Code drawer, change one input, and recompile. Once you’re comfortable with plot() and the built-in series functions, move on to conditional logic — if crossover(fast, slow) is usually the first real “strategy” moment for people coming from simple indicators.