Arrays
Introduction
Pine Script™ Arrays are one-dimensional collections that can hold
multiple value references. Think of them as a better way to handle cases
where one would otherwise need to explicitly declare a set of similar
variables (e.g., price00
, price01
, price02
, …).
All elements in an array must be of the same built-in type, user-defined type, or enum type.
Scripts reference arrays using array IDs similar to the IDs of lines, labels, and other special types. Pine Script™ does not use an indexing operator to reference individual array elements. Instead, functions including array.get() and array.set() read and write the values of array elements.
Scripts reference the elements of an array using an index, which starts at 0 and extends to the number of elements in the array minus one. Arrays in Pine Script™ can have a dynamic size that varies across bars, as one can change the number of elements in an array on each iteration of a script. Scripts can contain multiple array instances. The size of arrays is limited to 100,000 elements.
Declaring arrays
Pine Script™ uses the following syntax to declare arrays:
Where <type>
is a
type template for the array that declares the type of values it will
contain, and the <expression>
returns either an array of the specified
type or na
.
When declaring a variable as an array, we can use the
array
keyword followed by a
type template. Alternatively, we can use the type
name followed by the
[]
modifier (not to be confused with the
[]
history-referencing operator).
Since Pine always uses type-specific functions to create arrays, the
array<type>/type[]
part of the declaration is redundant, except when
declaring an array variable assigned to na
. Even when not required,
explicitly declaring the array type helps clearly state the intention to
readers.
This line of code declares an array variable named prices
that points
to na
. In this case, we must specify the type to declare that the
variable can reference arrays containing “float” values:
We can also write the above example in this form:
When declaring an array and the <expression>
is not na
, use one of
the following functions: array.new<type>(size, initial_value),
array.from(),
or
array.copy().
For array.new<type>(size, initial_value)
functions, the arguments of
the size
and initial_value
parameters can be “series” to allow
dynamic sizing and initialization of array elements. The following
example creates an array containing zero “float” elements, and this
time, the array ID returned by the array.new<float>()
function call is assigned to prices
:
The initial_value
parameter of array.new*
functions allows users to
set all elements in the array to a specified value. If no argument is
provided for initial_value
, the array is filled with na
values.
This line declares an array ID named prices
pointing to an array
containing two elements, each assigned to the bar’s close
value:
To create an array and initialize its elements with different values,
use
array.from().
This function infers the array’s size and the type of elements it will
hold from the arguments in the function call. As with array.new*
functions, it accepts “series” arguments. All values supplied to the
function must be of the same type.
For example, all three of these lines of code will create identical “bool” arrays with the same two elements:
Using `var` and `varip` keywords
Users can utilize var and varip keywords to instruct a script to declare an array variable only once on the first iteration of the script on the first chart bar. Array variables declared using these keywords point to the same array instances until explicitly reassigned, allowing an array and its element references to persist across bars.
When declaring an array variable using these keywords and pushing a new
value to the end of the referenced array on each bar, the array will
grow by one on each bar and be of size bar_index + 1
(bar_index
starts at zero) by the time the script executes on the last bar, as this
code demonstrates:
The same code without the var keyword would re-declare the array on each bar. In this case, after execution of the array.push() call, the a.size() call would return a value of 1.
Reading and writing array elements
Scripts can write values to existing individual array elements using
array.set(id, index,
value),
and read using array.get(id,
index).
When using these functions, it is imperative that the index
in the
function call is always less than or equal to the array’s size (because
array indices start at zero). To get the size of an array, use the
array.size(id)
function.
The following example uses the
set()
method to populate a fillColors
array with instances of one base color
using different transparency levels. It then uses
array.get()
to retrieve one of the colors from the array based on the location of
the bar with the highest price within the last lookbackInput
bars:
Another technique for initializing the elements in an array is to create an empty array (an array with no elements), then use array.push() to append new elements to the end of the array, increasing the size of the array by one on each call. The following code is functionally identical to the initialization section from the preceding script:
This code is equivalent to the one above, but it uses
array.unshift()
to insert new elements at the beginning of the fillColors
array:
We can also use
array.from()
to create the same fillColors
array with a single function call:
The array.fill(id, value, index_from,
index_to)
function points all array elements, or the elements within the
index_from
to index_to
range, to a specified value
. Without the
last two optional parameters, the function fills the whole array, so:
and:
are equivalent, but:
only fills the second and third elements (at index 1 and 2) of the array
with close
. Note how
array.fill()‘s
last parameter, index_to
, must be one greater than the last index the
function will fill. The remaining elements will hold na
values, as the
array.new()
function call does not contain an initial_value
argument.
Looping through array elements
When looping through an array’s element indices and the array’s size is unknown, one can use the array.size() function to get the maximum index value. For example:
Note that:
- We use the
request.security_lower_tf()
function which returns an array of
close
prices at the
1 minute
timeframe. - This code example will throw an error if you use it on a chart
timeframe smaller than
1 minute
. - for
loops do not execute if the
to
expression is na. Note that theto
value is only evaluated once upon entry.
An alternative method to loop through an array is to use a
for…in
loop. This approach is a variation of the standard for loop that can
iterate over the value references and indices in an array. Here is an
example of how we can write the code example from above using a
for...in
loop:
Note that:
- for…in
loops can return a tuple containing each index and corresponding
element. For example,
for [i, price] in a
returns thei
index andprice
value for each element ina
.
A while loop statement can also be used:
Scope
Users can declare arrays within the global scope of a script, as well as the local scopes of functions, methods, and conditional structures. Unlike some of the other built-in types, namely fundamental types, scripts can modify globally-assigned arrays from within local scopes, allowing users to implement global variables that any function in the script can directly interact with. We use the functionality here to calculate progressively lower or higher price levels:
History referencing
Pine Script™‘s history-referencing operator [ ] can access the history of array variables, allowing scripts to interact with past array instances previously assigned to a variable.
To illustrate this, let’s create a simple example to show how one can
fetch the previous bar’s close
value in two equivalent ways. This
script uses the [
]
operator to get the array instance assigned to a
on the previous bar,
then uses the
get()
method to retrieve the value of the first element (previousClose1
).
For previousClose2
, we use the history-referencing operator on the
close
variable directly to retrieve the value. As we see from the
plots, previousClose1
and previousClose2
both return the same value:
Inserting and removing array elements
Inserting
The following three functions can insert new elements into an array.
array.unshift() inserts a new element at the beginning of an array (index 0) and increases the index values of any existing elements by one.
array.insert()
inserts a new element at the specified index
and increases the index
of existing elements at or after the index
by one.
array.push() adds a new element at the end of an array.
Removing
These four functions remove elements from an array. The first three also return the value of the removed element.
array.remove()
removes the element at the specified index
and returns that element’s
value.
array.shift() removes the first element from an array and returns its value.
array.pop() removes the last element of an array and returns its value.
array.clear() removes all elements from an array. Note that clearing an array won’t delete any objects its elements referenced. See the example below that illustrates how this works:
Using an array as a stack
Stacks are LIFO (last in, first out) constructions. They behave somewhat like a vertical pile of books to which books can only be added or removed one at a time, always from the top. Pine Script™ arrays can be used as a stack, in which case we use the array.push() and array.pop() functions to add and remove elements at the end of the array.
array.push(prices, close)
will add a new element to the end of the
prices
array, increasing the array’s size by one.
array.pop(prices)
will remove the end element from the prices
array,
return its value and decrease the array’s size by one.
See how the functions are used here to track successive lows in rallies:
Using an array as a queue
Queues are FIFO (first in, first out) constructions. They behave somewhat like cars arriving at a red light. New cars are queued at the end of the line, and the first car to leave will be the first one that arrived to the red light.
In the following code example, we let users decide through the script’s
inputs how many labels they want to have on their chart. We use that
quantity to determine the size of the array of labels we then create,
initializing the array’s elements to na
.
When a new pivot is detected, we create a label for it, saving the
label’s ID in the pLabel
variable. We then queue the ID of that label
by using
array.push()
to append the new label’s ID to the end of the array, making our array
size one greater than the maximum number of labels to keep on the chart.
Lastly, we de-queue the oldest label by removing the array’s first
element using
array.shift()
and deleting the label referenced by that array element’s value. As we
have now de-queued an element from our queue, the array contains
pivotCountInput
elements once again. Note that on the dataset’s first
bars we will be deleting na
label IDs until the maximum number of
labels has been created, but this does not cause runtime errors. Let’s
look at our code:
Negative indexing
The array.get(), array.set(), array.insert(), and array.remove() functions support negative indexing, which references elements starting from the end of the array. An index of -1
refers to the last element in the array, an index of -2
refers to the second to last element, and so on.
When using a positive index, functions traverse the array forwards from the beginning of the array (first to last element). The first element’s index is 0
, and the last element’s index is array.size() - 1
. When using a negative index, functions traverse the array backwards from the end of the array (last to first element). The last element’s index is -1
, and the first element’s index is –array.size()
:
Like positive indexing, negative indexing is bound by the size of the array. For example, functions operating on an array of 5 elements only accept indices of 0 to 4 (first to last element) or -1 to -5 (last to first element). Any other indices are out of bounds and will raise a runtime error.
We can use negative indices to retrieve, update, add, and remove array elements. This simple script creates an “int” countingArray
and calls the array.get(), array.set(), array.insert(), and array.remove() functions to perform various array operations using negative indices. It displays each array operation and its corresponding result in a table:
Note that not all array operations can use negative indices. For example, search functions like array.indexof() and array.binary_search() return the positive index of an element if it’s found in the array. If the value is not found, the functions return -1
. However, this returned value is not a negative index, and using it as one would incorrectly reference the last array element. If a script needs to use a search function’s returned index in subsequent array operations, it must appropriately differentiate between this -1
result and other valid indices.
Calculations on arrays
While series variables can be viewed as a horizontal set of values stretching back in time, Pine Script™‘s one-dimensional arrays can be viewed as vertical structures residing on each bar. As an array’s set of elements is not a time series, Pine Script™‘s usual mathematical functions are not allowed on them. Special-purpose functions must be used to operate on all of an array’s values. The available functions are: array.abs(), array.avg(), array.covariance(), array.min(), array.max(), array.median(), array.mode(), array.percentile_linear_interpolation(), array.percentile_nearest_rank(), array.percentrank(), array.range(), array.standardize(), array.stdev(), array.sum(), array.variance().
Note that contrary to the usual mathematical functions in Pine Script™,
those used on arrays do not return na
when some of the values they
calculate on have na
values. There are a few exceptions to this rule:
- When all array elements have
na
value or the array contains no elements,na
is returned.array.standardize()
however, will return an empty array. array.mode()
will returnna
when no mode is found.
Manipulating arrays
Concatenation
Two arrays can be merged — or concatenated — using array.concat(). When arrays are concatenated, the second array is appended to the end of the first, so the first array is modified while the second one remains intact. The function returns the array ID of the first array:
Copying
You can copy an array using
array.copy().
Here we copy the array a
to a new array named _b
:
Note that simply using _b = a
in the previous example would not have
copied the array, but only its ID. From thereon, both variables would
point to the same array, so using either one would affect the same
array.
Joining
Use array.join() to concatenate all of the elements in the array into a string and separate these elements with the specified separator:
Sorting
Arrays containing “int” or “float” elements can be sorted in either
ascending or descending order using
array.sort().
The order
parameter is optional and defaults to
order.ascending.
As all array.*()
function arguments, it is qualified as “series”, so
can be determined at runtime, as is done here. Note that in the example,
which array is sorted is also determined at runtime:
Another useful option for sorting arrays is to use the
array.sort_indices()
function, which takes a reference to the original array and returns an
array containing the indices from the original array. Please note that
this function won’t modify the original array. The order
parameter is
optional and defaults to
order.ascending.
Reversing
Use array.reverse() to reverse an array:
Slicing
Slicing an array using
array.slice()
creates a shallow copy of a subset of the parent array. You determine
the size of the subset to slice using the index_from
and index_to
parameters. The index_to
argument must be one greater than the end of
the subset you want to slice.
The shallow copy created by the slice acts like a window on the parent array’s content. The indices used for the slice define the window’s position and size over the parent array. If, as in the example below, a slice is created from the first three elements of an array (indices 0 to 2), then regardless of changes made to the parent array, and as long as it contains at least three elements, the shallow copy will always contain the parent array’s first three elements.
Additionally, once the shallow copy is created, operations on the copy
are mirrored on the parent array. Adding an element to the end of the
shallow copy, as is done in the following example, will widen the window
by one element and also insert that element in the parent array at index
3. In this example, to slice the subset from index 0 to index 2 of array
a
, we must use _sliceOfA = array.slice(a, 0, 3)
:
Searching arrays
We can test if a value is part of an array with the array.includes() function, which returns true if the element is found. We can find the first occurrence of a value in an array by using the array.indexof() function. The first occurence is the one with the lowest index. We can also find the last occurrence of a value with array.lastindexof():
We can also perform a binary search on an array but note that performing a binary search on an array means that the array will first need to be sorted in ascending order only. The array.binary_search() function will return the value’s index if it was found or -1 if it wasn’t. If we want to always return an existing index from the array even if our chosen value wasn’t found, then we can use one of the other binary search functions available. The array.binary_search_leftmost() function, which returns an index if the value was found or the first index to the left where the value would be found. The array.binary_search_rightmost() function is almost identical and returns an index if the value was found or the first index to the right where the value would be found.
Error handling
Malformed array.*()
call syntax in Pine scripts will cause the usual
compiler error messages to appear in Pine Editor’s console, at the
bottom of the window, when you save a script. Refer to the Pine Script™
v6 Reference
Manual when in
doubt regarding the exact syntax of function calls.
Scripts using arrays can also throw runtime errors, which appear as an exclamation mark next to the indicator’s name on the chart. We discuss those runtime errors in this section.
Index xx is out of bounds. Array size is yy
This will most probably be the most frequent error you encounter. It
will happen when you reference an nonexistent array index. The “xx”
value will be the value of the faulty index you tried to use, and “yy”
will be the size of the array. Recall that array indices start at
zero — not one — and end at the array’s size, minus one. An array of
size 3’s last valid index is thus 2
.
To avoid this error, you must make provisions in your code logic to prevent using an index lying outside of the array’s index boundaries. This code will generate the error because the last index we use in the loop is outside the valid index range for the array:
The correct for
statement is:
To loop on all array elements in an array of unknown size, use:
When you size arrays dynamically using a field in your script’s
Settings/Inputs tab, protect the boundaries of that value using
input.int()‘s
minval
and maxval
parameters:
See the Looping through array elements section of this page for more information.
Cannot call array methods when ID of array is ‘na’
When an array ID is initialized to na
, operations on it are not
allowed, since no array exists. All that exists at that point is an
array variable containing the na
value rather that a valid array ID
pointing to an existing array. Note that an array created with no
elements in it, as you do when you use a = array.new_int(0)
, has a
valid ID nonetheless. This code will throw the error we are discussing:
To avoid it, create an array with size zero using:
or:
Array is too large. Maximum size is 100000
This error will appear if your code attempts to declare an array with a size greater than 100,000. It will also occur if, while dynamically appending elements to an array, a new element would increase the array’s size past the maximum.
Cannot create an array with a negative size
We haven’t found any use for arrays of negative size yet, but if you ever do, we may allow them :)
Cannot use shift() if array is empty.
This error will occur if array.shift() is called to remove the first element of an empty array.
Cannot use pop() if array is empty.
This error will occur if array.pop() is called to remove the last element of an empty array.
Index ‘from’ should be less than index ‘to’
When two indices are used in functions such as array.slice(), the first index must always be smaller than the second one.
Slice is out of bounds of the parent array
This message occurs whenever the parent array’s size is modified in such a way that it makes the shallow copy created by a slice point outside the boundaries of the parent array. This code will reproduce it because after creating a slice from index 3 to 4 (the last two elements of our five-element parent array), we remove the parent’s first element, making its size four and its last index 3. From that moment on, the shallow copy which is still poiting to the “window” at the parent array’s indices 3 to 4, is pointing out of the parent array’s boundaries: