Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.darvas.app/llms.txt

Use this file to discover all available pages before exploring further.

Two phases of execution

Every custom indicator script has two distinct execution phases:
// PHASE 1: top-level init
// Runs ONCE when the indicator is first evaluated.
// Declare inputs, register plots, create Series here.

const length = input.int("Length", 14, { min: 1, max: 200 });
const src    = input.source("Source", "close");

plot("MA", null, { color: "#22c55eFF", linewidth: 2 });

// PHASE 2: onBar callback
// Runs ONCE per bar, oldest to newest, then once per realtime tick.

onBar((i) => {
  const value = ta.sma(src, length);
  plot("MA", value);
});
Top-level code runs once. Per-bar computation goes inside onBar. Mixing them up is the most common mistake new authors make.

The i argument

onBar receives a single argument i - the current bar index (0 = oldest bar in the window, ctx.length - 1 = newest). It is equivalent to calling ctx.i() inside the callback.
onBar((i) => {
  // i === ctx.i() === ctx.barIndex()
  if (i === 0) {
    console.log("First bar in window");
  }
  if (ctx.isLast()) {
    console.log("Realtime bar");
  }
});

What belongs where

Top level (runs once)Inside onBar (runs per bar)
input.* declarationsplot(name, value) - writing values
plot(name, null, opts) - series registrationta.* calculations
Series(name) creationEntity .set() / .delete() calls
hline(value, opts)bgcolor / barcolor calls
Line/Box/Label/Marker initial creationconsole.log for per-bar debugging

Side-effect ordering

  1. All input.* values are resolved from the Inputs panel (or defaults).
  2. All plot(name, null, opts) calls register plot series.
  3. onBar callback is registered but not yet invoked.
  4. The runtime replays all historical bars, calling onBar(i) for each in ascending bar-index order.
  5. On each realtime update, onBar(i) is called again for the current bar.
Calling input.* inside onBar throws a RuntimeError. Always declare parameters at the top level.

Minimal template

// overlay = true
const length = input.int("Length", 14, { min: 1, max: 200 });
const src    = input.source("Source", "close");

plot("MA", null, { color: "#22c55eFF", linewidth: 2 });

onBar((i) => {
  plot("MA", ta.sma(src, length));
});

Inputs

All input types and the form controls they produce.

Bar context

ctx.open, ctx.close, ctx.i, ctx.isLast and friends.

Series

Persistent indexed storage across bars.

Plot basics

How to register and emit plot values.