Indicator API
The full Context, metadata schema, conditional colors, and AssemblyScript subset.
This is the reference for the indicator API: the Context object your calc() function receives, the metadata schema that shapes it, and the
AssemblyScript subset you can use.
The entry point
Every indicator defines exactly one top-level function:
function calc(ctx: Context): void It returns nothing; outputs are written to ctx.plots. For how and when the
chart calls it, see the overview's execution
model.
Context
| Member | Type | Description |
|---|---|---|
ctx.bar | Bar | The current bar — see below. Read-only. |
ctx.bars | BarHistory | Historical ring buffer, max 200 bars. Index 0 = current bar, 1 = previous, etc. Read-only. |
ctx.inputs | generated | Your declared inputs as typed fields. Read-only. |
ctx.plots | generated | Your declared plots. Writable. Reset to NaN at the start of each new bar; values persist across live ticks of the same bar. |
ctx.barIndex | i32 | 0-based bar index. Stable across live ticks of the same bar. |
ctx.isNewBar | bool | true on the first call for a bar; false on live ticks of the same bar. |
Bar
The current bar's OHLCV plus a timestamp:
declare class Bar {
readonly open: f64;
readonly high: f64;
readonly low: f64;
readonly close: f64;
readonly volume: f64;
readonly timestamp: f64; // Unix seconds, UTC
} Timestamp is Unix seconds, UTC. There is no timezone API and no
per-period info exposed inside the sandbox — you can't tell whether you're on 1m or 1H
from inside calc(). For hour-of-day or day-index logic, use integer math:
const t = <i32>ctx.bar.timestamp;
const utcHour = (t / 3600) % 24;
const utcDay = t / 86400; BarHistory
declare class BarHistory {
readonly count: i32; // up to 200
open(barsBack: i32): f64; // 0 = current bar
high(barsBack: i32): f64;
low(barsBack: i32): f64;
close(barsBack: i32): f64;
volume(barsBack: i32): f64;
} Returns NaN when barsBack >= count. Use only for the lookback
pattern — incremental indicators must keep state in module-level variables instead.
Inputs and Plots
These two classes are generated from your metadata, so the exact shape depends on what you declared. The editor's autocomplete reflects your current metadata in real time. As a reference, an indicator with one int input and three plots gets:
declare class Inputs {
readonly length: i32;
}
declare class Plots {
upper: f64;
middle: f64;
lower: f64;
} For plots that declare a colors array, an additional <name>Color: u32 field appears for selecting the per-bar color index.
Metadata schema
The full set of fields you can declare:
Top level
name— display name. Max 50 chars; letters / digits / spaces /-/./_/(/).shortName— short label shown on the chart. Same charset rules.overlay—truedraws on the price pane;falseopens a separate pane below (RSI, MACD).precision— optional. Number of decimals on the price scale; mainly useful for separate-pane indicators, which get their own scale.inputGroups— optional. Named sections for the settings dialog; inputs opt in via theirgroupfield (see below).inputs— see below.plots— see below.bands— optional horizontal reference lines.filledAreas— optional filled zones between two plots or two bands.
Inputs
| Type | AS type | Notes |
|---|---|---|
int | i32 | Supports default, min, max. |
float | f64 | Supports default, min, max. |
source | — | Price selector (open / high / low / close / hl2 / hlc3 / ohlc4 / hlcc4). See the note below. |
Picked a price source and nothing changed? source inputs currently have no effect on the calculation — the dropdown renders, but the
selection isn't passed to your code. Read ctx.bar / ctx.bars directly.
Every input also takes an optional display name, a description (shown as the ⓘ tooltip next to the input in the settings dialog), and a group — a key into the top-level inputGroups map, which renders
the settings form in titled sections:
"inputGroups": { "bands": { "name": "Bands" } },
"inputs": {
"length": { "type": "int", "default": 20, "description": "Averaging window" },
"multiplier": { "type": "float", "default": 2.0, "group": "bands" }
} Section order follows input order, not inputGroups key order; each group's name (or its key) is the section header.
Plots
Only type: "line" is supported (no shapes, arrows, or bar-colorers). Style
variants:
line,histogram,columns,area,stepLine,circles,cross- With breaks:
lineWithBreaks,stepLineWithBreaks,areaWithBreaks; alsostepLineWithDiamonds
Plot fields:
color— use 6-digit hex like"#2962FF". An 8-digit hex's alpha component is ignored; 3-digit hex works too. Usetransparencyfor opacity instead of alpha.linewidth— 1–4 recommended;linestylesolid | dotted | dashed,transparency0–100.title,visible,showOnPriceScale,showInDataWindow,showInStatusLine,trackPrice.colors— array of hex colors for conditional per-bar coloring (see below).offset— visual shift in bars. Number for fixed shift, string for input ref ("offset": "displacement"), or negated string for left shift.
Use lineWithBreaks if you want gaps at NaN bars —
session ranges, signal lines, "only when condition X" outputs. It's the style meant
for intermittent plots.
Bands and filled areas
Bands are horizontal reference lines: { value, name?, color?, linewidth?, linestyle?, visible? }.
Useful for RSI 30 / 70 or any oscillator zero-line.
Filled areas are semi-transparent fills between two plots or two bands: { from, to, color?, transparency?, title? }. Use them between plots for
clouds (Bollinger, Ichimoku) and between bands for zone shading (RSI 30–70).
Conditional colors
Adding a colors array to a plot creates a <name>Color: u32 field on ctx.plots that picks which color to use per bar — the palette
drives the per-bar color. Out-of-range indices are clamped.
"hist": { "type": "line", "style": "columns",
"colors": ["#26A69A", "#B2DFDB", "#EF5350", "#FFCDD2"] } ctx.plots.hist = value;
ctx.plots.histColor = value >= 0 ? (rising ? 0 : 1) : (falling ? 2 : 3); AssemblyScript gotchas
AS is TypeScript-shaped but stricter. The traps people hit most often:
- Untyped numeric literals default to
i32. Annotate floats explicitly:let x: f64 = 0.0;. - Don't
returnon a live tick without assigning plots. Plots reset to NaN only when a new bar starts; skipping the assignment mid-bar leaves the forming bar frozen at the last value you set. - Strict types. No
anyorundefined. Usef64for prices,i32for counters,bool,void.nulland nullable reference types exist, but indicator math rarely needs them. - Casts use angle brackets.
<f64>(len). Math.minandMath.maxtake exactly two arguments, not rest args.- No closures. Functions can't capture outer-scope variables.
- No
for..of. Use index-basedforloops. sourceinputs don't reach your code — see the callout above.- No
try / catch, noasync, no Promises, no imports.throwcompiles, but it aborts the indicator's calculation — plots stay unset. - No dynamic property access (
obj[key]). (Strings do support the standard methods —indexOf,substring,split… — even though autocomplete doesn't list them.)
What you can use:
- Classes with getters/setters,
for/while/if/switch,const/let. StaticArray<T>,Array<T>.Math.{sqrt, abs, pow, min, max, log, exp, floor, ceil, round, PI, E},NaN,Infinity,isNaN(),isFinite().- These also exist as bare functions:
abs,sqrt,floor,ceil,min,max.
Something missing or wrong? Email support@strategytune.com.