Type system
Introduction
The Pine Script™ type system determines the compatibility of a script’s values with various functions and operations. While it’s possible to write simple scripts without knowing anything about the type system, a reasonable understanding of it is necessary to achieve any degree of proficiency with the language, and an in-depth knowledge of its subtleties allows programmers to harness its full potential.
Pine Script™ uses types to classify all values, and it uses qualifiers to determine whether values and references are constant, established on the first script execution, or dynamic across executions. This system applies to all Pine values and references, including literals, variables, expressions, function returns, and function arguments.
The type system closely intertwines with Pine’s execution model and time series concepts. Understanding all three is essential for making the most of the power of Pine Script™.
Qualifiers
Pine Script™ qualifiers identify when values are accessible to a script:
- Values and references qualified as const are established at compile time (i.e., when saving the script in the Pine Editor or adding it to the chart).
- Values qualified as input are established at input time (i.e., when confirming values based on user input, primarily from the “Settings/Inputs” tab).
- Values qualified as simple are established at bar zero (i.e., the first script execution).
- Values qualified as series can change throughout the script’s executions.
Pine Script™ bases the dominance of type qualifiers on the following hierarchy: const < input < simple < series, where “const” is the weakest qualifier and “series” is the strongest. The qualifier hierarchy translates to this rule: whenever a variable, function, or operation is compatible with a specific qualified type, values with weaker qualifiers are also allowed.
Scripts always qualify their expressions’ returned types based on the dominant qualifier in their calculations. For example, evaluating an expression that involves “input” and “series” values will return a value qualified as “series”. Furthermore, scripts cannot change a value’s qualifier to one that’s lower on the hierarchy. If a value acquires a stronger qualifier (e.g., a value initially inferred as “simple” becomes “series” later in the script’s executions), that state is irreversible.
It’s important to note that “series” values are the only ones that can change across script executions, including those from various built-ins, such as close and volume, as well as the results of expressions involving “series” values. All values qualified as “const”, “input”, or “simple” remain consistent across all script executions.
const
Values or references qualified as “const” are established at compile time, before the script starts its executions. Compilation initially occurs when saving a script in the Pine Editor, which does not require it to run on a chart. Values or references with the “const” qualifier never change between script executions, not even on the first execution.
All literal values and the results returned by expressions involving only values qualified as “const” automatically adopt the “const” qualifier.
These are some examples of literal values:
- literal int:
1
,-1
,42
- literal float:
1.
,1.0
,3.14
,6.02E-23
,3e8
- literal bool:
true
,false
- literal color:
#FF55C6
,#FF55C6ff
- literal string:
"A text literal"
,"Embedded single quotes 'text'"
,'Embedded double quotes "text"'
Our Style guide recommends using uppercase SNAKE_CASE to name “const” variables for readability. While not a requirement, one can also use the var keyword when declaring “const” variables so the script only initializes them on the first bar of the dataset. See this section of our User Manual for more information.
Below is an example that uses “const” values within the
indicator()
and
plot()
functions, which both require a value of the “const string” qualified
type as their title
argument:
The following example will raise a compilation error since it uses syminfo.ticker, which returns a “simple” value because it depends on chart information that’s only accessible after the script’s first execution:
The
const
keyword allows the declaration of variables and parameters with constant
value assignments. Declaring a variable with this keyword instructs
the script to forbid using reassignment and compound assignment
operations on it. For example, this script declares the myVar
variable
with the keyword, then attempts to assign a new “float” value to the
variable with the addition assignment operator
(+=),
resulting in a compilation error:
It’s crucial to note that declaring a variable with the const keyword forces it to maintain a constant reference to the value returned by a specific expression, but that does not necessarily define the nature of the assigned value. For example, a script can declare a const variable that maintains a constant reference to an expression returning the ID of a special type. Although the script cannot reassign the variable, the assigned ID is a “series” value:
input
Most values qualified as “input” are established after initialization
via the input.*()
functions. These functions produce values that users
can modify within the “Inputs” tab of the script’s settings. When one
changes any of the values in this tab, the script restarts from the
beginning of the chart’s history to ensure its inputs are consistent
throughout its executions. Some of Pine’s built-in variables, such as
chart.bg_color
also use the “input” qualifier, even though input.*()
functions do
not return them, since the script receives their values at input time.
The following script plots the value of a sourceInput
from the
symbolInput
and timeframeInput
context. The
request.security()
call is valid in this script since its symbol
and timeframe
parameters allow “simple string” arguments, meaning they can also
accept “input string” values because the “input” qualifier is
lower on the hierarchy:
simple
Values qualified as “simple” are available on the first script execution, and they remain consistent across subsequent executions.
Users can explicitly define variables and parameters that accept
“simple” values by including the simple
keyword in their
declaration.
Many built-in variables return “simple” qualified values because they depend on information that a script can only obtain once it starts running on the chart. Additionally, many built-in functions require “simple” arguments that do not change over time. Wherever a script allows “simple” values, it can also accept values qualified as “input” or “const”.
This script highlights the background to warn users that they’re using
a non-standard chart type. It uses the value of
chart.is_standard
to calculate the isNonStandard
variable, then uses that variable’s
value to calculate a warningColor
that also references a “simple”
value. The color
parameter of
bgcolor()
allows a “series color” argument, meaning it can also accept a
“simple color” value since “simple” is lower on the hierarchy:
series
Values qualified as “series” provide the most flexibility in scripts since they can change across executions.
Users can explicitly define variables and parameters that accept
“series” values by including the series
keyword in their
declarations.
Built-in variables such as open, high, low, close, volume, time, and bar_index, and the result from any expression using such built-ins, are qualified as “series”. The result of any function or operation that returns a dynamic value will always be a “series”, as will the results from using the history-referencing operator [] to access historical values. Wherever a script allows “series” values, it will also accept values with any other qualifier, as “series” is the highest qualifier on the hierarchy.
This script displays the
highest
and
lowest
value of a sourceInput
over lengthInput
bars. The values assigned to
the highest
and lowest
variables are of the “series float”
qualified type, as they can change throughout the script’s execution:
Types
Pine Script™ types classify values and determine the functions and operations they’re compatible with. They include:
- The fundamental types: int, float, bool, color, and string
- The special types: plot, hline, line, linefill, box, polyline, label, table, chart.point, array, matrix, and map
- User-defined types (UDTs)
- Enums
- void
Fundamental types refer to the underlying nature of a value, e.g., a value of 1 is of the “int” type, 1.0 is of the “float” type, “AAPL” is of the “string” type, etc. Special types and user-defined types utilize IDs that refer to objects of a specific type. For example, a value of the “label” type contains an ID that acts as a pointer referring to a “label” object. The “void” type refers to the output from a function or method that does not return a usable value.
Pine Script™ can automatically convert values from some types into others. The auto-casting rules are: int → float → bool. See the Type casting section of this page for more information.
In most cases, Pine Script™ can automatically determine a value’s type. However, we can also use type keywords to explicitly specify types for readability and for code that requires explicit definitions (e.g., declaring a variable assigned to na). For example:
int
Values of the “int” type represent integers, i.e., whole numbers without any fractional quantities.
Integer literals are numeric values written in decimal notation. For example:
Built-in variables such as bar_index, time, timenow, dayofmonth, and strategy.wintrades all return values of the “int” type.
float
Values of the “float” type represent floating-point numbers, i.e., numbers that can contain whole and fractional quantities.
Floating-point literals are numeric values written with a .
delimiter.
They may also contain the symbol e
or E
(which means “10 raised to
the power of X”, where X is the number after the e
or E
symbol).
For example:
The internal precision of “float” values in Pine Script™ is 1e-16.
Built-in variables such as close, hlcc4, volume, ta.vwap, and strategy.position_size all return values of the “float” type.
bool
Values of the “bool” type represent the truth value of a comparison or condition, which scripts can use in conditional structures and other expressions.
There are only two literals that represent boolean values:
A bool
variable can never be na, and any conditional
structure that can return na
will return false
instead. For example, an if condition returns bool
values, when the condition is not met and the else
block is not specified, it will returns false
.
Built-in variables such as barstate.isfirst, chart.is_heikinashi, session.ismarket, and timeframe.isdaily all return values of the “bool” type.
color
Color literals have the following format: #RRGGBB
or #RRGGBBAA
. The
letter pairs represent hexadecimal values between 00
and FF
(0 to
255 in decimal) where:
RR
,GG
andBB
pairs respectively represent the values for the color’s red, green and blue components.AA
is an optional value for the color’s opacity (or alpha component) where00
is invisible andFF
opaque. When the literal does not include anAA
pair, the script treats it as fully opaque (the same as usingFF
).- The hexadecimal letters in the literals can be uppercase or lowercase.
These are examples of “color” literals:
Pine Script™ also has
built-in color constants, including
color.green,
color.red,
color.orange,
color.blue
(the default color in plot*()
functions and many of the default
color-related properties in
drawing types), etc.
When using built-in color constants, it is possible to add transparency information to them via the color.new() function.
Note that when specifying red, green or blue components in color.*()
functions, we use “int” or “float” arguments with values between 0
and 255. When specifying transparency, we use a value between 0 and 100,
where 0 means fully opaque and 100 means completely transparent. For
example:
See the User Manual’s page on colors for more information on using colors in scripts.
string
Values of the “string” type represent sequences of letters, numbers, symbols, spaces, and other characters.
String literals in Pine are characters enclosed in single or double quotation marks. For example:
Single and double quotation marks are functionally equivalent in Pine Script™. A “string” enclosed within double quotation marks can contain any number of single quotation marks and vice versa:
Scripts can escape the enclosing delimiter in a “string” using the
backslash character (\
). For example:
We can create “string” values containing the new line escape character
(\n
) for displaying multi-line text with plot*()
and log.*()
functions and objects of
drawing types. For example:
We can use the + operator to concatenate “string” values:
The built-ins in the str.*()
namespace create “string” values using
specialized operations. For instance, this script creates a formatted
string to represent “float” price values and displays the result
using a label:
See our User Manual’s page on Text and shapes for more information about displaying “string” values from a script.
Built-in variables such as syminfo.tickerid, syminfo.currency, and timeframe.period return values of the “string” type.
plot and hline
Pine Script™‘s plot() and hline() functions return IDs that respectively reference instances of the “plot” and “hline” types. These types display calculated values and horizontal levels on the chart, and one can assign their IDs to variables for use with the built-in fill() function.
For example, this script plots two EMAs on the chart and fills the space between them using a fill() call:
It’s important to note that unlike other special types, there is no
plot
or hline
keyword in Pine to explicitly declare a variable’s
type as “plot” or “hline”.
Users can control where their scripts’ plots display via the variables
in the display.*
namespace and a plot*()
function’s force_overlay
parameter. Additionally, one script can use the values from another
script’s plots as external inputs via the
input.source()
function (see our User Manual’s section on
source inputs).
Drawing types
Pine Script™ drawing types allow scripts to create custom drawings on charts. They include the following: line, linefill, box, polyline, label, and table.
Each type also has a namespace containing all the built-ins that create
and manage drawing instances. For example, the following *.new()
constructors create new objects of these types in a script:
line.new(),
linefill.new(),
box.new(),
polyline.new(),
label.new(),
and
table.new().
Each of these functions returns an ID which is a reference that uniquely identifies a drawing object. IDs are always qualified as “series”, meaning their qualified types are “series line”, “series label”, etc. Drawing IDs act like pointers, as each ID references a specific instance of a drawing in all the functions from that drawing’s namespace. For instance, the ID of a line returned by a line.new() call is used later to refer to that specific object once it’s time to delete it with line.delete().
Chart points
Chart points are special types that represent coordinates on the chart. Scripts use the information from chart.point objects to determine the chart locations of lines, boxes, polylines, and labels.
Objects of this type contain three fields: time
, index
, and
price
. Whether a drawing instance uses the time
or price
field
from a
chart.point
as an x-coordinate depends on the drawing’s xloc
property.
We can use any of the following functions to create chart points in a script:
- chart.point.new() -
Creates a new
chart.point
with a specified
time
,index
, andprice
. - chart.point.now() -
Creates a new
chart.point
with a specified
price
y-coordinate. Thetime
andindex
fields contain the time and bar_index of the bar the function executes on. - chart.point_from_index() -
Creates a new
chart.point
with an
index
x-coordinate andprice
y-coordinate. Thetime
field of the resulting instance is na, meaning it will not work with drawing objects that use anxloc
value of xloc.bar_time. - chart.point.from_time() -
Creates a new
chart.point
with a
time
x-coordinate andprice
y-coordinate. Theindex
field of the resulting instance is na, meaning it will not work with drawing objects that use anxloc
value of xloc.bar_index. - chart.point.copy() -
Creates a new
chart.point
containing the same
time
,index
, andprice
information as theid
in the function call.
This example draws lines connecting the previous bar’s
high
to the current bar’s
low on
each chart bar. It also displays labels at both points of each line. The
line and labels get their information from the firstPoint
and
secondPoint
variables, which reference chart points created using
chart.point_from_index()
and
chart.point.now():
Collections
Collections in Pine Script™ (arrays, matrices, and maps) utilize reference IDs, much like other special types (e.g., labels). The type of the ID defines the type of elements the collection will contain. In Pine, we specify array, matrix, and map types by appending a type template to the array, matrix, or map keywords:
array<int>
defines an array containing “int” elements.array<label>
defines an array containing “label” IDs.array<UDT>
defines an array containing IDs referencing objects of a user-defined type (UDT).matrix<float>
defines a matrix containing “float” elements.matrix<UDT>
defines a matrix containing IDs referencing objects of a user-defined type (UDT).map<string, float>
defines a map containing “string” keys and “float” values.map<int, UDT>
defines a map containing “int” keys and IDs of user-defined type (UDT) instances as values.
For example, one can declare an “int” array with a single element value of 10 in any of the following, equivalent ways:
Note that:
- The
int[]
syntax can also specify an array of “int” elements, but its use is discouraged. No equivalent exists to specify the types of matrices or maps in that way. - Type-specific built-ins exist for arrays, such as
array.new_int(),
but the more generic array.new<type>
form is preferred, which would be
array.new<int>()
to create an array of “int” elements.
User-defined types
The type keyword allows the creation of user-defined types (UDTs) from which scripts can create objects. UDTs are composite types; they contain an arbitrary number of fields that can be of any type, including other user-defined types.
The syntax to declare a user-defined type is:
where:
- export is the keyword that a library script uses to export the user-defined type. To learn more about exporting UDTs, see our User Manual’s Libraries page.
<UDT_identifier>
is the name of the user-defined type.<field_type>
is the type of the field.<field_name>
is the name of the field.<value>
is an optional default value for the field, which the script will assign to it when creating new objects of that UDT. If one does not provide a value, the field’s default is na. The same rules as those governing the default values of parameters in function signatures apply to the default values of fields. For example, a UDT’s default values cannot use results from the history-referencing operator [] or expressions.
This example declares a pivotPoint
UDT with an “int” pivotTime
field and a “float” priceLevel
field that will respectively hold
time and price information about a calculated pivot:
User-defined types support type recursion, i.e., the fields of a UDT
can reference objects of the same UDT. Here, we’ve added a nextPivot
field to our previous pivotPoint
type that references another
pivotPoint
instance:
Scripts can use two built-in methods to create and copy UDTs: new()
and copy()
. See our User Manual’s page on
Objects to learn more
about working with UDTs.
Enum types
The enum keyword allows the creation of an enum, otherwise known as an enumeration, enumerated type, or enum type. An enum is a unique type construct containing distinct, named fields representing members (i.e., possible values) of the type. Enums allow programmers to control the values accepted by variables, conditional expressions, and collections, and they facilitate convenient dropdown input creation with the input.enum() function.
The syntax to declare an enum is as follows:
where:
- export is the optional keyword allowing a library to export the enum for use in other scripts. See this section to learn more about exporting enum types.
<enumName>
is the name of the enum type. Scripts can use the enum’s name as the type keyword in variable declarations and type templates.<field_*>
is the name of an enum field, representing a named member (value) of theenumName
type. Each field must have a unique name that does not match the name or title of any other field in the enum. To retrieve an enum member, reference its field name using dot notation syntax (i.e.,enumName.fieldName
).<title_*>
is a “const string” title assigned to a field. If one does not specify a title, the field’s title is the “string” representation of its name. The input.enum() function displays field titles within its dropdown in the script’s “Settings/Inputs” tab. Users can also retrieve a field’s title with the str.tostring() function. As with field names, each field’s title must not match the name or title of any other field in the enum.
This example declares an maChoice
enum. Each field within this
declaration represents a distinct member of the maChoice
enum type:
Note that:
- All the enum’s possible values are available upon the first script execution and do not change across subsequent executions. Hence, they automatically adopt the simple qualifier.
The script below uses the maChoice
enum within an
input.enum()
call to create a dropdown input in the “Settings/Inputs” tab that
displays all the field titles. The maInput
value represents the member
of the enum that corresponds to the user-selected title. The script uses
the selected member within a
switch
structure to determine the built-in moving average it calculates:
See the Enums page and the Enum input section of the Inputs page to learn more about using enums and enum inputs.
void
There is a “void” type in Pine Script™. Functions having only side-effects and returning no usable result return the “void” type. An example of such a function is alert(); it does something (triggers an alert event), but it returns no usable value.
Scripts cannot use “void” results in expressions or assign them to
variables. No void
keyword exists in Pine Script™ since one cannot
declare a variable of the “void” type.
`na` value
There is a special value in Pine Script™ called
na,
which is an acronym for not available. We use
na to
represent an undefined value from a variable or expression. It is
similar to null
in Java and None
in Python.
Scripts can automatically cast na values to almost any type. However, in some cases, the compiler cannot infer the type associated with an na value because more than one type-casting rule may apply. For example:
The above line of code causes a compilation error because the compiler
cannot determine the nature of the myVar
variable, i.e., whether the
variable will reference numeric values for plotting, string values for
setting text in a label, or other values for some other purpose later in
the script’s execution.
To resolve such errors, we must explicitly declare the type associated
with the variable. Suppose the myVar
variable will reference “float”
values in subsequent script iterations. We can resolve the error by
declaring the variable with the
float
keyword:
or by explicitly casting the na value to the “float” type via the float() function:
To test if the value from a variable or expression is
na, we
call the
na()
function, which returns true
if the value is undefined. For example:
Do not use the ==
comparison operator to test for
na
values, as scripts cannot determine the equality of an undefined value:
Best coding practices often involve handling na values to prevent undefined values in calculations.
We can ensure the expression also returns an actionable value on the first bar by replacing the undefined past value with a value from the current bar. This line of code uses the nz() function to replace the past bar’s close with the current bar’s open when the value is na:
Protecting scripts against
na
instances helps to prevent undefined values from propagating in a
calculation’s results. For example, this script declares an
allTimeHigh
variable on the first bar. It then uses the
math.max()
between the allTimeHigh
and the bar’s
high
to update the allTimeHigh
throughout its execution:
This script plots a value of
na on
all bars, as we have not included any
na
protection in the code. To fix the behavior and plot the intended result
(i.e., the all-time high of the chart’s prices), we can use
nz() to
replace
na
values in the allTimeHigh
series:
Type templates
Type templates specify the data types that collections (arrays, matrices, and maps) can contain.
Templates for arrays and
matrices consist of a
single type identifier surrounded by angle brackets, e.g., <int>
,
<label>
, and <PivotPoint>
(where PivotPoint
is a
user-defined type (UDT)).
Templates for maps consist of
two type identifiers enclosed in angle brackets, where the first
specifies the type of keys in each key-value pair, and the second
specifies the value type. For example, <string, float>
is a type
template for a map that holds string
keys and float
values.
Users can construct type templates from:
- Fundamental types: int, float, bool, color, and string
- The following special types: line, linefill, box, polyline, label, table, and chart.point
- User-defined types (UDTs)
- Enum types
Note that:
- Maps can use any of these types as values, but they can only accept fundamental types or enum types as keys.
Scripts use type templates to declare variables that reference collections, and when creating new collection instances. For example:
Type casting
Pine Script™ includes an automatic type-casting mechanism that casts (converts) “int” values to “float” when necessary. Variables or expressions requiring “float” values can also use “int” values because any integer can be represented as a floating point number with its fractional part equal to 0.
It’s sometimes necessary to cast one type to another when auto-casting rules do not suffice. For such cases, the following type-casting functions are available: int(), float(), bool(), color(), string(), line(), linefill(), label(), box(), and table().
The example below shows a code that tries to use a “const float” value
as the length
argument in the
ta.sma()
function call. The script will fail to compile, as it cannot
automatically convert the “float” value to the required “int” type:
The code raises the following error: “Cannot call ‘ta.sma’ with argument ‘length’=‘LENGTH’. An argument of ‘const float’ type was used but a ‘series int’ is expected.”
The compiler is telling us that the code is using a “float” value
where an “int” is required. There is no auto-casting rule to cast a
“float” to an “int”, so we must do the job ourselves. In this
version of the code, we’ve used the
int()
function to explicitly convert our “float” LENGTH
value to the
“int” type within the
ta.sma()
call:
Explicit type casting is also handy when declaring variables assigned to na, as explained in the previous section.
For example, once could explicitly declare a variable with a value of na as a “label” type in either of the following, equivalent ways:
Tuples
A tuple is a comma-separated set of expressions enclosed in brackets. When a function, method, or other local block returns more than one value, scripts return those values in the form of a tuple.
For example, the following user-defined function returns the sum and product of two “float” values:
When we call this function later in the script, we use a tuple declaration to declare multiple variables corresponding to the values returned by the function call:
Keep in mind that unlike declaring single variables, we cannot
explicitly define the types the tuple’s variables (hlSum
and
hlProduct
in this case), will contain. The compiler automatically
infers the types associated with the variables in a tuple.
In the above example, the resulting tuple contains values of the same
type (“float”). However, it’s important to note that tuples can
contain values of multiple types. For example, the chartInfo()
function below returns a tuple containing “int”, “float”, “bool”,
“color”, and “string” values:
Tuples are especially handy for requesting multiple values in one request.security() call.
For instance, this roundedOHLC()
function returns a tuple containing
OHLC values rounded to the nearest prices that are divisible by the
symbol’s minimum
tick
value. We call this function as the expression
argument in
request.security()
to request a tuple containing daily OHLC values:
We can also achieve the same result by directly passing a tuple of
rounded values as the expression
in the
request.security()
call:
Local blocks of conditional structures, including if and switch statements, can return tuples. For example:
and:
However, ternaries cannot contain tuples, as the return values in a ternary statement are not considered local blocks:
Note that all items within a tuple returned from a function are qualified as “simple” or “series”, depending on its contents. If a tuple contains a “series” value, all other elements within the tuple will also adopt the “series” qualifier. For example: