Skip to content

Cross-section ​

How do you rank fifty coins against each other on each bar, or turn their scores into weights? Use the cs.* functions. Each takes an array with one cell per member, such as the result of request.each, and returns a new array in the same order.

ameno
indicator("Who leads this month")
array<string> names = array.from("BTC-USD", "ETH-USD", "SOL-USD", "XRP-USD")
array<float> mom = request.each(names, close / close[30] - 1)
array<float> rank = cs.rank(mom) // 0 for the weakest, 1 for the strongest
// Long the leaders, short the laggards, gross 1
array<float> w = cs.scale(cs.demean(mom))
plot(array.get(rank, 0), "BTC's rank")
plot(array.get(w, 0), "BTC's weight")

The functions ​

FunctionReturns
cs.rank(x)Each cell's percentile, from 0 for the lowest to 1 for the highest. Ties share their average.
cs.zscore(x)Each cell's distance from the mean, in sample standard deviations.
cs.demean(x, group)Each cell less the mean. With group, an array of labels of the same size, less the mean of the cells that share its label.
cs.scale(x)The cells divided by the sum of their absolute values, so the absolute values sum to 1.
cs.winsor(x, pct)The lowest and highest pct of cells moved to the nearest value kept. Of n cells with a value it moves pct × n at each end, rounded down, and always keeps one in the middle: the default 0.05 of 50 cells moves two.
cs.residual(y, x)What's left of y after a straight-line fit on x, cell by cell.
cs.top(x, n)An array<bool>, true on the n largest cells. A tie goes to the earlier cell, so on a list in rank order, to the better rank.
cs.row_sum(m)One sum per row of a matrix, such as a request.pairs result.

They chain: cs.zscore(cs.winsor(score, 0.05)) trims the extremes before it standardises.

na cells ​

Every cs.* function skips na cells when it computes, and returns na in the same cells. An infinite cell, which a division by zero makes, counts as na too. So a member with no data yet neither moves the mean nor gets a rank. cs.row_sum reads na for a row with nothing in it, not 0, so a member with no pairs yet doesn't look average.

Passed to book.target, a na cell reads as a zero weight.

Arithmetic on arrays ​

In AmenoScript, arithmetic works on whole arrays, cell by cell. + - * / %, the comparisons, and, or, not and unary minus take an array and a number, or two arrays of the same size. A matrix takes a number or a matrix of the same shape, cell by cell. So these are ordinary expressions:

ameno
indicator("Scores, cell by cell")
array<string> names = array.from("BTC-USD", "ETH-USD", "SOL-USD")
[fast, slow] = request.each(names, [ta.roc(close, 10), ta.roc(close, 60)])
// One score per coin
array<float> score = cs.zscore(fast) + 0.5 * cs.zscore(slow)
array<bool> above = score >= array.median(score)
plot(array.get(score, 0), "BTC's score")
plot(array.get(above, 0) ? 1 : 0, "BTC above the median")

Two arrays of different sizes stop the run. Pine v6 strict stops on array arithmetic too, since TradingView has none.

Time-series kernels ​

Three ta.* functions often feed a cross-section:

FunctionReturns
ta.ts_rank(x, from, to)The sum, over k from from to to, of +1 where x > x[k] and −1 otherwise, skipping na. Equal values count −1.
ta.cov(x, y, n)The population covariance of x and y over the last n bars where both had a value.
ta.beta(y, x, n)The slope of y on x over the same bars.
ameno
indicator("Beta to Bitcoin")
float btc = request.security("BTC-USD", "1D", close)
float r = math.log(close / close[1])
float rb = math.log(btc / btc[1])
plot(ta.beta(r, rb, 90), "90-day beta")
plot(ta.ts_rank(close, 1, 20), "Closes beaten in the last 20, net")

ta.ts_rank compares with Pine's tolerance, so values within 1e-10 count as equal. to may be a series that changes from bar to bar.

See also ​

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