Appearance
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
| Function | Returns |
|---|---|
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:
| Function | Returns |
|---|---|
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.