Skip to content

Language Basics

OpenPine implements Pine Script v6. Every script begins with a version annotation and a declaration statement.

Script Structure

pine
//@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

pine
// Single-line comment
float a = 10  // End-of-line comment

//@version=6                          // Compiler annotation
//@description A library for math utilities

There 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:

pine
a = 1
b = 2
c = a + b

// Multiple statements on one line
a = 1, b = 2, c = 3

Blank 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:

pine
// 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:

pine
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:

pine
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):

pine
// 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

TypeDescriptionExamples
intInteger. Range: −9,007,199,254,740,992 to 9,007,199,254,740,992 (±2⁵³).42, -123, +5
floatFloating-point number. Approximate range: ±1.8 × 10³⁰⁸; precision: ~15–17 significant decimal digits.3.14, .5, 3., 1e-3, 1.2E+5
boolBooleantrue, false
stringText"hello", 'world'
colorRGBA color#FF0000, #FF000080
naMissing/undefined valuena

int and float

pine
// 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.314

string

Strings can use single or double quotes with identical semantics:

pine
a = "Hello, World!"
b = 'Hello, World!'
full = "Hello, " + "World!"  // String concatenation with +

Supported escape sequences:

EscapeMeaning
\\Backslash
\nNewline
\rCarriage return
\tTab
\" 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
pine
red = #FF0000          // fully opaque red
semiRed = #FF000080    // semi-transparent red

Named 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:

pine
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 value

Operators

Arithmetic

pine
a = 10 + 3    // 13
b = 10 - 3    // 7
c = 10 * 3    // 30
d = 10 / 3    // 3.333...
e = 10 % 3    // 1

Comparison

pine
close > open     // Greater than
close < open     // Less than
close >= 100     // Greater or equal
close <= 100     // Less or equal
close == open    // Equal
close != open    // Not equal

Logical

pine
a and b    // Logical AND
a or b     // Logical OR
not a      // Logical NOT

Ternary

pine
color = close > open ? color.green : color.red

History 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:

pine
// 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 bar

Key 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:

pine
int lookback = input.int(5, "Lookback")
pastClose = close[lookback]

Collection Types

Pine Script provides three generic collection types:

TypeDescription
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:

pine
var array<float> highs = array.new<float>()
highs.push(high)   // accumulates one value per bar

Deprecated 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

pine
// 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

pine
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 elements

Searching

pine
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 -1

Slicing and combining

pine
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 elements

Sorting

pine
a.sort()                           // ascending (default)
a.sort(order.descending)           // descending
array<int> idx = a.sort_indices()  // indices that would sort a, without modifying a

Statistics (numeric arrays only)

pine
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 - min

Iterating with for...in

pine
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.

pine
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

pine
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

pine
// 3×4 matrix, all elements 0.0
matrix<float> mat = matrix.new<float>(3, 4, 0.0)

Reading and writing

pine
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 array

Structural operations

pine
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)

pine
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 power

Tuples

Tuples group multiple return values from functions:

pine
calculate(x, y) =>
    [x + y, x - y]          // return a tuple

[sum, diff] = calculate(10, 3)

Next Steps

Released under the MIT License.