ChartnautDocs

Declaring indicator dependencies

A script can build on another indicator, such as ATR or VWAP, by declaring it once at the top of the file and reading its values on ctx.indicators. Indicators, definitions and studies declare dependencies the same way.

Declare

indicators.declare({ slug: "atr", as: "atr14", version: 1, settings: { period: 14 } });

Field

Role

slug

The indicator's slug: a built-in, a scripted indicator or a definition

as

Your local name. You read ctx.indicators.<as>. Unique within the script

version

Required. The exact version you read, so an upstream edit never changes your numbers. Built-in indicators always use version: 1

settings

Optional. Merged on top of the dependency's default settings, so pass only what you change

Call it at the top level, next to the inputs. A missing version fails lint with indicators/missing-version, a version that does not exist with indicators/version-not-found, and an unknown slug with indicators/unknown-slug. Two declarations with the same as fail with indicators/duplicate-as, and a script that ends up depending on itself fails with indicators/cycle.

When the Agent writes a script, it looks up a dependency's real output keys with resolve_dependency_contract, and asks you to approve adding one with request_add_indicator. Look the contract up the same way before you guess a key.

Read in an indicator or definition

Values are always nested under the dependency's declared output key, even when it has a single output:

function onBar(ctx) {
  const v = ctx.indicators.atr14.atr.value; // ctx.indicators.<as>.<outputKey>
  if (v == null) return;
  ctx.plot("atr_copy", v);
}

There is no .value on the alias itself. ctx.indicators.atr14.value fails lint with indicators/container-series-read, which also names the keys that exist.

Each output key

Member

Returns

value

This bar's value, never carried forward. undefined on bars the dependency did not emit

at(offset)

The value offset emits back. It counts emits, not bars: on a sparse output, at(1) is the previous emitted value, wherever it was

last()

The most recent defined value, carried forward over gaps. Not a sign that the value is fresh

lastTime()

Bar time of that value, unix seconds

history(maxBars?)

Recent values, oldest first, with gaps kept as undefined

The alias itself

Method

Returns

lifecycle(query?)

Formation and close state of the dependency's ranges, hlines and trendlines

ranges / hlines / marks / plots / trendlines(query?)

Raw paint rows. Not the authority on whether something closed

layer(id, query?)

Rows of one of the dependency's layers. See Layers & rows

A range is not a numeric series. To ask which zones are alive on this bar, use lifecycle:

const live = ctx.indicators.fvg.lifecycle({ outputId: "gaps", at: ctx.time });
for (const z of live) {
  // z.bounds.price1 / price2, z.createdAt, z.meta
}

A lifecycle query takes outputId, kind ("range", "hline", "trendline"), state ("live", "closed", "any", default "any"), at, and the window filters after, before, limit and window: [secondsBefore, secondsAfter]. at returns instances alive on that bar and ignores after / before. state: "live" means still live at the end of the loaded history, so a zone that closed later does not count on earlier bars: use at: ctx.time for "alive on this bar". An outputId the dependency never declared returns nothing, silently, which looks exactly like "the pattern never formed". Each instance carries instanceId, state, createdAt, updatedAt, closedAt, closeReason ("predicate", "event", "replace", "expired"), bounds and meta.

Inside a timeframe handler, ctx.indicators has the same shape, computed on that timeframe's own bars. A dependency that is itself a scripted indicator reads undefined there.

An indicator with a bandTemplate exposes one key per row, such as upper_2. Get the real keys from the contract rather than guessing.

Read in a study

A study reads the same nested shape, positioned at the event's time:

export function onEvent(ctx) {
  const atr = ctx.indicators.atr14?.atr?.value;
  if (atr == null) return;
  ctx.collect("atr", atr);
}

Studies also get valueAt(unixSec), valuesInRange(from, to) and historyAt(unixSec, maxBars?) on each output key, for reading at a time other than the event's, such as the bar where price touched a level. A study declares the definitions whose events it measures with flows.declare: see Declarations.

Empty values are normal

A correct declaration still gives undefined during the dependency's warmup, on bars it did not emit, and across data gaps. Guard every read. See Dependency outputs.