---
title: "Limitations"
description: "What a wrun indicator does not do in the chart, and where to find the numbers. An indicator is a sandboxed, declared computation over market data; a few things…"
order: 132
section: "reference"
---

<!-- source: docs/indicators/reference/limitations.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# Limitations

What a wrun indicator does not do in the chart, and where to find the numbers. An indicator is a sandboxed, declared computation over market data; a few things are deliberately out of scope, and knowing them up front saves you from designing around a feature that is not there.

## Not supported today

<!-- wrun:cards -->
**Numbers only in outputs.** Every output is a 64-bit float, and `NaN` is the one "nothing here" value. Text reaches the chart only through string slots and renderers (the second runtime contract, which **Run** derives for you when you declare one). Decisions are numbers too: `1` or `0` in a data-only output, turned into a look by a declaration (`shape_where`, `color_by`, a box's `when`).

**Settings are typed, text included.** A setting is `param.<kind>(...)`: a whole number, a number, a toggle, a menu, a color, a time or a price picked on the chart, a range, a multi-select, a list, a price field, a timeframe, a market, a session or words, each drawn by the overlay's settings dialog as its own control and laid out in pages and sections ([The settings dialog](../settings/overview.md)). A `param.text` or `param.text_area` takes plain words within its `max_bytes` ([Setting kinds](../settings/kinds.md#text-settings)). A sheet holds at most 128 settings after a range (2), a session (3), a list (`max` + 1) and a unit menu (1 more) expand. A source, timeframe or symbol pick is applied by the chart when the Indicator runs in the browser; an Indicator that runs on OpenMarket's servers keeps the declared default for those three, and an alert refuses to arm while one is off its default. Changing a setting runs the module again over the loaded bars without recompiling.

**Pins are served with restrictions.** An unpinned input follows the chart's own market and interval. The chart checks every pin before it fetches anything and refuses the ones it cannot serve by name:

- a market pin (`symbol` and `exchange` together) only on a secondary `ohlcv` input; every other source and every celled input follows the chart's own market, except `odds`, which names its Polymarket market by condition id;
- an interval pin only coarser than the chart and a whole multiple of it, on a secondary `ohlcv` input, an `odds` input, or `funding` and `oi`;
- no market or interval pin on the first input: it follows the chart (an `odds` input may come first and still names its market);
- a `forming` view up to `WEEK`, and never a view without an interval pin.

Pins on stocks and ETFs (`POLYGON`), forex, gold and silver (`FX_OTC`) are served like crypto pins, on any chart. An index pin reads `NaN` with a warning row, and a CME Group pin or a `3d` pin on a stock, forex or gold market is refused by name. [Multi-timeframe](../core-concepts/multi-timeframe.md) and [Multi-source](../core-concepts/multi-source.md) have the rules in full.

**No market picker.** The chart has no picker for a market chosen per use: an `odds` input declared with `binding` is refused ("the chart has no market picker yet; pin the market's condition id (0x…) in the input's symbol"). Pin the Polymarket market by its condition id instead.

**No live prints.** The chart does not serve `tape` (individual prints); it is refused by name ("celled source class 'tape' is not served by the browser lane yet"). Side-split volume per bar is the `trades` source with `side: "BUY"` or `"SELL"`, a CVD is that pair accumulated in the module, and the split by trade size is `trade_volume_by_size` ([Data sources](../core-concepts/data-sources.md)).

**No other indicator's output.** Reading another indicator's output as an input has no declaration at all, and neither does a `series` source, so a file in the editor cannot ask for either.

**Some feeds are the chart's alone.** The option summaries beyond implied volatility and skew (`volatility_index`, `options_oi`, `options_volume`), `long_short_ratio`, the ETF feeds, `ethena_positions`, `bitfinex_funding`, `treasury_balance` and `economic` are served when the indicator runs in the browser, like `etf_flow`. An alert on an indicator that reads one is refused.

**The options chain is the live row's only.** `options_chain` fills the newest row with the current chain (a 30-second snapshot, polled live) and leaves every history row an empty block, so a gamma map over past chains has no form yet. `venue` picks the chain: `auto` (the chart's own options venue, else the coin's Deribit chain), or one venue's (`deribit`, `cme`, `binance`, `okx`, `bybit`, `bullish`, `derive`), refused by name on a chart that venue cannot serve.

**The order book is the chart's.** `book` cells come from the book the chart loads for its own market, up to 500 price levels a side, so a full book is up to 1,000 tuples; size `max_cells` for that. The declaration requires `block_size`, but the chart does not read it or `max_depth`, so neither changes what it fetches ([Order flow](../functions/order-flow-kit.md)).

**Drawings have fixed budgets.** Boxes and segments are declared once and evaluated on every bar, 16 of each. Drawing handles (line, box, label, and polyline) are objects the module creates, moves, and deletes across bars, capped at 500 live per kind, 1,500 in all, 100,000 points per polyline and 524,288 across the live polylines, and 4,096 draw calls per row; there is no table handle (a table is `render.table`). One run draws at most 2,000 drawings on the chart; past that the chart draws the newest 2,000, says "Drawings capped" on the indicator's legend row, and its card says how many were drawn ([Drawing objects](../presentation/drawing-objects.md)).

**No history array.** `onBar()` sees one bar, which is also why an indicator cannot look ahead by accident ([Execution model](../core-concepts/execution-model.md)). There is no `[]` operator on a series, and `History` from `./sdk/stats` keeps the window for you: `push()` the value once per bar and `ago(n)` reads it `n` bars back ([Stats, history and lists](../functions/stats-history-lists.md)).

**No persistence across runs.** Module state lives for one run over the loaded bars. Nothing survives a reload, a new **Run**, or a pan that loads older bars (the chart runs the module again over the wider window); anything you need is recomputed from the loaded history. A draft's overlay lasts for the session and is not saved with the layout: **Run** it from the editor again to bring it back.

**No cross-indicator communication.** Each overlay runs its own module. Two wrun indicators on the same chart share no state, and one cannot read another's outputs; combine the logic into one file.

**No orders from the file.** The module has no filesystem, no network, no clock, and no order capability. A strategy's orders fill only in the Strategy Tester's simulated broker, against the chart's own candles; nothing reaches an exchange ([Strategies overview](../strategies/overview.md)).

**One file per indicator.** Helpers, classes, and constants live in the one editor tab beside `onBar()`. The kit and the accessors generated from your declarations are the only names there are, so there are no library imports, and a `//@file=` line is refused ([Publishing](../functions/publishing.md)):

```text
One file per indicator for now. Move the code from <file> into this tab and remove the //@file line.
```

**History is what the chart loaded.** The module sees exactly the candles the chart has loaded, and every source is fetched over that window; there is no separate warm-up fetch, so a long lookback needs the chart to load more bars. An indicator runs at any chart interval from 1s to 1Y, but on a 1s chart the sources not served at 1s are declined, prediction-market odds need a one-minute chart or coarser, and `intrabar` cells come from bars no finer than 1m, so they need a chart of 2m or coarser.

**Alerts read pins within 600 bars.** An alert needs a published version and runs in OpenMarket's cloud on the chart's market and interval with the overlay's settings, over at most 600 bars of that interval. It serves another market's candles on a secondary `ohlcv` input and a coarser pin that is a whole multiple of the chart's interval, read once its candle has closed; 600 chart bars hold 600 / (leg / chart) candles of a pin, so a higher-timeframe average longer than that arms but stays empty, and belongs on a coarser chart. It refuses a pin on the first input, a finer pin, a custom timeframe, a source alerts cannot evaluate yet (the feeds above among them), and a source, timeframe or symbol setting off its default. An indicator that builds a higher timeframe from the chart's own bars must declare `warmup()`, or its alert can arm and stay empty. A data-only output is not offered as a condition target ([Alerts](../functions/alerts.md)).

**CME data is first-party only.** **Run** is paused while the chart is on a CME market, and drafts and community indicators cannot read CME data; OpenMarket's official wrun indicators can. On CME markets only price alerts are available.

**Where it runs is fixed at the first publish.** A name first published as Compiled or Open source runs in each reader's browser, and one first published as Protected runs on OpenMarket's servers; a later version cannot switch sides. The Publish dialog has no price, and paid indicators cannot be added from the chart yet ([Publishing](../functions/publishing.md)).

## What AssemblyScript and the sandbox leave out

The file is AssemblyScript: TypeScript syntax over fixed-width numbers, compiled in your browser. The editor's compile hints link here when you reach for something it does not have:

<!-- wrun:cards -->
| You wrote | Write instead |
| --- | --- |
| a closure over a local variable | keep the value at module scope, or pass it as a parameter |
| a union type | one concrete type (`f64`, `i32`, `string`), or a nullable class |
| `any` | the concrete type: `f64`, `i32`, `string`, or a class |
| a bare object literal | a class with those fields, built with `new` |
| the spread operator | a counting loop, or `Array.slice()` |
| destructuring | read the fields or indexes one at a time (`const a = pair[0]`) |
| optional chaining (`?.`) or nullish coalescing (`??`) | test for `null` with `if` or a ternary |
| `for...of` | a counting `for` loop over the indexes |
| the `in` operator | `Map.has()`, or read the field directly |
| a regular expression | the string methods: `includes`, `startsWith`, `indexOf`, `split` |
| `try` / `catch` | test the condition before the call; a throw ends the run |
| `async` / `await` | nothing: an indicator runs synchronously, one bar at a time |
| `console.log(...)` | the debug log: `string("debug", { max_bytes: 256 })` and `str_debug(text)` in `onBar()` ([Debugging](../faq/debugging.md)) |
| `Date.now()` or `new Date()` | `bar.time()`, the bar's open time in epoch seconds |
| `Math.random()` | a variation derived from the bar data: a run is reproducible bar for bar |
| `performance.now()` | nothing: the sandbox has no clock |

## Named refusals

Each gap above is refused by name, never worked around silently, and the message names the way out: [Common errors](../faq/common-errors.md) lists every refusal with its cause and fix.

## Sandbox limits

Every wrun indicator runs under fixed ceilings (declaration caps, text and frame budgets, memory, and a deadline per run) so one module cannot exhaust the chart; every number is in the [Limits reference](limits.md).

## Build and runtime errors

For the messages you meet while writing an indicator (a coordinate passed as a string instead of a handle, a param option that does not exist, a missing cast, a sheet field out of range, an `onBar()` written with the wrong signature, a run that drew nothing), see [Common errors](../faq/common-errors.md). Each one is listed by its exact text with a cause and a fix.
