Skip to content

Each and pairs ​

How do you compute the same indicator on fifty coins at once, each with its own history? Pass the list and the expression to request.each. It runs the expression on every member's own bars, as if each had a chart of its own, and returns one value per member for this bar. request.pairs does the same for every ordered pair of members, on the ratio of their closes.

ameno
indicator("Coins up over 30 days")
array<string> m = request.universe(20, exclude_tags = "stablecoin")
// One 30-day return per member, each on its own bars
array<float> mom = request.each(m, close / close[30] - 1)
plot(array.size(array.filter(mom, r => r > 0)), "Coins up over 30 days")

The result is a plain array in the list's order. Pass it to the cross-section functions to rank, z-score or pick from it, or to a book as weights.

request.each ​

request.each(members, expression, gaps) reads expression on each member the way request.security reads it on one symbol, at the chart's timeframe. Each member's ta.* calls, var variables and x[k] history are its own. A CMC: member has one bar a day at any timeframe, so close[30] on it is 30 days back on any chart.

  • A tuple gives one array per element: [hi, lo] = request.each(m, [ta.highest(high, 20), ta.lowest(low, 20)]).
  • gaps = barmerge.gaps_on reads na on a bar where the member printed nothing new.
  • A member with no data yet reads na, and its bars are fetched for the next run.
  • A member can leave the list and come back. Its expression keeps running on its own bars while it is out, so it comes back up to date. Who else is a member never changes its value.
  • It can read a strategy held in a function, one account per member. See A strategy held in a function.

request.pairs ​

request.pairs(members, expression, warm) returns a matrix: row i, column j is expression run on member i over member j. Inside it, three names describe the pair:

NameValue
closeThe ratio of the two closes, row over column.
leftThe row member's close.
rightThe column member's close.

close[k], left[k] and right[k] read the pair's own past. The diagonal is na, and so is a pair whose right-hand close is zero. A tuple expression gives one matrix per element.

ameno
indicator("Strongest by pairs")
array<string> m = request.universe(10, exclude_tags = "stablecoin")
// Row i, column j: is coin i trending up against coin j?
matrix<float> up = request.pairs(m, close > ta.ema(close, 50) ? 1.0 : -1.0)
// Per coin: the coins it trends up against, less those it trends down
array<float> score = cs.row_sum(up)
plot(array.max(score), "Best score")

A ratio is only a close, so a pair has no open, high, low or volume. Those names, hl2 and its kin, and any builtin that reads the bar without being told what to read (ta.atr, ta.tr, ta.vwma, the short form ta.highest(5)) are errors inside request.pairs. So are request.*, syminfo.* and barstate.islast.

ameno
indicator("A pair has no high")
array<string> m = request.universe(10)
matrix<float> r = request.pairs(m, high / low)
plot(matrix.rows(r))

When a pair steps ​

A pair steps on each bar either coin printed, from the first bar on which both have a price. A coin with no print on that bar holds its last close, and a bar neither printed doesn't count. For CMC: members a bar is a day. So a pair's values don't depend on the chart it runs on. A pair reads closed bars only.

warm decides where a pair starts:

warmA pair starts
true, the defaultAt the two coins' first shared price, whenever it is first read. A coin that joins the list arrives with its past.
falseOn the first bar it is read, as a new listing would.

Either way the pair then runs on. A coin that leaves and comes back finds its pairs caught up.

What it refuses ​

These stop the script before it runs:

  • x[k] on a result. Positions move when members move, so mom[5] has no meaning. Take the past inside: request.each(m, close[5]).
  • A call anywhere but the top level. Inside a function, a block or another request there is no single list per bar.
ameno
indicator("The past of a cross-section")
array<string> m = request.universe(10)
array<float> c = request.each(m, close)
array<float> old = c[5] // refused: write request.each(m, close[5])
plot(array.size(old))

The list's own past, m[1], is fine.

What they cost ​

request.each runs your expression once per member. request.pairs runs it once per ordered pair, which is n × (n − 1) of them: 90 for 10 members, 2,450 for 50. Keep the expression small, and compute anything that doesn't need the pair outside it.

Up to 12,000 pairs are kept between bars. Past that, the longest unread pairs go, and a pair read again replays from its start to the same values, so the limit never changes a result. A name of your own read through x[k] inside a pair keeps every bar of that pair, where close[k] keeps only what it needs.

A chart runs these scripts on Rust native, on the server, by default. The engines says when each engine runs.

Traps ​

  • ta.* in a for loop over members shares one state. In a loop over the list, one ta.sma call sees each member's value in turn, as if they were one series, so every member's average mixes in the others'. Use request.each, which keeps one state per member.
  • A value taken out of a result has another coin's past. After v = array.get(c, 0), v[1] is whichever coin was first on the bar before. Take the past inside instead: request.each(m, close[1]).
  • A member's first bars are warm-up. A coin that joined recently has few bars, so a 200-bar average reads na for it until it has 200.

See also ​

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