Skip to content

Collections ​

How do you keep many values at once, such as the last ten swing highs or a count for each weekday? Put them in a collection: an array for a list, a matrix for rows and columns, a map for values you look up by a key. Declare the collection with var to keep it from bar to bar, because without var it's made again, empty, on every bar.

pine
//@version=6
indicator("Mean of the last 10 swing highs", overlay = true)
var array<float> swings = array.new<float>() // var: one array for the run
float ph = ta.pivothigh(high, 5, 5)
if not na(ph)
    swings.push(ph)
    if swings.size() > 10
        swings.shift()                       // drop the oldest
plot(swings.size() > 0 ? swings.avg() : na, "Mean swing high")

Which one to use ​

You needUseMake oneRead one value
A list in order, which grows and shrinksarray<float>array.new<float>(), array.from(1.0, 2.0)a.get(0)
A grid of rows and columns, or linear algebramatrix<float>matrix.new<float>(rows, columns, 0.0)m.get(row, column)
A value for each keymap<string, float>map.new<string, float>()m.get("BTC")

Every element of a collection has one type, the one in its angle brackets, and that can be a type of your own, as in array<Swing>. array.from(...) takes its type from the values you give it. Most functions are also methods, so swings.push(ph) is array.push(swings, ph).

What a new collection holds ​

array.new<float>(5) holds five na values, and array.new<float>(5, 0.0) holds five zeros. A bool array made with no value holds false, since a bool is never na. map.get of a key the map doesn't hold reads na.

A collection declared without var is right for a list you fill and read on the same bar, such as the ten values a loop collects. Anything you mean to carry to the next bar needs var.

Reading and writing ​

Indexes count from 0, and a negative index counts from the end, so a.get(-1) is the last element. An array index past either end stops the script with an error such as "Array index 5 out of bounds (size 3)", and so do pop, shift, first and last on an empty array. Errors shows what a stopped run looks like.

WARNING

A matrix is looser: m.get past its edge reads na and m.set there does nothing, where TradingView stops the script. A wrong matrix index goes unnoticed here, so check it against m.rows() and m.columns().

for...in walks a collection in order:

pine
//@version=6
indicator("Weekday with the most up closes")
var counts = map.new<int, int>()
if close > open
    counts.put(dayofweek, nz(counts.get(dayofweek)) + 1)
int best = na
int most = 0
for [day, n] in counts      // each key with its value
    if n > most
        best := day
        most := n
plot(best, "Weekday")      // 1 is Sunday, 7 Saturday
LoopWalks
for v in aEach element of an array
for [i, v] in aEach index with its element
for row in mEach row of a matrix, as an array
for [k, v] in mEach key of a map with its value, in the order the keys were first put

Shared, not copied ​

A collection variable holds a reference to the collection, not its values. b = a gives one array a second name, so a change through b shows through a. A function you pass an array to gets the same array and can change it, and an array of arrays or objects holds references to them.

To get a collection you can change apart from the first, make a copy: array.copy(a), map.copy(m) or matrix.copy(m). Don't use a.slice() for this: here a slice is a copy, but on TradingView it shares the original's elements, so a script that writes to one reads differently there (Pine compatibility). A copy is one level deep, so an object, array or map held inside it is the same one in both. array's References and copies has an example, and map and matrix read the same way.

An earlier bar's collection ​

a[1] is the collection as it stood when the bar before closed: its size and its elements then. On the first bar it's na. Like a copy, it's one level deep, so an array or object held inside it is today's, with today's changes. Keep a copy of your own when you need the older contents of what's inside.

Map keys ​

A map matches a key only when it's exactly equal. Two floats are one key only when they are the same number, although == treats floats within 1e-10 of each other as equal, so a key worked out at run time can miss the one you meant. Round a float before you use it as a key, or key on an int or a string. map's Key equality has the full rules.

How big a collection can grow ​

An array or a matrix holds at most 100,000 elements. Making or growing one past that stops the script with an error. A map has no limit here, where TradingView's holds at most 50,000 keys, so keep a map you mean to publish under that.

What AmenoScript adds ​

Pine refuses a collection inside a collection, such as array<array<float>> or map<string, array<float>>: hold the inner one in a type of your own instead. AmenoScript accepts them as written, and adds list literals and lambdas such as a.map(x => x * 2). See Syntax TradingView refuses, Lists written in the text and Lambdas.

Traps ​

  • No var, no memory. A collection declared without var starts empty on every bar, so a list you push to once a bar never holds more than one value.
  • A list that only grows. Pushing on every bar with nothing removed reaches the 100,000 limit on a long chart. Drop the oldest with shift() once the list is as long as you need.
  • One object in every slot. array.new<Swing>(3, Swing.new()) fills all three slots with the same object. Push a Swing.new() of its own for each. Types and enums explains why.
  • A float key that never matches. A float made at run time and the literal you look it up by can differ in the last bit. Round it first.
  • ta.* inside a loop over elements. A ta.sma called in a for loop keeps one state for all its passes, so it doesn't give one average per element, and the run warns you. State in functions and branches explains why, and AmenoScript's request.each runs a calculation per member of a list.

See also ​

AmenoScript is compatible with Pine Script v6. Pine Script is a trademark of TradingView, Inc., which has no part in AmenoScript.