// TRADERVIEW — WALKTHROUGH

Tutorial index Docs hub
Progress
10 / 18

10.Custom indicators — AST you write once, reuse everywhere

Author an indicator in the small DSL, save it to custom_indicators, evaluate it per symbol via POST /custom-indicators/eval/{sym}, and reference it by name from chart overlays, scanner filters, alert rules, and backtest strategies. Compiles to the same stryke bytecode the backtest engine runs.

The DSL — small on purpose

Custom indicators are pure functions over a bar series. The DSL has:

  • Bar accessorsopen, high, low, close, volume, time.
  • Built-in TAsma(x, n), ema(x, n), rsi(x, n), atr(n), stdev(x, n), bb_upper(x, n, k), highest(x, n), lowest(x, n), plus the ~30 standard indicators.
  • Arithmetic, comparison, boolean operators.
  • Time / calendar accessorshour, minute, day_of_week, is_market_open, is_first_15_min.
  • References to other custom indicators — by name. The compiler resolves the DAG and detects cycles.
  • Plot hints@plot(line, color = "#05d9e8"), @plot(histogram), @plot(none). Controls how the chart overlay renders the result.
// vwap_distance_z — z-score of price distance from VWAP
@plot(line, color = "#ff2a6d", subpane = true)

let vw   = vwap(session)
let dist = close - vw
let z    = (dist - sma(dist, 200)) / stdev(dist, 200)
return z

Indicators are stateless w.r.t. each other — every call sees the same input bar series. Look-back is implicit through the windowed TA functions; no manual indexing into "the bar N ago".

Authoring + saving

The Custom Indicators tile is a list + editor. Each indicator has:

  • Name — unique per user. Used to reference the indicator from other places.
  • Body — the DSL source.
  • Default parameters — if the body references undeclared identifiers (e.g. fast_len, slow_len), they're surfaced as parameters with defaults.
  • Tags — for organization.

The editor has live syntax highlighting + inline errors. POST /custom-indicators/eval/{sym}?from=&to=&tf= evaluates the indicator against the symbol over the date range and renders a preview chart in the editor before you save.

Where you can reference custom indicators

  • Charts overlays. Add my_indicator from the overlay menu; it renders per its @plot hint (line / histogram / none).
  • Screener filters. The screener (step 08) accepts custom-indicator expressions in its filter row. "my_volume_spike > 3.0".
  • Alert rules. Alerts (step 11) take a boolean expression over indicators; custom indicators show up by name.
  • Strategy alerts. AND/OR/NOT boolean composition over base alerts (step 11). Each base alert can reference a custom indicator.
  • Backtest strategies. The entry / exit / sizing expressions in a backtest preset (step 09) can call any of your custom indicators by name.

The compiler resolves the call graph and rebuilds the bytecode whenever any indicator in the graph changes — backtests / alerts / overlays automatically pick up the new logic.

Evaluation modes

The same bytecode runs in three modes:

  • Live — per incoming tick or per just-closed bar; result lands in the WebSocket alert / scanner pipelines.
  • Historical — over a date range; renders chart overlays + backtest signals.
  • One-shot — single evaluation at a specific bar; used by the screener and the per-symbol research dossier.

The compiler emits the same bytecode in all three modes. The only difference is how bars are streamed in; the indicator code itself is identical.

Performance & cost

A typical indicator costs sub-microsecond per bar after JIT warm-up. The screener evaluates indicators across a 3 000-symbol universe in the time it takes to fetch the bars. Backtest walk-forward sweeps that evaluate the same indicator 1 000 times against 5 years of intraday bars finish in seconds, not minutes.

The expensive piece is the bar fetch, not the indicator math. The bars cache (per-symbol, per-timeframe) is sized aggressively for exactly this reason.

Tip When a backtest sweep is slow, the bottleneck is almost always (a) a long-lookback indicator (sma(x, 500) over 1m bars = a lot of evaluation) or (b) a too-broad universe. Profile by halving each axis once.