Limits reference
Every ceiling a wrun indicator runs under on the chart, in one place: the declaration caps Run checks when it derives the sheet, the runtime ceilings the browser sandbox enforces on every run, the data bounds, and the publish bounds.
Each row gives the exact number, what it protects, the shape that hits it, and the message it produces. Most of them are counts of declarations, because a wrun indicator declares almost everything once.
Plans
Free and Plus differ in how much you keep on your charts and in your account:
| Free | Plus | |
|---|---|---|
| Indicators | 3 across your charts | 10 per chart |
| Indicator alerts | 2 | 25 |
| Saved scripts | 100 | 500 |
At the saved-script limit, saving a new script stops with "Saved-script limit reached (100 on your plan). Delete a script or upgrade to save new ones."
Declaration caps
Checked by Run in your browser when it derives the sheet from your
declarations, before anything reaches the chart. A declaration over a cap
is refused in the editor's Console, anchored on the declaration that
breaks it and naming the field (boxes.0.top, renderers.2.size).
| Limit | Value | What it caps | When you hit it | Refusal |
|---|---|---|---|---|
| Boxes | 16 per indicator | box( declarations, each drawn once per bar | one declaration per occurrence instead of one gated declaration | boxes must declare at most 16 entries |
| Segments | 16 per indicator | segment( declarations, each drawn once per bar | the same shape | segments must declare at most 16 entries |
| Renderers | 64 per indicator | render., render., render., render., render., render., render. | a footprint-style file with a text tile per price level | renderers must declare at most 64 entries |
| Drawings | 64 per indicator | draw., draw., draw., draw. and the widgets, placed from the newest bar | many run-level objects; a growing list is handles instead | drawings must declare at most 64 entries |
| Polyline points | 64 pairs | points on one declared draw. (a polyline handle takes 100,000) | a curve traced point by point in a declaration | polyline points must be <= 64 pairs |
| Declared alerts | 64 per indicator; message and description at most 200 characters each; title 1 to 120 characters on one line, without control or invisible characters; text names a declared string slot, whose words are cut at 200 characters when the alert fires | alert( declarations | a signal per level instead of one gate output | alerts must declare at most 64 entries, message must be at most 200 characters, alert 'cross' title is 121 characters; it takes at most 120, alert 'cross' text names 'ghost', which is not a declared string slot |
| Handle defaults | width 0.5..20, border_ 0..10, opacity 0..1, size and font_ 6..64, line_ and border_ solid/dashed/dotted, extend none/left/right/both, align left/center/right, valign top/middle/bottom, padding 0..64, corner_ 0..32, glow 0..32, max_ 0..2000 (0 = no wrap), angle -90..90, gradient 2..8 colours, arrow none/start/end/both, axis_ none/price (text on a label), panel overlay/lower; the legacy border, border and line spellings still read, never beside their snake_case twins | the defaults one handles. declaration sets for line, box, label or polyline, and the same keys on a declared draw., draw., draw. or draw. | a label handle no longer needs a string slot: . draws an emblem with no text | handles., a line has no text; use price |
| Table geometry | 32 rows x 8 cols | one render.; cells must list exactly rows * cols slot names; rows_ paints the first rows only (a data-only output's value on the table's bar, clamped to 0..rows) | a dashboard dump | table rows must be <= 32, table cols must be <= 8, table 'stats' declares 2x2 = 4 cells but lists 3, render. |
| Table look | width and each of column_ 0..4096 px (0 = measured, one per column), cell_ 0..64, offset within [-200, 200], border_ and grid_ 0..10, corner_ 0..32, font_ 6..64, opacity 0..1, a gradient 2..8 colours, header_ at most rows, a colspan or rowspan inside the grid at every cell carrying the slot, every colour ladder in a pair (<key>_ + <key>s, or <key>_, never both) | the look keys and styles entries on one render. (Styled tables) | a span past the last column, a widths list shorter than the columns | table 'board' lists 1 column widths for 2 columns, table 'board' cell 'b' spans 2 columns from column 2 of 2, table 'board' declares text_ |
| String slots | 64 per indicator | string( declarations | one slot per table cell on a big table | string_ |
| Slot bytes | 1..4096 per slot | max_ on one slot | a long per-bar readout | max_ |
| Frames | 8 per indicator, each at most 96 KiB (98,304 bytes) | frame( declarations and their max_ | a snapshot per widget instead of one frame per payload | named by field |
| Mini-chart grid | 1..12 panels of 0..100 candles; labels 1..24 characters, the title at most 40 | the frame of one draw. | a grid of more than 12 markets or timeframes | a grid holds at most 12 panels, a panel holds at most 100 candles |
| Mini-chart look | columns 1..4, panel_ 72..320, panel_ 48..220, gap 0..48, x and y -2000..2000, ma_ 0.5..5 | the options of one draw. | columns must be within 1. | |
| Docked profiles | 4 per indicator; a frame of 1..512 rows; width_ 0.05..0.5 or width_ 16..600 (never both); offset x 0..4096; thickness_ 2..40; opacity, outside_ 0..1; series 1..8 with unique names; gradient 2..8 colours; border_, value_, poc_ 0..10 (the POC at least 1); font_ 6..64; step, scale_ above 0; labels_ 0..24 characters a row, tooltips 0..64, poc_ 1..24 | plot. and its frame (Docked profiles) | a level beside itself or on the other dock, a behind_ profile on the lower pane, a scale: "fixed" with no scale_ | width_, scale_, wrun_ for a frame whose series count or shape disagrees with the declaration |
| Level spans | 64 spans per frame, 1..512 prices each, 4096 rows across every span | a plot. frame under span: "time" | one span per bar instead of one per session | wrun_ |
| Panels | 8 per indicator; series 1..8 (a table 1..12); frame rows 2000 (a table 128, a pie or tiles 24); markers and guides 0..8; summary 0..5 rows; height_ 0.05..0.9; title 1..40, caption 1..64; x_, y_ 1..40; width 0..4096, column_ 0..4096 each (one per column), cell_ 0..64, offset -200..200, font_ 6..64, border_ and grid_ 0..10, background_ 2..8, palette 2..8, bins 2..200, columns 1..8, slice_ 0..8, trail_ 0.5..6, a series width 0.5..20, marker and guide width 0.5..4, labels 1..24 | panel. and its frame (Frames, panels and compact widgets) | a 9th marker, a word on a kind that does not take it, a rich-line word on a boxed line | markers allows at most 8, table allows at most 128 rows, series allows at most 12 columns on a table, <word> is only valid for <kinds>, <word> needs chrome 'grid' or 'none' on a time, category or stacked line |
| Side strip | width_ 160..480, height_ 80..800, both with place: "side" only; the strip takes at most 40% of the chart and folds away under 480 px | a panel placed beside the chart or a matrix with dock: "side" | a side panel on a narrow layout | width_ |
| Heatmaps | 4 per indicator; palette 2..8 colours; auto_ 0.5..1; cell_ 0..4; opacity 0..1; tooltip 1..200 characters, label 1..40; out. rows 2..128, and the grid's outputs count toward the 256 | plot. and out. (Price canvases) | a grid with a row per tick, cells beside grid | heatmaps must declare at most 4 entries, out., heatmap 'h' takes cells or grid, not both |
| Profiles | 4 per indicator across footprints, TPOs and time-anchored profiles; imbalance_ 1.1..20; stacked_ 0..10; value_ 0.5..0.95; poc_ 1..10; letter_ 1..240; a TPO palette 2..26; width_ 0.05..1; outside_, background_ 0..1 | plot., plot., plot. | a footprint over book cells, a TPO colour mode that needs profile cells | footprint 'fp' does not take period, tpo 't' color_, profile 'p' behind_ |
| Matrices | 4 per indicator, wrun-4 only; a frame of 1..128 prices, 1..12 columns and at most 1536 cells; cell text and column labels 1..16 characters; column_ 24..160; row_ 8..64; cell_ 0..12; tooltip 1..200, label 1..40 | plot. and its frame | a matrix on a wrun-3 sheet, a 13th column | matrices needs abi_, wrun_ |
| Widgets | 8 cards per indicator and 32 cards, feeds and meters per pane; a card's rows 1..12 (0..12 with a headline); title 1..40 (80 with a template), a row label 1..24 (64 with a template), a literal value 0..64; offset -4096..4096; width 80..1200; corner_ 0..32; padding 0..24; border_ 0..10; font_ and title_ 6..64; background_ 2..8; state_ exactly 4; a ladder frame 1..256 rows, width_ 0.02..0.5, offset x 0..4096 with y 0; a feed 1..50 lines of 1..80 characters; a meter ramp 2..5, bar_ 2..40 | draw., draw., draw., draw. (Status cards) | a card with neither rows nor a headline, accent_ beside state_ | card 'x' needs a row or a headline, title takes 1 to 40 characters, 80 with a {{template}}, offset entries must be integers from -4096 to 4096, accent_ |
| HUD cards | width 80..1200 (300, or 180 at one column); offset -4096..4096; corner_ 0..32; padding 0..24; border_ 0..10; text_ 0..8; accent_ 2..8; one headline tile per card; rings 1..3; a tile font_ 6..64, a tile height 16..120 | render. and its tiles (HUD cards) | two headline tiles, a fourth ring | a HUD takes at most one headline tile, look must be one of 'default', 'glass', . |
| Number format | decimals 0..8; unit at most 8 characters on panes, panels, levels, cards, ladders and canvases; decimals and signed only beside format (an output's unit stays unbounded with or without format, as it always was; without format it is never printed) | every format word (outputs, panes, panels, levels, cards, ladders, canvases) | a unit longer than 8 characters on a pane, panel, level, card, ladder or canvas | decimals must be an integer from 0 to 8, unit takes at most 8 characters, decimals needs format |
| Text size | 6..64 px | size on text and label renderers, and every font_ | size must be >= 6, size must be <= 64, font_ | |
| Type words | font_ ui/mono/serif/rounded, font_ normal/medium/bold, valign top/middle/bottom, gradient_ vertical/horizontal, corner_ 0..32 | every surface that takes them (panels, levels, cards, feeds, meters, ladders, HUDs, handles, drawings, renderers, canvases) | a CSS family name, a numeric weight | font_, font_, corner_ |
| Display offset | -500..500 bars, integer | displacement_ on an output | a lagging span longer than 500 bars | displacement_, displacement_, displacement_ |
| Shape offset | -500..500 bars, a whole or fractional literal | from / to on a box or segment | a zone anchored further back than 500 bars | a literal bar offset must be within -500. |
| Width ladder | 1..10 entries, each 0.5..20 (0.05..20 on bar and histogram) | widths beside width_ | widths must list at most 10 entries, widths entries must be <= 20 | |
| Line width | 0.5..20 | width on an output or a segment | width must be <= 20 | |
| Border width | 0..10 | border on a box | border_ | |
| Edge width | 1..10, integer | edge_ on a range | edge_ | |
| Gradients | 2..8 colors | gradient on a range, a line or a docked profile, fill_ on an area, background_ on a widget, a HUD or a table, a box's gradient | named by field, <key> must list 2 to 8 colours | |
| Opacity | 0..1 | opacity on an output or a box (box default 0.2) | named by field | |
| Color string | 1..64 chars, or a theme token | any color on a box, segment, renderer, or drawing; theme., theme., theme., theme., theme., theme. and theme. pass everywhere a colour goes (a colour param's default and presets excepted), and any other theme. word is refused | a theme. word outside the seven | color must be a non-empty string of at most 64 chars, colour token must be one of theme., a colour param takes #rrggbb or #rrggbbaa; put the token on the colour key |
| Colour word | #rrggbb, #rrggbbaa or a theme token | every colour key this round added (panels, levels, cards, feeds, meters, ladders, HUDs, canvases, and the frames they read) | rgba( or a named colour on a panel series | a colour must be #rrggbb, #rrggbbaa or one of theme. |
| Box fill color | hex, rgb(, or hsl( | a box color (the fill takes the opacity) | a named color such as "red" | color must be a hex, rgb( |
| Palette | at least 2 entries | colors beside color_ | color_ | |
| Celled cap | required, positive integer, counts TUPLES | max_ on a celled input | a celled input declared without it | celled input 'profile' needs max_ |
| Bucket size from a setting | a param. whose min. lies inside 1..500 | ticks_ on a volume_ input | an open-ended setting, or one that reaches past 500 | input 'profile' ticks_ |
| Colour alpha | a decimal from 0 to 1 after the setting's name, "@<param>/ | every colour bound to a setting: the default lands as rgba( where the colour takes it, as #rrggbbaa where it takes a colour word | a panel series colour, which binds without an alpha | whose alpha is not a decimal from 0 to 1 (, a panel series colour binds without an alpha |
| Outputs | 1..256 | output declarations; the sandbox tracks 256 output slots per run (a file whose visuals are all frames may declare none) | a file past 256 outputs, or a raw write past the tracked slots | WRUN output index 300 out of bounds for 256 tracked outputs |
| Settings | 128 per indicator, after expansion: most kinds count 1, a unit list on a number 1 more, a range 2, a session 3, a list its max + 1, a draw. grid that binds no setting 9 to 16 (its look's own settings) | param. declarations and each hidden market. setting; OpenMarket's cloud accepts at most 128 settings per indicator, so the build refuses the declaration that crosses the line (Picks, lanes, the cap) | a list whose max alone crosses 128 (a list's max counts its slots and the count, so max is at most 127), or many composites | the sheet would carry 129 params after expansion ( |
| Text settings | max_ 1..4096 UTF-8 bytes per setting, 256 when left out; the default within it and free of invisible and control characters | param. / param. declarations (Setting kinds) | a default longer than its cap, a pasted name carrying a bidi control | param., param. |
What the chart trims when it draws
These limits never refuse a run. The chart applies them when it draws, so one indicator can't bury another, hide the chart's own buttons, or draw more rows than a screen can show.
| Limit | Value | What happens past it |
|---|---|---|
| Drawing and label text | 256 characters | the text is cut and ends with an ellipsis |
| Drawing order | zorder -100..100 | a higher value draws as 100, a lower one as -100 |
| Drawings per run | 2,000, counting every bar a segment draws, every declared drawing and every live handle | the newest 2,000 are drawn and the oldest are left out; the indicator's legend row says "Drawings capped", and its card says how many of the total were drawn |
| Widgets | 16 cards, feeds, meters and ladders per indicator per pane | the ones past 16 are not drawn |
| Legend entries | 32 per indicator, each 64 characters | entries past 32 are not shown; longer text ends with an ellipsis |
| HUD cards | inside the price pane: below the symbol row and the legend, left of the price axis | an offset that would move a card out is clamped; a card under 0.15 opacity lets clicks through |
| Card controls | controls: "none" | the glyphs stay hidden at rest; hovering always shows hide and collapse |
| TPO rows per period | 512 per day, week or month | the chart doubles the row height until every period fits, at most 6 times, for "auto" and a number alike; every run says so with a warning row in the Console and the warning on the legend chip: tpo 'tpo': row_height 1 coarsened to 8 (512 rows per day is the chart's limit) (Row height) |
| TPO rows on screen | 3 px | thinner rows merge on the price grid: a merged row's letters are the union of its rows' letters, its counts and volumes add up, and the POC and the value area are computed on the merged rows |
| TPO blocks | 262,144 per TPO | the blocks past it are not drawn, and the Console says so |
| Heatmap cells per indicator | the chart's budget, across all of an indicator's heatmaps, the loaded history and the live bars together (one cell per book level, grid row or profile bucket on each bar) | the oldest bars are dropped and the newest are drawn whole; the indicator's legend row says "Cells capped", and its card says how many bars are drawn |
Names share one namespace across outputs, boxes, segments, renderers, and
drawings; color_by, width_by, and shape_where cannot name their own
output; each ladder half without the other is refused by name. The full
validation list is at the end of
Declarations and the sheet.
Runtime ceilings
Enforced on every run by the sandbox your browser runs the module in. A breach refuses the WHOLE run by name and the editor's Console says why; nothing is truncated, clamped, or silently dropped. An indicator that already drew keeps its last good render, and its legend offers a retry.
| Limit | Value | What it protects | When you hit it | Refusal |
|---|---|---|---|---|
| Module memory | 4 MiB (4,194,304 bytes), fixed | the linear memory a module uses | a buffer sized far past any param's max | WRUN memory 8388608 bytes exceeds limit 4194304 bytes |
No growth after on | 0 bytes of growth | flat per-bar memory across a long history and the forming bar's replays | an array that grows per bar, string concatenation in on | "The Indicator allocated memory after init() (65536 to 131072 bytes): the sandbox forbids growth once the bars start." |
| Output values | finite or NaN | a chart and an alert that mean something | a division by zero written as Infinity | WRUN output 0 is Infinity; guard the calculation and emit Na |
| Run deadline | 20 s per run (the compile has its own 15 s) | one runaway module never holds the sandbox: the worker is stopped and replaced | unbounded loops, deep recursion | "The run did not finish within 20 s." |
| Cell block | max_ tuples per bar, per celled input | the one buffer of max_ tuples the build allocates per celled input when the module starts; each bar's block is read into it once, and in_ reads it in place | a deeper book than the cap; a profile with more price buckets than the cap | .; a volume profile declared missing: "empty" is not refused: that bar reads an empty block (in_ is 0) and the run goes on |
| TPO rows per bar | 4,096 | the rows one bar expands into at a TPO's row height | a declared row_ far below one bar's range ("auto" stays under it) | tpo 'tpo': row_ |
| Cell read bounds | inside exported memory | a copy that would overrun the module's memory | a pointer outside the preallocated buffer | WRUN wrun_ |
| Cell phase | on | the cell readers answer for the bar being evaluated; before the first bar (in on, at module start) the count reads -1 and there is no block | the host's cell import called in another phase, which the generated readers never do | WRUN wrun_ |
| String phase | on | slots belong to the row being committed | a string sent outside on | WRUN wrun_ |
| String slot bytes | max_ per slot (at most 4096) | the buffer reserved per slot | a line longer than the slot | WRUN string slot 0 write of 40 bytes exceeds the slot's max_ |
| String bytes per row | 65,536 (64 KiB) | one bar's text across all slots | many slots near their cap on one bar | WRUN string writes for one row total 70000 bytes, over the per-row limit 65536; emit less text per bar |
| Strings and frames per run | 2,097,152 (2 MiB), together; a live session counts as one run | one run's text and frame snapshots across all bars | long readouts on every bar of a long history | WRUN string and frame writes for this run total 2100000 bytes, over the per-run limit 2097152; emit less text or fewer rows |
| UTF-8 | valid sequences only | a slot never carries a replacement character silently | raw bytes sent through the host import | WRUN string slot 0 write of 5 bytes is not valid UTF-8; wrun_ |
| Expanded render result | 8 MiB (8,388,608 bytes) | the selection the chart receives after the run: 16 bytes per selected entry, 8 per carried number, the UTF-8 bytes of every carried string (table cells charge 8 more per cell; handles and a strategy's result count too) | many renderers over a long history with long strings; hundreds of live polylines | WRUN render result exceeds 8388608 bytes expanded ( |
| Live handles per kind | 500 | the handles of one kind alive at once, counted on every creation | a zone tracker that never deletes | wrun_: 500 live box handles already exist; the cap is 500 per kind . |
| Live handles in total | 1500 | every kind together | the same shape across kinds | wrun_: 1500 live handles already exist; the cap is 1500 in total . |
| Polyline handle points | 1..100,000 per call | the points one set sends; the buffer must fit the module's memory | a path that never trims its oldest points | wrun_, wrun_, wrun_ |
| Polyline points per run | 524,288 across the live polylines | the points every live polyline holds, checked on each set: a re-sent path gives its old points back first and a deleted one frees its own | hundreds of long paths kept alive at once | wrun_ |
| Draw calls per bar | 4096 | every handle call on one bar | re-sending every live handle on every bar | wrun_: more than 4096 draw calls on one row . |
| Draw phase | on | handle ops belong to the row being committed | a handle operation outside on | wrun_ |
| Handle identity | ids >= 0, one space across kinds, kind declared in handles, coordinates finite | the ledger the chart mirrors, kind by kind | a negative id; a label on a box's id; a kind the file does not declare; a Na corner; a setter on an id nobody holds | wrun_, wrun_, wrun_, wrun_, wrun_ |
| Handle style values | the prop's range, integer where required, legal on the kind | the same setter table the engine enforces | size on a line; width of 30; style of 7 | wrun_, wrun_, wrun_ |
| Handle label text | a declared slot, written on the bar of the draw call | the text a label carries is the slot's bytes at call time | text( before the slot's sender ran that bar | wrun_, wrun_ |
The module contract is checked by Run before any of this: the four
engine functions the build adds around onBar() with exact signatures (a
file that exports them itself is checked the same way,
The four-function form),
exported memory when the cell, string, or polyline channel is imported,
every present draw import with its exact signature, and an import set
limited to the contract's allowlist. Before
a module runs, the chart checks its bytes against the sheet's
wasm_sha256 and refuses a mismatch. Those messages are in
Common errors.
Data bounds
| Limit | Value | Where |
|---|---|---|
| History | the bars the chart has loaded | every source is fetched over the chart's loaded window, and a pan back runs again over the extended window; warm-up is the file's decision: an output onBar() leaves unwritten is NaN and draws nothing |
| Live updates | the forming bar, about once a second at most | ticks in between are coalesced and the last one is never dropped; only the forming bar is replayed; a live gap wider than 250 bars rebuilds the run |
| Source deadline | 45 s per source | a source that does not answer in time fails the run with its name |
| Empty source | refused | a required source that answers empty fails the run: " |
| Coarse pins | as of the coarser candle's close | a pinned interval contributes to a row only once its candle has closed; that market's candles are fetched from two leg spans before the chart's first bar |
| Pinned intervals | coarser than the chart's and a whole multiple of it (WEEK counts 7 days) | any other interval pin is refused by name before any fetch |
| Pinned venues | crypto, POLYGON, FX_OTC | stocks and ETFs on POLYGON, forex, gold and silver on FX_OTC are served like a crypto pin; an index pin (POLYGON_INDICES) reads its missing fill with a warning row; a CME Group pin is refused by name (Multi-source) |
| Multi-day pins on session venues | 3d refused | a 3d candle on a stock, forex or gold market groups trading days, so the pin is refused before any fetch (wrun_); pin 1d or 1w |
| Intrabar | finer than the chart's interval and dividing it evenly, 1m at the finest | so the chart must be 2 minutes or coarser; max_cells at least the finer bars in one chart bar; closed finer bars only |
| Book depth | at most 500 levels each side | the chart's own order book at its own grouping, whatever block_size and max_depth say, so max_cells: 1000 covers any bar |
| Options chain | the live row only | the chain arrives on the last row; size max_cells for the chain (a BTC chain is about 1,550 contracts) |
| Cell memory, per celled input | max_cells x tuple width x 8 bytes | 4 f64 per volume_profile tuple, 3 per book tuple, 6 per intrabar bar, 10 per options_chain contract, all inside the 4 MiB of module memory; the build allocates this buffer once per celled input when the module starts, reads each bar's block into it once, and in_ reads it in place (a copy through in_ holds the block twice) |
| Alerts | 600 bars of the chart's interval, at most | the window is the larger of the file's warmup() and its largest setting maximum (up to 500); an indicator that reads a coarser pin gets all 600 unless it also reads book or volume_profile cells, and those bars hold 600 / (leg / chart) candles of the pin (a 4h pin: 150 on 1h, 37 on 15m, 12 on 5m), so a longer higher-timeframe average arms but stays empty; another market's candles on a secondary ohlcv input and a coarser 1m to 1w pin that is a whole multiple of the chart's interval are served; a pin on the first input, a finer pin, a custom timeframe and a source alerts cannot evaluate are refused when you save the alert (Alerts) |
| Strategy order ids | 64 distinct per run (closeAll counts as one) | the 65th distinct id refuses the run (wrun_); an id is a slot, re-issuing one replaces its pending order |
| Strategy id text | 64 bytes of UTF-8 per id, from name or OCA name | longer text refuses by name (wrun_), never truncated; invalid UTF-8 refuses (wrun_) |
| Strategy calls per bar | 4096 order calls and getter reads per bar | over it refuses (wrun_); an infinite price refuses (wrun_), NaN is absent |
| Closed trades per run | 10000 | the 10001st closed trade refuses on the bar it closes (wrun_); every engine rejection (pyramiding, legs, sizing, margin, the forming bar) is counted in rejectedOrders, never a refusal |
| Strategy result bytes | inside the 8 MiB expanded render result | the trades, orders and equity together with the drawings (wrun_) |
| Strategy feed | the price input reads ohlcv on the chart's own market, no symbol or exchange pin | refused by name (wrun_); slippageModel: "bookEstimate" refused (wrun_); strategy. and strategy.equity are reserved output names (wrun_) |
Publish bounds
| Limit | Value |
|---|---|
| Open source code | 256 KB ("The source is {size} KB; the limit is {limit} KB.") |
| What changed | one line of up to 280 characters, from the second version |
| Name | lowercase letters, digits, and dashes after your @yourname/ scope; fixed after the first publish |
The module runs sandboxed: no filesystem, no network, no order capability. Each wrun indicator on a chart, a draft from Run included, counts against the chart's indicator limit. Publishing has the dialog.
Other bounds
Bounds that are not a number in the tables above.
| Shape | In wrun |
|---|---|
| Sources per indicator | No per-indicator source budget. On the chart, a wrun indicator counts against your plan's per-chart indicator limit once per distinct source it reads (Data sources). Each source is fetched once per run over the chart's loaded bars, however many inputs read it; a pinned market or interval on ohlcv adds its own fetch, a funding or oi interval pin adds none. |
| Output objects per run | No object count. Every output is one number per bar, in at most 256 slots; decorations are declared once. Every bar emits a row, with NaN in each output onBar() did not write, so the leading NaN rows of a warm-up count toward the per-run budgets above. What reaches the chart is capped by the 8 MiB expanded render result, and past 2,000 drawings in a run the chart draws the newest 2,000. |
| Table cells | 32 x 8 = 256 cells per table renderer, each a string slot; 64 slots per indicator. A panel.table over a frame takes 128 rows by 12 columns instead, and a plot.matrix 128 prices by 12 columns within 1536 cells. |
| Collection size | No element cap. Collections live inside the 4 MiB module memory and are allocated before the first bar, at module start or in onStart(). |
| Nesting and recursion depth | The compiler's own rules apply; the module's stack lives inside its memory, and the 20-second run deadline stops runaway recursion. |
| Backtest bars and runs per day | No grant to spend: the chart's Strategy Tester runs a wrun strategy in your browser over the bars the chart has loaded and recomputes as you pan ("This strategy runs locally over the chart window and recomputes as you pan."), Reading the Strategy Tester. |
Strategy declaration bounds (leverage, fees, funding) | Checked by Run with the engine's rules: initialCapital and leverage more than 0, pyramiding an integer of at least 1, maintenance at least 0 and under 100, the fee and slippage rates at least 0, enum fields the engine's literals. A setting linked to a param is checked once before the first bar and falls back to the default when out of range, reported. Writing strategies has the table. |
Notes that save debugging time
max_cellsis a contract, not a hint. The build sizes one buffer per celled input from it (in_<input>_capacityf64s, tuples x width) when the module starts, andin_<input>_view()reads it in place; size abookinput for the chart's depth: at most 500 levels a side, so 1000 tuples.- Strings never truncate. Pick
max_bytesfor the longest line the slot will ever carry; the generated builder counts the bytes a line REQUIRES, so an oversize line refuses by name instead of clipping. - Most caps are counts of declarations. You meet them when you press
Run, in the editor's Console, anchored on the declaration. A
repeating shape is one declaration gated per bar with
when, never one declaration per occurrence. - Segments count as drawings. A segment drawn on every bar of a long
window is the shape that reaches 2,000 drawings per run; gate it with
whenor narrow its span. - The render cap counts expanded bytes. Sixty-four text renderers over a long history with long strings is the shape that reaches 8 MiB; leave slots unwritten on quiet bars so those rows select nothing.
- Delete what you no longer draw. A handle stays alive until
delete(); the per-kind cap is met by the tracker that creates a zone on every pivot and never frees one. Keep a bounded pool of ids and delete the oldest before reusing its id, and re-send only the handles that changed on a bar (the calls-per-bar cap counts every setter). - Allocate once. Buffers are sized at module start or in
onStart()from a param'smax; per-bar allocation is the one pattern that both slows a long history and trips the no-growth rule.
Example: the caps as contracts
A book-imbalance indicator that sizes everything from what the chart
delivers: the chart's order book carries at most 500 levels a side, so
max_cells is 1000; the cell buffer is the build's own, sized from that
cap and read in place; and the readout slot is sized for its longest
line:
input("close", ohlcv.close);
// The chart's order book carries at most 500 levels a side, so a bar never holds more than 1000 tuples:
// max_cells is a contract, and a bigger block would refuse the whole run.
// block_size is required by the declaration; the chart keeps its own price grouping.
input("book", book.cells, { max_cells: 1000, block_size: 10 });
output("imbalance", line, lower, { unit: "ratio" });
// The longest line is "500 bids / 500 asks" (19 bytes); 32 leaves room and stays under the 4096 cap.
string("depth", { max_bytes: 32 });
render.text("depth_mark", { y: "imbalance", text: "depth", size: 10 });
function onBar(): void {
const n = in_book_cells();
if (n <= 0) return;
// The build's own buffer, sized once at module start: max_cells x 3 cells per [price, size, side] tuple.
const cells = in_book_view();
let bidSize = 0.0;
let askSize = 0.0;
let bids: i32 = 0;
let asks: i32 = 0;
for (let i = 0; i + 2 < n; i += 3) {
if (cells[i + 2] > 0.0) {
bidSize += cells[i + 1];
bids += 1;
} else {
askSize += cells[i + 1];
asks += 1;
}
}
const total = bidSize + askSize;
const imbalance = total > 0.0 ? (bidSize - askSize) / total : NaN;
if (isNaN(imbalance)) return;
out_imbalance(imbalance);
sb_clear();
sb_int(bids);
sb_text(" bids / ");
sb_int(asks);
sb_text(" asks");
str_depth_sb();
}The scan branches on each tuple's side, never on its position: the chart
lists the bids best first, then the asks from the farthest to the best.