General FAQ

View as MarkdownOpen the editor

Frequently asked questions about wrun: getting started, data sources and context, technical indicators, plotting, troubleshooting, language features, performance, and the error messages people search for.

Getting started

What is a wrun indicator? One file in TypeScript syntax. You write it in the chart's editor, Run compiles it in your browser and draws it on the chart, and Publish puts it on OpenMarket for you, the people you invite, or everyone. Once published it runs in each reader's browser, or on OpenMarket's servers when its code is Protected. Every output is a number series: a line, a shape, a box coordinate, and every drawn one is a value an alert can follow. The module has no filesystem, no network, and no order capability. Your first indicator walks through the first one.

Do I need to know TypeScript? The file is AssemblyScript: TypeScript syntax over fixed-width numbers. You need about a page of it: let x: f64 = NaN declares a number, i32(...) turns a float into a whole number, function, if, for, and class work as in TypeScript, and there is no any, no closures capturing locals, and no dynamic typing. The Moving Average starter (sma-codefirst) is a handful of lines with a comment on every statement; if you can read it, you can write one.

Why does an indicator declare its outputs instead of drawing them? An indicator declares what it reads and writes at the top of the file and computes numbers once per bar in onBar(); the chart draws from the declarations. That is what lets one file compile in your browser, compute the same way in each reader's browser or on OpenMarket's servers, and carry alerts that run in OpenMarket's cloud. What is wrun has the full picture.

I have a file written with init, state, finalize and reset. Does it still work? Yes, unchanged: a file that exports those four functions builds and runs exactly as it did, next to files written with onBar(). Execution model describes that form.

Data sources and context

How does an indicator know which market the chart shows? Nothing you write. bar.close(), or an input("close", ohlcv.close) with no pin, follows the chart's own market and interval, and the module never sees a market name. A fixed market is a pinned secondary input, input("btc", ohlcv.close, { symbol: "BTCUSDT", exchange: "BINANCE_FUTURES" }): the chart fetches that market's candles and joins them to yours bar by bar (Data sources).

Why is the start of my line empty? The TA classes return NaN until their window fills, and a NaN output draws nothing on that bar. That is warm-up, not a bug: a 200-bar average has no value on bar 50. Load more history if the line never appears, and read Execution model for how warm-up works.

Can I read the previous bar? Yes, with History from ./sdk/stats: push() the value once per bar in onBar(), then ago(1) is the previous bar's value, and max(), min(), mean() and sum() read the whole window (Stats, history and lists). There is no close[1] index, and onBar() sees exactly one bar. By hand it is the same idea: keep the value in a module-level variable when you see it, and a window is a StaticArray<f64> you fill as a ring buffer, sized from the param's max so it is allocated once:

param("bars", 5, { min: 2, max: 50, description: "Bars in the trailing window" });
output("change", line, lower, { description: "Close minus the previous bar's close" });
output("window_high", line, overlay, { description: "Highest high of the trailing window" });

const MAX_BARS = 50;
const highs = new StaticArray<f64>(MAX_BARS);
let n: i32 = 5;
let cursor: i32 = 0;
let count: i32 = 0;
let prevClose: f64 = NaN;

function onStart(): void {
  n = i32(p_bars());
}

function onBar(): void {
  const close = bar.close();
  // The previous close is whatever we kept from the previous call: there is no close[1] to read.
  const change = isNaN(prevClose) ? NaN : close - prevClose;
  prevClose = close;
  highs[cursor] = bar.high();
  cursor = (cursor + 1) % n;
  if (count < n) count += 1;
  if (count < n || isNaN(change)) return;
  let h = -Infinity;
  for (let i = 0; i < n; i++) if (highs[i] > h) h = highs[i];
  out_change(change);
  out_window_high(h);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

Can I analyze multiple markets in one indicator? Yes: one pinned ohlcv input per market, symbol and exchange always together. The first input stays unpinned and defines the grid; the chart fetches each pinned market's candles at the chart's interval and joins them by timestamp under the input's missing policy, live from that market's own feed. Other sources (trades, funding, open interest, liquidations) always read the chart's own market. Stocks, ETFs, forex, gold and silver pin the same way (Stocks, forex and gold), and an alert reads the pinned markets too (Alerts):

// The first input is the grid: it follows the chart's own market.
input("close", ohlcv.close);
// Pinned inputs read fixed markets; symbol and exchange always pin together.
input("btc_close", ohlcv.close, { symbol: "BTCUSDT", exchange: "BINANCE_FUTURES" });
input("eth_close", ohlcv.close, { symbol: "ETHUSDT", exchange: "BINANCE_FUTURES" });
output("eth_btc", line, lower, { description: "ETH priced in BTC" });
output("vs_btc", line, lower, { unit: "%", description: "This market's close as a percent of BTC's" });

function onBar(): void {
  const btc = in_btc_close();
  if (isNaN(btc) || btc <= 0.0) return;
  out_eth_btc(in_eth_close() / btc);
  out_vs_btc((bar.close() / btc) * 100.0);
}

Can I write an indicator without a data source? Not without the chart's own candles. A file with onBar() and no input(...) line runs on them: the chart's close is the bar grid the module walks, whether or not the code reads bar.close(). It still needs an output, and Run refuses a file without one by name (a file with onBar() needs at least one output(...) statement: declare what the Indicator draws, e.g. output("value", line, overlay), and write it in onBar() with out_value(...)). A constant you want to draw is an output written to the same value on every bar.

Can I declare inputs inside if, a loop, or a function? No. Declarations are read from the text before anything runs, so they are top-level statements of the entry file only; one inside a function is refused with input(...) declarations must be top-level statements, not inside a function, class, or expression. Every input is fetched before the first bar, so there is no such thing as a conditional subscription.

How do I read the order book? Declare a celled input over the book class behind the first input line, input("close", ohlcv.close) then input("book", book.cells, { max_cells: 1000, block_size: 10 }), and scan its [price, size, side] tuples in onBar() through the generated in_book_cells() count and in_book_view() buffer. The chart serves the book it loads for its own market, up to 500 price levels a side, so a full book is up to 1,000 tuples and a smaller max_cells refuses the run; block_size is required by the declaration, but the chart does not read it. Order flow has the bid and ask sums as worked scans.

How does an indicator handle data gaps? With a declared policy per input rather than interpolation. A secondary input carries its last value forward by default (missing: "carry"), delivers NaN on bars without an observation under missing: "nan", or 0 under missing: "zero". Line plots do not interpolate across NaN; a gap is a gap. When you want your own fill, do it in onBar():

input("close", ohlcv.close);
// A sparse feed: bars with no liquidation deliver NaN instead of repeating the last value.
input("liqs", liquidations.liquidations, { missing: "nan" });
output("liq", line, lower, { description: "Liquidation volume this bar, 0 on quiet bars" });
output("last_liq", line, lower, { description: "The most recent liquidation volume, carried forward" });

let lastLiq: f64 = NaN;

function onBar(): void {
  const x = in_liqs();
  let liq: f64;
  if (isNaN(x)) {
    liq = 0.0; // your own zero policy
  } else {
    liq = x;
    lastLiq = x; // your own forward fill
  }
  out_liq(liq);
  out_last_liq(lastLiq);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

On the primary input, "nan" and "zero" do more: they densify the grid, so a naturally sparse feed becomes one row per bar (Data sources).

Technical indicators

Why am I getting NaN in my calculations? Three usual causes: a TA class that has not warmed up yet, a division by zero, or an input under missing: "nan" on a bar with no observation. Check with isNaN(x) (there is no isnum; !isNaN(x) is the same test), and decide per value: return from onBar() before any write (nothing is drawn for that bar), write NaN to that one output, or substitute. A two-line nz helper covers the substitution case (Best practices).

Which TA classes exist? 52 stateful classes in ./sdk/ta (Sma, Ema, Rsi, Atr, Bb, Macd, Stoch, Adx, Supertrend, an anchored Vwap, and the rest). The TA library lists every class with its constructor, update() arguments, and fields. ./sdk/ta-plus adds 30 more of the same shape (Dema, Tema, Trix, Kama, Aroon, Vortex, the volume lines, Pine's two-length Dmi, SourceStoch and SourceVwap over any source, HeikinAshi, and the rest) on Extra indicators. The kit pages beside it cover what sits around the math: Strings and text, the Clock and sessions kit, the Colors, the Order flow, the Levels kit and the Market structure kit.

How do I use a param as a period? Read it in onStart() and cast it: sma = new Sma(i32(p_period())). Params are f64; a period is i32; the cast is explicit and the compiler refuses to guess.

Plotting and visualization

How do I draw several lines on the same chart? One output(...) per line. overlay puts it on the price pane, lower in its own pane; color, width, opacity, and description are per output (Plotting).

Can I plot conditional signals? Yes: a shape output draws a mark at its value only on bars where a second, data-only output named in shape_where is nonzero. The decision is a number:

param("len", 14, { min: 2, max: 200 });
param("level", 30, { min: 5, max: 50, description: "RSI at or below this counts as oversold" });
output("rsi", line, lower, { color: "#a78bfa" });
// The mark sits at the output's value (this bar's low) and draws only on bars where the gate is 1.
output("buy", shape, overlay, { color: "#22c55e", shape_where: "oversold" });
output("oversold", none);

let rsi = new Rsi(14);
let level: f64 = 30.0;

function onStart(): void {
  rsi = new Rsi(i32(p_len()));
  level = p_level();
}

function onBar(): void {
  const value = rsi.update(bar.close());
  if (isNaN(value)) return;
  out_rsi(value);
  out_buy(bar.low());
  out_oversold(value <= level ? 1.0 : 0.0);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

How do I change plot colors dynamically? With a palette and a decision: color_by names a data-only output, and each bar's value indexes colors (floored; a missing or out-of-range value falls back to entry 0):

param("fast", 10, { min: 1, max: 200 });
param("slow", 30, { min: 2, max: 400 });
// Each bar's trend value (0 or 1) indexes the palette: red below the slow average, green above.
output("price", line, overlay, { width: 2, color_by: "trend", colors: ["#ef4444", "#22c55e"] });
output("trend", none);

let fast = new Sma(10);
let slow = new Sma(30);

function onStart(): void {
  fast = new Sma(i32(p_fast()));
  slow = new Sma(i32(p_slow()));
}

function onBar(): void {
  const close = bar.close();
  const f = fast.update(close);
  const s = slow.update(close);
  if (isNaN(s)) return;
  out_price(close);
  out_trend(f > s ? 1.0 : 0.0);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

Can I write an output inside a conditional or a loop? The writers are ordinary calls in onBar(), so if (...) out_x(a); else out_x(b); is fine, and so is computing a value in a loop and writing it once. Each output holds one value per bar: the last write wins, so writing the same output in a loop is a longer way of writing it once. To draw nothing for one output on a bar, write NaN to it, or leave it unwritten.

What is the difference between line, area, histogram, and scatter? They are the plot kinds an output can declare, and they change the look, not the value: line joins the points, area fills under them, histogram and bar draw a column per bar, scatter draws a point per bar, candle draws four outputs as a candle, shape draws a gated mark, and none computes without drawing. Plotting lists the options each one takes.

How does positioning work for shapes and text? A shape output draws at (this bar, its value); there is no separate location argument, so put the mark at the bar's low or high by writing that price. Text comes from string slots: render.text draws one mark per bar at (bar, a named output), render.label draws ONE label at (x, y) where x is an output in epoch seconds (the time source), and boxes and segments place themselves by bar offsets from the current bar (Drawing objects).

My line hugs the bottom of the price chart. It is a small-magnitude series drawn on the price axis. Declare it lower.

My box draws with no fill. Its opacity is 0; the default is 0.2. (A named color never gets that far: the sheet check refuses it and asks for hex, rgb(), or hsl().)

My mark draws on every bar. A shape output without shape_where draws wherever it has a value. Add a data-only gate output and name it in shape_where; write 0 to the gate on quiet bars.

Common issues and troubleshooting

My indicator isn't displaying anything. What's wrong? Check, in this order: the editor's Console (a build that stopped at any stage draws nothing, and after a Run of a draft it says when an output was NaN on every bar); whether onBar() ever reaches its writes (a TA class that needs more bars than the chart loaded returns NaN on every bar); whether the output you expect is declared none; whether the chart's market serves the source the input reads (the chart says so by name, and the overlay's legend shows Could not load); and whether a pin was refused, or reads nothing on this market. Debugging is the full workflow.

Undefined identifier errors. Cannot find name 'out_sma' means the accessor does not exist: you renamed or removed the declaration, or misspelled the name. Accessors are generated from the declarations, one per name, lowercased with every run of characters outside [a-z0-9_] collapsed to _: param("fast.len", ...) is p_fast_len(), input("BTC-Close", ...) is in_btc_close(). Capitals are fine either way: the accessor also answers to the spelling your file uses, so param("fastLen", ...) is read by p_fastLen() as well as p_fastlen(). Update the call; the compiler's hint names the declaration an out_, p_, or in_ name comes from. A plain variable you forgot to declare is the same message from the compiler.

How do I debug my indicator? Three ways, all in the editor. Log text with the debug log: string("debug", { max_bytes: 256 }) and str_debug(text) in onBar() print each bar's line in the Console. Read numbers at the Console prompt: after a Run, type an output's name, last 20 and an output's name, or an expression. Or declare the suspect value as an output: a lower line to see its shape, a none output to read it at the prompt without drawing it. Debugging has the workflow and the usual suspects.

Why isn't my indicator updating in real time? It is, at the chart's pace: the forming bar re-evaluates as live updates arrive, at most about once a second per indicator (updates in between are coalesced, and the last one is never dropped). Each time, the chart restores the module's state as it stood after the last closed bar and runs the forming bar again, so a live value never double-counts. The order book, trades, funding, liquidations, and the volume profile arrive as they happen; open interest, implied volatility, skew, the options chain, and ETF flows are polled, so they move when their poll lands. A draft's overlay stops with its session: Run it again after a reload.

Can I use null instead of NaN for missing data? No. Outputs are f64, and f64 has no null: NaN is the one value that means "nothing here", and the chart draws a gap for it. Writing 0 draws a zero. isNaN(x) is the test, and there is no null to compare against.

The chart refuses my symbol or interval pin. The chart serves pins with restrictions: a market pin only on a secondary ohlcv input, one market per pin and never a CME Group venue (CME, CBOT, NYMEX, COMEX, GLOBEX), an interval pin only coarser than the chart and a whole multiple of it (on ohlcv, odds, funding, and oi) and never 3d on a stock, forex or gold market, and no market or interval pin on the first input. Anything else, an exchange id outside the list included, is refused by name in the Console before anything is fetched (Limitations, Multi-timeframe).

Language features and syntax

Does it support switch statements? Yes, over integers, with the same fallthrough rules as TypeScript (write break). Bucket a float into a small integer first, then branch:

param("len", 14, { min: 2, max: 200 });
output("score", line, lower, { description: "-1 oversold, 0 neutral, 1 overbought" });

let rsi = new Rsi(14);

// A switch runs over integers: bucket the value first, then branch. Each case breaks.
function zone(value: f64): i32 {
  if (value <= 30.0) return 0;
  if (value >= 70.0) return 2;
  return 1;
}

function onStart(): void {
  rsi = new Rsi(i32(p_len()));
}

function onBar(): void {
  const value = rsi.update(bar.close());
  if (isNaN(value)) return;
  let score: f64 = 0.0;
  switch (zone(value)) {
    case 0:
      score = -1.0;
      break;
    case 2:
      score = 1.0;
      break;
    default:
      score = 0.0;
      break;
  }
  out_score(score);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

Are objects and arrays supported? Yes, with static types. StaticArray<f64> is the fixed-size buffer for windows, Array<f64> grows, Map exists, and a class with typed fields and methods is the struct. Arrays are homogeneous, any does not exist, and allocation belongs in onStart() or at module start, never in onBar() (Collections, User-defined types).

Can an indicator read another indicator's values? No. Each overlay runs its own module, and no declaration reads another indicator's output. Combine the logic into one file.

Can I use inputs and TA classes inside my own functions? Yes. bar.close() and the in_<name>() readers work in onBar() and in any function it calls, and a value passes on as an f64 argument; a TA class is an ordinary object you can hold in a module-level variable or pass to a function. What a function cannot do is capture a local from an enclosing function (AssemblyScript closures cannot), so shared state is module-level (User functions).

Does it support alerts? Yes, from the chart. Publish the indicator, add it to a chart, and open the alert dialog from its bell in the legend, the right-click menu, or Alert on > Indicators in the sidebar's alert menu; pick a drawn output and a condition (crossing, greater or less than, a channel, moving up or down). The file can also name a ready-made signal with alert(name, { when, message }) over a data-only gate. The alert runs in OpenMarket's cloud on the chart's own market, with the overlay's settings (Alerts).

Can trades be executed from an indicator? No. The module is sandboxed: no filesystem, no network, no order capability. A strategy (strategy({...}) in the file) places orders only in the Strategy Tester's simulation, filled against the chart's own candles (Strategies overview).

Can I show text? Through a string slot and a renderer (render.label for one tag, render.text per bar, render.table for a grid), which switches the file to the second runtime contract; outputs and params stay numbers (Drawing objects).

Can I move or delete a shape after drawing it? Yes, with a drawing handle: a line, box, label, or polyline the module creates, updates, and deletes across bars by an id. Boxes and segments declared at the top of the file work differently: each bar decides what they draw, and one that should end is a when gate that turns 0 (Drawing objects).

How many shapes can I have? 16 declared boxes and 16 segments, each drawn once per bar; 64 renderers, 64 declared drawings, and 64 string slots; drawing handles up to 500 live per kind and 1,500 in all; and at most 2,000 drawings on the chart per run. Limits has every cap.

Performance and optimization

My indicator is running slowly. How can I optimize it? The module runs once per loaded bar, and again on the forming bar as live updates arrive, so the cost that matters is per-bar work. Allocate every buffer once, in onStart() or at module start, sized from a param's max; never new anything inside onBar() (memory that grows once the bars start stops the run). Keep loops bounded by a param with a declared max. Compute a value once and keep it in a variable instead of recomputing it for every output that needs it. The compiled module is a few kilobytes and a 20-bar average over 5,000 bars is instant; an array that grows on every call is the one pattern that makes a long history slow.

Should I use const for constants? Yes: a module-level const MAX_BARS = 500 is a compile-time constant, the right size for a StaticArray, and it costs nothing per bar. A value that depends on a param is a module-level let assigned once in onStart().

Common error messages

What does "Conversion from type 'f64' to 'i32' requires an explicit cast" mean? You handed a float to something that wants a whole number: a class period, a loop bound, an array index, or a counter declared without a type (let count = 0 is an integer). Write i32(p_period()), and declare float variables as f64 (let value: f64 = 0.0). The full list of messages, each with its fix, is in Common errors.

Still have questions?

Common errors lists every message with its fix, Debugging is the workflow for an indicator that runs but draws the wrong thing, and Limitations says what is deliberately out of scope today. For everything else, join the discussion on Discord or read the Quick reference.