Appearance
matrix
How do you hold a table of numbers in a script and do linear algebra on it, such as fitting a line to the last 20 closes? Make a matrix with matrix.new<float>(rows, columns, initial_value), fill it with matrix.set, and combine matrices with matrix.mult, matrix.transpose and matrix.inv. Rows and columns count from 0. Most functions here can also be called as a method, as in m.get(0, 1).
pine
//@version=6
indicator("Slope of the last 20 closes")
int n = 20
X = matrix.new<float>(n, 2, 1.0) // column 0 stays 1: the intercept
y = matrix.new<float>(n, 1)
for i = 0 to n - 1
X.set(i, 1, i) // column 1: the bar's place, oldest first
y.set(i, 0, close[n - 1 - i])
Xt = X.transpose()
beta = Xt.mult(X).inv().mult(Xt).mult(y) // least squares: inv(X'X) X'y
plot(bar_index >= n ? beta.get(1, 0) : na, "Slope per bar")Shape and indexes
A matrix has matrix.rows rows and matrix.columns columns, and every row is as long as the first. It holds at most 100,000 elements, and making or growing one past that stops the script with an error.
An index past an edge reads na here
matrix.get, matrix.row and matrix.col past an edge read na, and matrix.set there does nothing. TradingView stops the script with an error, so a wrong index here goes unnoticed.
References and copies
A matrix variable holds a reference, so m2 = m gives one matrix two names. matrix.set, fill, add_row, add_col, remove_row, remove_col, swap_rows, swap_columns, reverse and sort change the matrix itself. Every other function returns a new matrix or array, and changing it leaves the original as it was.
An earlier bar's matrix
m[1] keeps the rows the matrix had when the previous bar closed, so a row added since doesn't show in it. The rows themselves are shared with today's matrix, though, so a cell written since shows its new value in m[1]. To compare with the earlier bar's values, keep a matrix.copy of them yourself.
Where this engine parts from TradingView
Several functions don't yet do what TradingView's do. Each says so in a warning: matrix.concat, matrix.diff, matrix.reshape, matrix.reverse, matrix.mult with an array, matrix.add_row and matrix.add_col without an array, matrix.remove_row and matrix.remove_col without an index, and the parameter names of matrix.row and matrix.col.
matrix.new
pine
matrix.new<type>(rows, columns, initial_value) → matrix<type>A new matrix of rows by columns, every element set to initial_value.
With no initial value each element is na, a matrix<bool> included. An object given as the initial value is one object shared by every cell.
| Parameter | Default | Description |
|---|---|---|
rowsseries int | 0 | How many rows. |
columnsseries int | 0 | How many columns. |
initial_value<matrix_type> | na | The value of every element. |
matrix.get
pine
matrix.get(id, row, column) → <matrix_type>Also a method: id.get(row, column).
The element at row and column, or na past an edge: see shape and indexes.
| Parameter | Description |
|---|---|
idany matrix type | The matrix. |
rowseries int | The row, from 0. |
columnseries int | The column, from 0. |
matrix.set
pine
matrix.set(id, row, column, value) → voidAlso a method: id.set(row, column, value).
Sets the element at row and column to value. Past an edge it does nothing.
| Parameter | Description |
|---|---|
idany matrix type | The matrix. |
rowseries int | The row, from 0. |
columnseries int | The column, from 0. |
valueseries <type of the matrix's elements> | The new value. |
matrix.rows
pine
matrix.rows(id) → series intAlso a method: id.rows().
How many rows the matrix has.
| Parameter |
|---|
idany matrix type |
matrix.columns
pine
matrix.columns(id) → series intAlso a method: id.columns().
How many columns the matrix has, or 0 if it has no rows.
| Parameter |
|---|
idany matrix type |
matrix.elements_count
pine
matrix.elements_count(id) → series intAlso a method: id.elements_count().
How many elements the matrix holds: its rows times its columns.
| Parameter |
|---|
idany matrix type |
matrix.row
pine
matrix.row(id, index) → array<type>Also a method: id.row(index).
A new array of the values in row index, or na past the last row.
Pass the row by position
TradingView names this parameter row. Here it is index, so pass it by position. matrix.row(m, row = 1) stops the script with an error.
| Parameter | Description |
|---|---|
idany matrix type | The matrix. |
indexseries int | The row, from 0. |
matrix.col
pine
matrix.col(id, index) → array<type>Also a method: id.col(index).
A new array of the values in column index, or na past the last column.
Pass the column by position
TradingView names this parameter column. Here it is index, so pass it by position. matrix.col(m, column = 1) doesn't read the column you name, and on the compiled engine it stops the script with an error.
| Parameter | Description |
|---|---|
idany matrix type | The matrix. |
indexseries int | The column, from 0. |
matrix.add_row
pine
matrix.add_row(id, row, array_id) → voidAlso a method: id.add_row(row, array_id).
Inserts a row holding the elements of array_id at row, moving the rows from there down one.
A row of another length is kept
Give an array as long as the matrix is wide. Without an array, the new row is empty, and with an array of another length, the row keeps that length. Either way the rows no longer match, where TradingView fills a missing row with na.
| Parameter | Description |
|---|---|
idany matrix type | The matrix. |
rowseries int | Where the new row goes, from 0. Left out, after the last row. |
array_idany array type | The new row's values. |
matrix.add_col
pine
matrix.add_col(id, column, array_id) → voidAlso a method: id.add_col(column, array_id).
Inserts a column holding the elements of array_id at column, moving the columns from there right one.
Missing cells hold 0 here
Without an array the new column holds 0, and an array shorter than the matrix is tall leaves 0 in the rest. TradingView fills them with na.
| Parameter | Description |
|---|---|
idany matrix type | The matrix. |
columnseries int | Where the new column goes, from 0. Left out, after the last column. |
array_idany array type | The new column's values. |
matrix.remove_row
pine
matrix.remove_row(id, row) → array<type>Also a method: id.remove_row(row).
Removes row row and returns its values as an array.
The default row differs by engine
Always pass the row. Left out, the TS engine removes nothing and the Rust engine removes the last row.
| Parameter | Description |
|---|---|
idany matrix type | The matrix. |
rowseries int | The row to remove, from 0. |
matrix.remove_col
pine
matrix.remove_col(id, column) → array<type>Also a method: id.remove_col(column).
Removes column column and returns its values as an array.
The default column differs by engine
Always pass the column. Left out, the TS engine removes nothing and the Rust engine removes the last column.
| Parameter | Description |
|---|---|
idany matrix type | The matrix. |
columnseries int | The column to remove, from 0. |
matrix.swap_rows
pine
matrix.swap_rows(id, row1, row2) → voidAlso a method: id.swap_rows(row1, row2).
Swaps rows row1 and row2.
| Parameter |
|---|
idany matrix type |
row1series int |
row2series int |
matrix.swap_columns
pine
matrix.swap_columns(id, column1, column2) → voidAlso a method: id.swap_columns(column1, column2).
Swaps columns column1 and column2.
| Parameter |
|---|
idany matrix type |
column1series int |
column2series int |
matrix.fill
pine
matrix.fill(id, value, from_row, to_row, from_column, to_column) → voidAlso a method: id.fill(value, from_row, to_row, from_column, to_column).
Sets every element in a block of rows and columns to value. Each range runs from its first index up to, but not including, its last.
| Parameter | Default | Description |
|---|---|---|
idany matrix type | The matrix. | |
valueseries <type of the matrix's elements> | The value to set. | |
from_rowseries int | 0 | The first row to fill. |
to_rowseries int | The row to stop before. Left out, the fill runs to the last row. | |
from_columnseries int | 0 | The first column to fill. |
to_columnseries int | The column to stop before. Left out, the fill runs to the last column. |
matrix.reverse
pine
matrix.reverse(id) → voidAlso a method: id.reverse().
Reverses the order of the rows, in place.
Each row keeps its order here
TradingView's reverses the columns too, so its first element becomes the last. Here each row keeps its order: [[1, 2], [3, 4]] becomes [[3, 4], [1, 2]], where TradingView gives [[4, 3], [2, 1]].
| Parameter |
|---|
idany matrix type |
matrix.sort
pine
matrix.sort(id, column, order, sort_field) → voidAlso a method: id.sort(column, order, sort_field).
Reorders the rows by the values in one column, in place.
A na sorts last in ascending order.
| Parameter | Default | Description |
|---|---|---|
idany matrix type | The matrix. | |
columnseries int | 0 | The column whose values set the order. |
ordersimple sort_order | order.ascending | order.ascending or order.descending. |
sort_fieldseries string/int | In a matrix of objects, the field to sort by: its name, or its place counting from 0. Left out, the first field. |
matrix.copy
pine
matrix.copy(id) → matrix<type>Also a method: id.copy().
A new matrix holding the same values. An object in a cell is the same object in both.
| Parameter |
|---|
idany matrix type |
matrix.submatrix
pine
matrix.submatrix(id, from_row, to_row, from_column, to_column) → matrix<type>Also a method: id.submatrix(from_row, to_row, from_column, to_column).
A new matrix of the rows from from_row and the columns from from_column, each up to, but not including, the last index given.
| Parameter | Default | Description |
|---|---|---|
idany matrix type | The matrix. | |
from_rowseries int | 0 | The first row to take. |
to_rowseries int | The row to stop before. Left out, the last row is included. | |
from_columnseries int | 0 | The first column to take. |
to_columnseries int | The column to stop before. Left out, the last column is included. |
matrix.transpose
pine
matrix.transpose(id) → matrix<type>Also a method: id.transpose().
A new matrix with the rows and columns swapped, so row i becomes column i.
| Parameter |
|---|
idany matrix type |
matrix.reshape
pine
matrix.reshape(id, rows, columns) → voidAlso a method: id.reshape(rows, columns).
A new matrix of rows by columns, filled with the elements read row by row. It reads na unless the counts match.
It returns a new matrix here
TradingView's reshapes id itself and returns nothing. Here id stays as it was, so assign the result: m2 = matrix.reshape(m, 3, 2).
| Parameter | Description |
|---|---|
idany matrix type | The matrix. |
rowsseries int | The new number of rows. |
columnsseries int | The new number of columns. |
matrix.concat
pine
matrix.concat(id1, id2) → matrix<type>Also a method: id1.concat(id2).
A new matrix with the columns of id2 after those of id1. It reads na unless the two have as many rows.
It returns a new matrix, joined side by side
TradingView's appends id2 to id1 itself and returns id1. Here id1 stays as it was, and the result is joined side by side.
| Parameter | Description |
|---|---|
id1any matrix type | The matrix on the left. |
id2any matrix type | The matrix added on its right. |
matrix.sum
pine
matrix.sum(id1, id2) → matrix<float>/matrix<int>
matrix.sum(id1) → series float // AmenoScript onlyAlso a method: id1.sum(id2).
A new matrix of id1 plus id2, element by element, or of id1 with a number added to every element.
Two matrices of different shapes give na. With id2 left out, the call gives the sum of every element of id1 as a number, and na if any is na. TradingView has no such one-argument form.
| Parameter | Description |
|---|---|
id1matrix<int>/matrix<float> | The matrix. |
id2matrix<int>/matrix<float>/series float | A matrix of the same shape, or a number. |
matrix.diff
pine
matrix.diff(id1, id2) → matrix<float>/matrix<int>Also a method: id1.diff(id2).
A new matrix of the differences between each row of id1 and the row before it.
The second matrix is ignored here
TradingView's returns id1 minus id2, a matrix or a number. Here id2 is ignored, so matrix.diff(a, b) gives the wrong result. Subtract with matrix.sum(a, matrix.mult(b, -1)) instead.
| Parameter | Description |
|---|---|
id1matrix<int>/matrix<float> | The matrix. |
id2matrix<int>/matrix<float> | Read nowhere. |
matrix.mult
pine
matrix.mult(id1, id2) → matrix<float>Also a method: id1.mult(id2).
A new matrix: the matrix product of id1 and id2, or id1 with every element multiplied by a number.
For a product, id1 needs as many columns as id2 has rows, and the result has id1's rows and id2's columns. Other shapes give na.
A matrix times an array reads na here
TradingView also multiplies a matrix by an array, giving an array. Here that reads na, so turn the array into a one-column matrix first.
| Parameter | Description |
|---|---|
id1matrix<int>/matrix<float> | The matrix on the left. |
id2matrix<int>/matrix<float>/series float | The matrix on the right, or a number. |
matrix.kron
pine
matrix.kron(id1, id2) → matrix<float>/matrix<int>Also a method: id1.kron(id2).
A new matrix: the Kronecker product of id1 and id2, in which each element of id1 multiplies a whole copy of id2.
| Parameter |
|---|
id1matrix<int>/matrix<float> |
id2matrix<int>/matrix<float> |
matrix.pow
pine
matrix.pow(id, power) → matrix<float>/matrix<int>Also a method: id.pow(power).
A new matrix: a square matrix multiplied by itself power times.
A power of 0 gives the identity matrix. A negative power gives the inverse of the matrix raised to that power, or na if there is none. A matrix that isn't square gives na.
| Parameter | Description |
|---|---|
idmatrix<int>/matrix<float> | The square matrix. |
powerseries int | How many times to multiply it. |
matrix.det
pine
matrix.det(id) → series float/intAlso a method: id.det().
The determinant of a square matrix: 0 for a singular one, and na for one that isn't square.
| Parameter |
|---|
idmatrix<int>/matrix<float> |
matrix.inv
pine
matrix.inv(id) → matrix<float>/matrix<int>Also a method: id.inv().
A new matrix, the inverse of a square matrix, or na when it is singular or not square.
| Parameter |
|---|
idmatrix<int>/matrix<float> |
matrix.pinv
pine
matrix.pinv(id) → matrix<float>/matrix<int>Also a method: id.pinv().
A new matrix, the pseudoinverse of id.
Only a full-rank matrix inverts here
Here it works only when the rows or the columns are independent (full rank), and reads na otherwise. TradingView's gives a pseudoinverse for any matrix.
| Parameter |
|---|
idmatrix<int>/matrix<float> |
matrix.rank
pine
matrix.rank(id) → series intAlso a method: id.rank().
How many independent rows the matrix has, counting pivots larger than 1e-10.
| Parameter |
|---|
idany matrix type |
matrix.trace
pine
matrix.trace(id) → series float/intAlso a method: id.trace().
The sum of the main diagonal of a square matrix, or na for one that isn't square.
| Parameter |
|---|
idmatrix<int>/matrix<float> |
matrix.eigenvalues
pine
matrix.eigenvalues(id) → array<float>/array<int>Also a method: id.eigenvalues().
An array of the eigenvalues of a square matrix, or na if it isn't square.
A 2 by 2 matrix gives the larger first. A larger matrix gives them in order of size, ignoring sign, so -5 comes before 3.
A complex eigenvalue reads as its real part
An eigenvalue that is a complex number comes back as its real part alone, so a rotation's ±i read 0.
| Parameter |
|---|
idmatrix<int>/matrix<float> |
matrix.eigenvectors
pine
matrix.eigenvectors(id) → matrix<float>/matrix<int>Also a method: id.eigenvectors().
A new matrix whose column j is the eigenvector, scaled to length 1, for element j of matrix.eigenvalues.
It reads na if the matrix isn't square, or if a vector can't be found.
| Parameter |
|---|
idmatrix<int>/matrix<float> |
matrix.avg
pine
matrix.avg(id) → series float/intAlso a method: id.avg().
The mean of the elements, skipping na.
| Parameter |
|---|
idmatrix<int>/matrix<float> |
matrix.min
pine
matrix.min(id) → series float/intAlso a method: id.min().
The smallest element, skipping na.
| Parameter |
|---|
idmatrix<int>/matrix<float> |
matrix.max
pine
matrix.max(id) → series float/intAlso a method: id.max().
The largest element, skipping na.
| Parameter |
|---|
idmatrix<int>/matrix<float> |
matrix.median
pine
matrix.median(id) → series float/intAlso a method: id.median().
The middle element once sorted, skipping na. With an even count it is the mean of the middle two.
| Parameter |
|---|
idmatrix<int>/matrix<float> |
matrix.mode
pine
matrix.mode(id) → series float/intAlso a method: id.mode().
The element that occurs most often, skipping na. On a tie it gives the smallest of them.
| Parameter |
|---|
idmatrix<int>/matrix<float> |
matrix.is_square
pine
matrix.is_square(id) → series boolAlso a method: id.is_square().
Whether the matrix has as many rows as columns, and at least one.
| Parameter |
|---|
idany matrix type |
matrix.is_zero
pine
matrix.is_zero(id) → series boolAlso a method: id.is_zero().
Whether every element is 0. A matrix with no elements reads true, and a na element reads false.
| Parameter |
|---|
idmatrix<float> |
matrix.is_binary
pine
matrix.is_binary(id) → series boolAlso a method: id.is_binary().
Whether every element is 0 or 1. A na element reads false.
| Parameter |
|---|
idmatrix<float> |
matrix.is_diagonal
pine
matrix.is_diagonal(id) → series boolAlso a method: id.is_diagonal().
Whether every element off the main diagonal is 0. A matrix that isn't square reads false.
| Parameter |
|---|
idmatrix<float> |
matrix.is_antidiagonal
pine
matrix.is_antidiagonal(id) → series boolAlso a method: id.is_antidiagonal().
Whether every element off the diagonal from top right to bottom left is 0. A matrix that isn't square reads false.
| Parameter |
|---|
idmatrix<float> |
matrix.is_identity
pine
matrix.is_identity(id) → series boolAlso a method: id.is_identity().
Whether the matrix has 1 down its main diagonal and 0 everywhere else. A matrix that isn't square reads false.
| Parameter |
|---|
idmatrix<float> |
matrix.is_symmetric
pine
matrix.is_symmetric(id) → series boolAlso a method: id.is_symmetric().
Whether the matrix equals its transpose. A matrix that isn't square reads false.
| Parameter |
|---|
idmatrix<float> |
matrix.is_antisymmetric
pine
matrix.is_antisymmetric(id) → series boolAlso a method: id.is_antisymmetric().
Whether the matrix equals its transpose with every sign flipped, so that its diagonal is 0. A matrix that isn't square reads false.
| Parameter |
|---|
idmatrix<float> |
matrix.is_triangular
pine
matrix.is_triangular(id) → series boolAlso a method: id.is_triangular().
Whether every element above the main diagonal, or every element below it, is 0. A matrix that isn't square reads false.
| Parameter |
|---|
idmatrix<float> |
matrix.is_stochastic
pine
matrix.is_stochastic(id) → series boolAlso a method: id.is_stochastic().
Whether every row sums to 1, to within 1e-9, with no negative element.
| Parameter |
|---|
idmatrix<float> |