Language Basics
OpenPine implements Pine Script v6. Every script begins with a version annotation and a declaration statement.
Script Structure
//@version=6
indicator("My Indicator")
// Your code here
plot(close)The first line //@version=6 is required. The second line declares the script type — either indicator(), strategy(), or library().
Comments
// Single-line comment
float a = 10 // End-of-line comment
//@version=6 // Compiler annotation
//@description A library for math utilitiesThere are no multi-line comments in Pine Script. Special comments starting with //@ are compiler annotations (version, description, etc.).
Statements
Statements are separated by newlines. Semicolons are not used. Multiple statements can appear on one line separated by commas:
a = 1
b = 2
c = a + b
// Multiple statements on one line
a = 1, b = 2, c = 3Blank lines and comment-only lines are ignored.
Indentation
Pine Script uses indentation to define code blocks (similar to Python). One indentation level equals 4 spaces or 1 Tab:
// Correct: 4 spaces
if close > open
color = color.green
label.new(bar_index, high, "Up")
else
color = color.red
// Correct: Tab
if close > open
doSomething()
// WRONG: 2 spaces — will NOT be recognized as a code block
if close > open
doSomething() // This is NOT part of the if block!Nested blocks require cumulative indentation:
if condition1 // level 0
if condition2 // level 1 (4 spaces)
for i = 0 to 10 // level 2 (8 spaces)
doSomething(i) // level 3 (12 spaces)Spaces and Tabs can be mixed, but each indentation unit must be a complete 4 spaces or 1 Tab.
Line Continuation
Long expressions can span multiple lines. Inside brackets (parentheses, square brackets), any indentation is allowed:
plot(
series = close,
title = "Close",
color = color.red,
linewidth = 2
)
array<int> arr = [
1,
2,
3
]Outside brackets, continuation lines must have extra whitespace (at least 1 space or tab more than the current block level):
// Correct: continuation with extra spaces
float result = longVariableName
+ anotherLongVariable
- someOtherValue
// WRONG: no extra spaces — parsed as a new statement!
float result = a
+ b // This is a separate statement!Basic Types
Primitive Types
| Type | Description | Examples |
|---|---|---|
int | Integer. Range: −9,007,199,254,740,992 to 9,007,199,254,740,992 (±2⁵³). | 42, -123, +5 |
float | Floating-point number. Approximate range: ±1.8 × 10³⁰⁸; precision: ~15–17 significant decimal digits. | 3.14, .5, 3., 1e-3, 1.2E+5 |
bool | Boolean | true, false |
string | Text | "hello", 'world' |
color | RGBA color | #FF0000, #FF000080 |
na | Missing/undefined value | na |
int and float
// Integers
a = 42
b = -123
c = +5
// Floats — decimal notation
d = 3.14
e = .14 // leading dot
f = 3. // trailing dot
// Floats — scientific notation
g = 314e-2 // 3.14
h = 0.00314E+2 // 0.314string
Strings can use single or double quotes with identical semantics:
a = "Hello, World!"
b = 'Hello, World!'
full = "Hello, " + "World!" // String concatenation with +Supported escape sequences:
| Escape | Meaning |
|---|---|
\\ | Backslash |
\n | Newline |
\r | Carriage return |
\t | Tab |
\" or "" | Double quote (in double-quoted strings) |
\' or '' | Single quote (in single-quoted strings) |
color
Color literals use hex notation with #:
#RRGGBB— 6 hex digits, fully opaque (alpha = FF)#RRGGBBAA— 8 hex digits, explicit alpha channel
red = #FF0000 // fully opaque red
semiRed = #FF000080 // semi-transparent redNamed color constants are available via the color module: color.red, color.blue, color.green, etc. Use color.new() to create colors with custom transparency.
na
na represents a missing or undefined value. It is compatible with any type but requires an explicit type annotation when used alone:
float a = na // OK
int b = na // OK
a = na // ERROR: cannot infer variable type
// Test for na with na():
if na(myValue)
// handle missing valueOperators
Arithmetic
a = 10 + 3 // 13
b = 10 - 3 // 7
c = 10 * 3 // 30
d = 10 / 3 // 3.333...
e = 10 % 3 // 1Comparison
close > open // Greater than
close < open // Less than
close >= 100 // Greater or equal
close <= 100 // Less or equal
close == open // Equal
close != open // Not equalLogical
a and b // Logical AND
a or b // Logical OR
not a // Logical NOTTernary
color = close > open ? color.green : color.redHistory Reference Operator
In Pine Script, every expression is evaluated once per bar, producing a time series of values. The [] operator accesses the value that an expression produced on a previous bar — not just built-in variables, but any expression:
// Built-in series variables
previousClose = close[1] // close price one bar ago
twoBarsAgo = high[2] // high price two bars ago
// Function call results — retrieves the already-computed result, not a re-evaluation
prevSma = ta.sma(close, 14)[1] // the SMA value computed on the previous bar
prevEma = ta.ema(close, 10)[3] // the EMA value from 3 bars ago
// Arithmetic expressions
prevRange = (high - low)[1] // the bar range from the previous bar
// User-defined variables
myValue = close * volume
prevMyValue = myValue[1] // the value myValue had on the previous barKey concept
ta.sma(close, 14)[1] does not compute a new SMA — it returns the SMA value that was already calculated when the script ran on the previous bar.
On early bars where insufficient history exists, the result is na. The offset can be a series expression:
int lookback = input.int(5, "Lookback")
pastClose = close[lookback]Collection Types
Pine Script provides three generic collection types:
| Type | Description |
|---|---|
array<T> | Ordered, indexable sequence |
matrix<T> | 2D table of rows × columns |
map<K, V> | Key-value store |
Collections are reference types — assigning a collection to another variable copies the reference, not the contents. Use .copy() to get an independent copy.
Collections are typically declared with var so they persist across bars rather than being recreated on every bar:
var array<float> highs = array.new<float>()
highs.push(high) // accumulates one value per barDeprecated syntax
The shorthand int[], float[] etc. is a legacy alias for array<int>, array<float> and will be removed in a future version. Always use the array<T> form.
array<T>
An ordered, zero-indexed sequence that grows and shrinks dynamically.
Creating arrays
// Empty array, then fill manually
var array<float> a = array.new<float>()
// Pre-sized, all elements set to 0.0
array<float> b = array.new<float>(5, 0.0)
// From a literal list
array<int> c = array.from(10, 20, 30)Reading and writing elements
a.push(close) // append to end
a.unshift(close) // prepend to front
float last = a.last() // last element
float first = a.first() // first element
float val = a.get(2) // by index (negative index counts from end)
a.set(2, 99.0) // overwrite by index
a.insert(1, 42.0) // insert at index, shifting right
float removed = a.remove(1) // remove at index, returns removed value
float popped = a.pop() // remove and return last element
float shifted = a.shift() // remove and return first element
int n = a.size() // number of elementsSearching
bool found = a.includes(close) // true if value exists
int idx = a.indexof(close) // first index, or -1
int last = a.lastindexof(close) // last index, or -1Slicing and combining
array<float> sub = a.slice(1, 4) // elements [1, 4)
array<float> both = a.concat(b) // new array with b appended
array<float> dup = a.copy() // independent copy
a.reverse() // reverse in place
a.fill(0.0, 0, 3) // fill elements [0, 3) with 0.0
a.clear() // remove all elementsSorting
a.sort() // ascending (default)
a.sort(order.descending) // descending
array<int> idx = a.sort_indices() // indices that would sort a, without modifying aStatistics (numeric arrays only)
float total = a.sum()
float mean = a.avg()
float med = a.median()
float sd = a.stdev()
float hi = a.max()
float lo = a.min()
float rng = a.range() // max - minIterating with for...in
var array<float> vals = array.from(1.0, 2.0, 3.0)
float total = 0.0
for v in vals
total += v
// With index
for [i, v] in vals
log.info("{0}: {1}", i, v)map<K, V>
An unordered key-value store. Keys must be a primitive type (int, float, bool, string, color); values can be any type.
var map<string, float> m = map.new<string, float>()
m.put("open", open) // insert or update; returns previous value or na
m.put("close", close)
float o = m.get("open") // returns na if key absent
bool exists = m.contains("close")
int count = m.size()
float old = m.remove("open") // removes key, returns removed value or na
array<string> ks = m.keys() // all keys as array
array<float> vs = m.values() // all values as array
m.put_all(other) // merge another map in
m.clear() // remove all entries
map<string, float> dup = m.copy()Iterating
for [k, v] in m
log.info("{0} = {1}", k, v)matrix<T>
A two-dimensional table indexed by [row, column], both zero-based.
Creating
// 3×4 matrix, all elements 0.0
matrix<float> mat = matrix.new<float>(3, 4, 0.0)Reading and writing
mat.set(0, 0, 1.5) // set element at row 0, col 0
float v = mat.get(0, 0) // read element
int r = mat.rows() // number of rows
int c = mat.columns() // number of columns
array<float> row0 = mat.row(0) // entire row as array
array<float> col1 = mat.col(1) // entire column as arrayStructural operations
mat.add_row(na, array.from(1.0, 2.0, 3.0, 4.0)) // append a row
mat.add_col() // append a column filled with na
mat.remove_row() // remove last row (returns it)
mat.remove_col(0) // remove column 0 (returns it)
mat.reshape(2, 6) // change shape (same element count)
matrix<float> sub = mat.submatrix(0, 2, 0, 2) // rows [0,2), cols [0,2)
matrix<float> t = mat.transpose() // rows ↔ columns
mat.swap_rows(0, 1)
mat.swap_columns(0, 1)
mat.reverse() // reverse element order in place
mat.fill(0.0) // fill entire matrix
mat.sort() // sort all elements ascending
matrix<float> dup = mat.copy()Math operations (numeric matrices)
float total = mat.avg()
float hi = mat.max()
float lo = mat.min()
float med = mat.median()
matrix<float> s = mat.sum(other) // element-wise sum
matrix<float> d = mat.diff(other) // element-wise difference
matrix<float> p = mat.multi(other) // matrix multiplication
matrix<float> i = mat.inv() // matrix inverse
float det = mat.det() // determinant
int rk = mat.rank() // rank
float tr = mat.trace() // trace (sum of diagonal)
matrix<float> pw = mat.pow(2) // matrix powerTuples
Tuples group multiple return values from functions:
calculate(x, y) =>
[x + y, x - y] // return a tuple
[sum, diff] = calculate(10, 3)Next Steps
- Types & Variables — type qualifiers, var/varip, tuple destructuring
- Control Structures — if, for, while, switch
- Functions & Methods — defining and calling functions