Lesson 17: Staying inside the limits
Every script runs within a budget of work, history, memory and drawings. Learn what the limits are, what happens at each one, and how to make a script cheaper.
Lesson 17 of 18. A script runs over every bar of the chart, in your browser, so the engine sets limits that a script cannot raise. Most scripts never come near them. This lesson shows the ones you are most likely to meet, what the engine does at each, and how to write the same thing more cheaply. The numbers below are filled in from the engine itself.
Steps: the work a script does#
Every loop pass, window, drawing and piece of text costs steps. One bar may spend 500,000 steps and a whole run 600,000,000. The count is of steps, not seconds, so a script stops at the same place on every machine. Past the limit the script stops with E-LIMIT-OPS, and the message names the bar.
This average adds up the last closes in a loop on every bar:
indicator "Average with a loop"
edition = 1
display = price
input length: int = 50, name = "Length", min = 1, max = 500
mut total = 0.0
for i in 0 until length
total += close[i]
plot(total / length, name = "Average", color = color.orange)It is correct, but each bar does length passes of work. The built-in function gives the same average and keeps a running total, so each bar does a few steps whatever the length:
indicator "Average without a loop"
edition = 1
display = price
input length: int = 50, name = "Length", min = 1, max = 500
plot(tech.sma(close, length), name = "Average", color = color.orange)A loop of a thousand passes on every bar is still fine. Look for a loop when a script is slow, and prefer a tech.* function or a state value that you update once a bar over a loop that redoes the same work on every bar.
History: how far back a script reads#
x[n] and every window such as tech.sma(x, n) read back n bars. No read may go back more than 8,192 bars. A number written in the script that is larger is refused before the script runs:
indicator "Too far back"
edition = 1
plot(close[9000])When the number comes from an input, give the input a max. Without one the engine cannot tell how far back the script reads, keeps the most history it allows, and warns:
indicator "An input without a max"
edition = 1
input back: int = 10, name = "Bars back", min = 1
plot(close - close[back], name = "Change")Add max = 500 (or whatever you need) to the input and the warning goes away.
Drawings#
Lines, labels and boxes are kept up to a number: 50 of each kind when the header says nothing, and at most 500 when you ask for more with max_lines, max_labels or max_boxes in the header. A script that makes more drawings keeps the newest and drops the oldest, with the warning W-OBJECT-LIMIT:
indicator "A line on every bar"
edition = 1
display = price
line.new(bar.index - 1, close[1], bar.index, close, color = color.blue)For a value on every bar, a plot is the right tool: it is one series, not thousands of objects. Keep drawings for things that happen now and then (a swing, a session, a break), or draw once on the last bar with if bar.is_last. Tables are limited too: 100 columns, 500 rows and 10,000 cells.
Memory, requests and time#
- Memory. A script may hold about 134,217,728 bytes at one moment and build 2,147,483,648 over a run; past either,
E-LIMIT-MEMORY. An array that grows on every bar and is never trimmed is the usual cause: keep a window withshift()once it is full, as Lesson 7 shows. - Collections and text. An array, a map or a matrix holds at most 100,000 items (
E-LIMIT-ITEMS); a text built by one call has at most 1,000,000 characters (E-LIMIT-TEXT). - Requests. A script may read at most 20 different timeframes of its symbol with
data.series; more isE-REQ-LIMIT. Several calls that read the same timeframe count once, so read a timeframe once and keep the result in a name. - Time. A run on the chart may take at most 5,000 ms of clock time (
E-LIMIT-TIME).
Making a script cheaper#
- Use the built-in
tech.*functions in place of loops over past bars. - Give every input that sets a length or a number of bars back a
max. - Draw with
plot,mark.*andlevelfor values on every bar; keep lines, labels and boxes for rare events or the last bar. - Trim collections that grow, and build long texts only on the last bar.
- Read each other timeframe once, and keep the result in a
let.
Limits and budgets lists every limit with its code.
Try changing#
- In "Average with a loop", set the length to 500 in the settings. The line matches the built-in average, at more cost per bar.
- In "An input without a max", add
max = 200to the input. The noteW-ENGINE-HISTORY-OPENgoes away. - In "A line on every bar", add
max_lines = 500underdisplay = price. More lines are kept, and on a long chart the oldest are still dropped. - Rewrite "A line on every bar" as a
plot(close): one line, no warning.
Checkpoint#
- You can name the limits a script meets most often: steps, history, drawings, memory and requests.
- You can replace a loop over past bars with a
tech.*function. - You can remove
W-ENGINE-HISTORY-OPENby giving an input amax. - You know when to use a plot and when a drawing.