Type system
Introduction
Pine Script® uses a system of types and type qualifiers to categorize the data in a script and indicate where and how the script can use it. This system applies to all values and references in a script, and to the variables, function parameters, and fields that store them.
Types in Pine Script indicate the kinds of information that a script’s data represents. Some types directly represent values, such as numbers, logical conditions, colors, or text, while others define structures for special tasks, such as displaying visuals on the chart. Qualifiers indicate when the values of any given type are accessible, and whether those values can change across script executions.
The combination of a type and a qualifier forms a qualified type, which determines the operations and functions with which a value or reference is compatible.
The type system closely connects to the execution model and its time series structure — together, they determine how a script behaves as it runs on a dataset. Although it’s possible to write simple scripts without understanding these foundational topics, learning about them and their nuances is key to mastering Pine Script.
Qualifiers
Pine’s type qualifiers (const, input, simple, and series) indicate when values in a script are accessible — either at compile time, input time, or runtime — and whether those values can change across script executions:
"const"
"input"
"simple"
"series"
Pine Script uses the following qualifier hierarchy to determine the compatibility of values in a script’s calculations:
const < input < simple < series
In this hierarchy, “const” is the lowest (weakest) qualifier, and “series” is the highest (strongest). Any variable, parameter, or operation that accepts a value with a specific qualifier also allows a value of the same type with a weaker qualifier, but not one that is stronger.
For instance, a function parameter that accepts a value of the “simple int” qualified type also allows a value of the “input int” or “const int” type, because “const” and “input” are lower than “simple” in the qualifier hierarchy. However, the parameter cannot accept a “series int” value, because “series” is higher in the hierarchy than “simple”.
Pine also uses this hierarchy to determine the qualifiers assigned to the results of expressions, i.e., function calls and operations. The returned types of an expression always inherit the strongest qualifier in the calculation. For example, an expression that performs a calculation using “input” and “simple” values returns “simple” results, because “simple” is a stronger qualifier than “input”.
Note that a script cannot change the qualifier of a returned value to one that is lower in the hierarchy to make it compatible with specific operations or functions. For instance, if a calculation returns a value qualified as “series”, the script cannot modify that value’s qualifier later to enable using it in code that requires “simple” or “const” values.
The following sections explain the behavior of each type qualifier, as well as the built-in keywords that programmers can use to specify qualifiers in their code.
const
Values qualified as “const” are available at compile time, before the script starts its first execution. Compilation occurs when the user saves the script in the Pine Editor, and immediately before a script starts to run on the chart or in another location. Values with the “const” qualifier remain constant after compilation; they do not change during any script execution. All literal values and the results of expressions that use only values qualified as “const” automatically inherit the “const” qualifier.
The following list shows a few values of each fundamental type. All of these represent literal values if a script includes them directly in its source code:
- 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"'
Scripts can declare variables that hold “const” values, and use those variables to calculate other constants. In the example below, we use “const” variables to set the title of a script and its plots. The script compiles successfully, because the indicator() and plot() calls used in the code both require a title
argument of the “const string” qualified type:
Note that:
- All the variables above the indicator() call in this script have the “const” qualifier, because they hold a literal value or the result of operations that use only “const” values.
- All our “const” variables in this example have names in uppercase snake case so that they are easy to distinguish in the code, as recommended by our Style guide.
- Although the “const” variables in this script hold constant values, the script initializes them on every bar. The only exception is
PLOT1_TITLE
, which the script initializes only on the first bar, because its declaration includes the var keyword. See the Declaration modes section of the Variable declarations page to learn more.
Any variable or function parameter that requires a “const” value cannot accept a value with the “input”, “simple”, or “series” qualifier, because “const” is the lowest qualifier in the qualifier hierarchy.
For example, the following script combines a literal string with the value of syminfo.ticker to set the value of a scriptTitle
variable. Then, it attempts to use the variable as the title
argument of the indicator() declaration statement, causing a compilation error. The title
parameter requires a “const string” argument, but scriptTitle
holds a value of the type “simple string”:
Note that:
- The syminfo.ticker variable holds a “simple string” value because it depends on data that is available only at runtime. Combining this value with a literal string produces another “simple string” value, because “simple” is a stronger qualifier than “const”.
- We did not name the
scriptTitle
variable using snake case, because our Style guide recommends using lower camel case to name variables that do not hold “const” values.
Programmers can restrict the behavior of a variable and force constant assignments on each execution by prefixing its declaration with the const keyword, followed by a type keyword or identifier. If a variable includes const in its declaration, the script cannot change its value with the reassignment or compound assignment operators. This restriction applies even if the new assigned value is still a constant.
For example, the script below declares a myVar
variable using the const keyword. Then, it attempts to change the variable’s value with the += operator, causing a compilation error:
For a variable of any value type, applying the const keyword to the declaration prevents the script from assigning a value qualified as “input”, “simple”, or “series”. Likewise, if a user-defined function parameter of these types includes the keyword in its declaration, it accepts only “const” values.
The following script attempts to use the value of the close variable as the initial value of a myVar
variable declared using the const keyword. However, close is not compatible with the variable, so a compilation error occurs. The value of close is of the type “series float”, because it updates from bar to bar, but the myVar
variable requires a “const float” value:
Note that:
- If we remove the const keyword from the variable declaration, the
myVar
variable automatically inherits the “series” qualifier, and no error occurs.
input
Values qualified as “input” are established at input time. They are similar to “const” values, because they are available before the first script execution and never change during runtime. However, unlike “const” values, “input” values depend on user input.
All function parameters that have the “input” qualifier can accept only “input” or “const” values; they do not allow values qualified as “simple” or “series”.
Most of the built-in input.*()
functions return values qualified as “input”. These functions create adjustable inputs in the script’s “Settings/Inputs” tab, enabling users to change specific values in a script without altering its source code. Each time the user changes the value of an input, the script reloads across all bars on the chart — from the first available bar to the most recent bar — to update its results using the specified value, as explained in the Execution model page.
The following script requests the value of an RSI calculated on the dataset for a specific symbol and timeframe, and then plots the result on the chart as columns. The script includes two string inputs that specify the context of the request, and it uses a float input value to set the base of the plotted columns. If the user changes any of these inputs in the “Settings/Inputs” tab, the script reloads to update its results for every bar:
Note that:
- The plot() function’s
histbase
parameter, which sets the base of the plotted columns, has the expected type “input int” or “input float”. As such, it cannot accept “simple int/float” or “series int/float” values, because “simple” and “series” are stronger qualifiers than “input”. - The request.security() function requests data from a specified dataset. Its
symbol
andtimeframe
parameters, which define the context of the request, accept “series string” values by default. Therefore, these parameters also accept “input string” values. See the Other timeframes and data page to learn more aboutrequest.*()
functions.
Some built-in chart.*
variables also hold “input” values, because these variables update at input time based on changes to the chart. Scripts that use these variables reload, executing across the entire dataset again, if any chart changes affect their values.
The example below uses some of these variables to display a gradient background color that incrementally changes over the chart’s visible bars. It uses chart.left_visible_bar_time and chart.right_visible_bar_time to get the timestamps of the leftmost and rightmost visible bars for its calculation, and it uses chart.bg_color and chart.fg_color to define the start and end colors of the gradient. If the user scrolls or zooms on the chart, or changes the chart’s background color, the script reloads to generate new results:
simple
Values qualified as “simple” are established at runtime, while the script executes on the first available bar. Similar to values qualified as “input” or “const”, “simple” values do not change across bars.
All variables and function parameters that have the “simple” qualifier can accept only “simple”, “input”, or “const” values; they do not allow values qualified as “series”.
Many built-in variables, such as most syminfo.*
and timeframe.*
variables, hold “simple” values instead of “const” or “input” because they depend on information that a script can obtain only after it starts running on a dataset. Likewise, various built-in function parameters require values with the “simple” qualifier or a weaker one.
The following script uses request.security() with a calc_bars_count
argument to retrieve a limited history of daily close values. It determines the number of historical days in the request based on the “simple string” value of syminfo.type. For cryptocurrency symbols, the call requests 14 days of historical data. For other symbols, it requests 10 days of data. The script compiles successfully because the reqDays
variable holds the type “simple int”, which matches the expected type for the calc_bars_count
parameter:
Programmers can explicitly define variables and parameters that require “simple” values, or values with a weaker qualifier, by prefixing their declaration with the simple keyword, followed by a type keyword. Variables declared with this keyword can hold runtime-calculated values that do not change across bars. These variables cannot accept values qualified as “series”, even if those values remain consistent on every bar.
The script below attempts to assign the result of a math.random() call to a rand
variable declared with the simple keyword, causing a compilation error. The math.random() function returns a different value on each call, meaning its return type is “series float”. However, the simple keyword forces the rand
variable to require a “simple float”, “input float”, or “const float” value:
series
Values qualified as “series” provide the most flexibility in a script’s calculations. These values are available at runtime, and they are the only values that can change from bar to bar.
All variables and function parameters that accept a “series” value also allow values with any other qualifier, because “series” is the highest qualifier in the qualifier hierarchy.
All built-in variables that store bar information — such as open, high, low, close, volume, time, bar_index, and barstate.isconfirmed — always hold “series” values. The same applies to variables that store data from internal calculations that update from bar to bar, such as ta.vwap and ta.pvi.
If an expression’s result can vary on any execution, it automatically inherits the “series” qualifier. Similarly, even if an expression returns an unchanging result on every bar, that result is still qualified as “series” if the calculation depends on at least one “series” value.
The following script calculates highest and lowest values from a sourceInput
series and a “const float” value over lengthInput
bars. The highest
and lowest
variables automatically inherit the “series” qualifier because the ta.highest() and ta.lowest() functions always return “series” results. These functions never return a value with a weaker qualifier, even if they calculate on a constant, because their source
parameter is of the type “series float”:
Programmers can use the series keyword to explicitly define variables and parameters that accept “series” values. A script cannot use a variable declared using this keyword in any part of the code that requires “simple” or weaker qualifiers, even if the variable’s assigned value never changes.
For example, the script below declares a lengthInput
variable with the series keyword. Then, it attempts to use the variable as the length
argument of a ta.ema() function call, causing a compilation error. Although the variable’s value comes from an integer input, the series keyword causes its type to become “series int” instead of “input int”. This type is not compatible with the ta.ema() function’s length
parameter, because the strongest qualified type that the parameter accepts is “simple int”:
Types
Types define the categories of values in a script and determine the kinds of functions and operations with which those values are compatible. Each type represents a different kind of data. The primary types available in Pine Script consist of the following:
- Fundamental types: int, float, bool, color, and string
- Enum types (enums)
- Special types: plot, hline, line, linefill, box, polyline, label, table, chart.point, array, matrix, and map
- User-defined types (UDTs)
- void
Fundamental types and enum types are also known as value types. Variables of these types directly hold values. Additionally, value types can inherit any type qualifier, depending on their use in the code. By contrast, special types and UDTs are reference types. Variables of these types do not store direct values; they hold references (sometimes referred to as IDs) that provide access to data stored elsewhere in memory. Instances of these types always inherit the “series” qualifier, regardless of how the script uses them.
Programmers can explicitly define the type of a variable, function parameter, or field by prefixing its declaration with a type keyword (e.g., int) or a type identifier (e.g., array<int>
). Specifying types in code is usually optional, because the compiler can automatically determine type information in most cases. However, type specification is required when:
- Declaring variables, user-defined function parameters, or UDT fields with initial
na
values. - Defining the parameters of exported library functions, or declaring exported constants.
- Using qualifier keywords in a variable or parameter declaration.
- Declaring the first parameter of a user-defined method.
The example below calculates a moving average and detects when the close series crosses over the value. The script uses values of different fundamental types in its calculations. It includes the int, float, bool, color, and string keywords in its variable declarations to specify which type each variable accepts:
Note that:
- The first five variables in this script do not require type keywords in their declarations, but including them helps promote readability. However, the
crossValue
variable does require a specified type in its declaration because its initial value is na.
The sections below explain the different types available in Pine Script and how they work.
Value types
The types covered in the following sections are value types. These types directly represent values, such as numbers, logical conditions, colors, or text sequences. Value types are compatible with any type qualifier, depending on their use in the code. Additionally, value types, unlike reference types, are compatible with arithmetic and logical operators.
int
Values of the “int” type represent integers: whole numbers without fractional parts.
Literal integers in a script are sequences of decimal digits without a decimal point (.
). These literals can also include the unary + or - operators at the beginning of the sequence to specify their sign (positive or negative).
Below are a few examples of literal integers:
Many built-in variables hold “int” values, including bar_index, time, timenow, dayofmonth, and strategy.wintrades.
float
Values of the “float” type represent floating-point numbers. In contrast to “int” values, “float” values represent the whole and fractional parts of a number.
Literal floating-point values in Pine have two different formats:
- A sequence of decimal digits that contains a decimal point (
.
) to separate the number’s whole and fractional parts. This format can include a unary + or - operator at the beginning to specify the number’s sign. - A number, with an optional decimal point, followed by
e
orE
and an additional whole number. The number before and aftere
orE
can include the unary + or - operator. This format represents a floating-point number in E notation. It translates to “X multiplied by 10 raised to the power of Y”, where “X” is the number beforee
orE
, and “Y” is the number that follows. This format provides a compact way to represent very large or very small values.
Below are a few examples of floating-point literals:
The internal precision of “float” values in Pine Script is 1e-16. Floating-point values in Pine cannot precisely represent numbers with more than 16 fractional digits. However, note that comparison operators automatically round “float” operands to nine fractional digits.
Many built-in variables store “float” values, including close, hlcc4, volume, ta.vwap, and strategy.position_size.
bool
Values of the “bool” type represent the Boolean truth values of conditions (true or false). Scripts use these values in conditional structures and expressions to trigger specific calculations in the code. All comparison and logical operators return “bool” values.
There are only two possible “bool” literals in Pine Script:
In contrast to most other types, values of the “bool” type are never na. Any expression or structure with the “bool” return type returns false
instead of na if data is not available.
For example, if a script uses the history-referencing operator to retrieve the value of a “bool” variable from a previous bar that does not exist, that operation returns false
. Likewise, an if statement with a return expression of the “bool” type returns false
if none of its local blocks activate. By contrast, expressions and structures with other return types, excluding void, return na if there is no available data.
All built-in variables that represent conditions store “bool” values, including barstate.isfirst, chart.is_heikinashi, session.ismarket, and timeframe.isdaily.
color
Values of the “color” type represent RGB colors, which scripts use to define the colors of chart visuals. Color literals in Pine have the format #RRGGBB
or #RRGGBBAA
, where:
- Each symbol after the number sign (
#
) represents a hexadecimal digit, which is a numeral from0
to9
or a letter fromA
(for 10) toF
(for 15). Each set of two digits represents one of the color’s component values, ranging from 0 (00
) to 255 (FF
). - The
RR
,GG
, andBB
parts represent the color’s red, green, and blue components, respectively. The last pair of digits,AA
, is optional; it specifies the color’s opacity (alpha). If the pair is00
, the color is transparent. IfFF
or not specified, the color is fully opaque. - All letters in the literal value can be uppercase or lowercase.
Below are several examples of literal “color” values:
Pine Script also includes several built-in color constants, such as color.green, color.orange, color.red, and color.blue. Note that color.blue is the default color for plots, and it is the default value for several color properties of drawing types.
The color
namespace contains functions for retrieving color components, modifying colors, and creating new colors. For instance, scripts can use color.new() to define a copy of a built-in color with different transparency, or use color.rgb() to create a new color with specific red, green, blue, and transparency components.
Note that the red
, green
, and blue
parameters of the color.rgb() function expect a number from 0 to 255, where 0 means no intensity and 255 means maximum intensity. The transp
parameter of color.rgb() and color.new() expects a value from 0 to 100, where 0 means fully opaque and 100 means completely transparent. Both functions automatically clamp arguments to these ranges, and they round the specified values to whole numbers.
The example below creates a new “color” value with color.rgb(), modifies the color’s transparency based on the current day of the week with color.new(), and then displays the resulting color in the chart’s background:
Note that:
- The value stored by
BASE_COLOR
is of the type “const color” because it depends on only “const” values. However, the modified color returned by color.new() is of the type “series color”, because the dayofweek variable used in the calculation has the “series” qualifier.
To learn more about working with colors in Pine, see the Colors page.
string
Values of the “string” type contain sequences of encoded characters representing text, including letters, digits, symbols, spaces, or other Unicode characters. Scripts use strings in many ways, such as to define titles, express symbols and timeframes, create alerts and debug messages, and display text on the chart.
Literal strings in Pine Script are sequences of characters enclosed by two ASCII quotation marks ("
) or apostrophes ('
). For example:
Quotation marks and apostrophes are functionally similar when used as the enclosing delimiters of literal strings. A string enclosed in quotation marks can contain any number of apostrophes. Likewise, a string enclosed in apostrophes can contain any number of quotation marks. For example:
A literal string can prefix some characters with the backslash character (\
) to change their meaning. For example, applying a backslash to a quotation mark or apostrophe adds that character directly into a literal string’s sequence instead of treating the character as the end of the string:
Applying a backslash to the n
or t
characters in a literal string creates escape sequences for multiline text or indentation respectively, which scripts can render using plot*()
functions, Pine Logs, or some drawing types. For example, this string represents multiline text with a single word per line:
Scripts can use two operators, + and +=, to concatenate (combine) two separate strings. These operators create a new string containing the first operand’s character sequence followed by the second operand’s sequence. For example:
The str
namespace contains several built-in functions that perform string-based calculations or create new strings. For example, the script below calls str.format() on each bar to create a formatted string containing representations of “float” price values, and it displays the result as multiline text in a label positioned at the bar’s high value:
Several built-in variables that contain symbol and timeframe information store “string” values, e.g., syminfo.tickerid, syminfo.currency, and timeframe.period.
For detailed information about Pine strings and the built-in str.*()
functions, refer to the Strings page. To learn more about displaying text from strings, see the Text and shapes and Debugging pages.
Enum types
The enum keyword enables the creation of an enum, otherwise known as an enumeration, enumerated type, or enum type. An enum is a unique type that contains distinct named fields. These fields represent the members (i.e., possible values) of the enum type. Programmers can use enums to maintain strict control over the values accepted by variables, parameters, conditional expressions, collections, and the fields of UDT objects. Additionally, scripts can use the input.enum() function to create enum-based dropdown inputs in the “Settings/Inputs” tab.
The syntax to declare an enum is as follows:
[export ]enum <enumName> <field_1>[ = <title_1>] <field_2>[ = <title_2>] ... <field_N>[ = <title_N>]
Where:
- export is the optional keyword for exporting the enum from a library, enabling its use in other scripts. See the Enum types section of the Libraries page to learn more about exporting enums.
enumName
is the name of the enum type. Scripts can use the enum’s name as the type keyword in variable declarations, parameter and field declarations, and the type templates of collections.field_*
is the name of an enum field. The field represents 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 member in the enum. To retrieve an enum member, use dot notation syntax on the enum’s name (e.g.,enumName.field_1
).title_*
is a “const string” value representing the title of an enum member. If the enum declaration does not specify a member’s title, its title is the “string” representation of its name. The input.enum() function displays enum member titles within a dropdown input in the “Settings/Inputs” tab. To retrieve the “string” title of an enum member, use the str.tostring() function on that member (e.g.,str.tostring(enumName.field_1)
). As with member names, each enum member’s title must be unique; it cannot match the name or title of another member in the same enum.
The following code block declares an enum named maChoice
. Each field within the declaration represents a unique, constant member of the maChoice
enum type with a distinct title:
The following script uses the input.enum() function to create a dropdown input from our maChoice
enum in the “Settings/Inputs” tab. The dropdown displays each field’s title as a possible choice. The value of maInput
is the maChoice
member corresponding to the selected title. The script compares the maChoice
value inside a switch structure to determine which ta.*()
function it uses to calculate a moving average:
See the Enums page and the Enum input section of the Inputs page to learn more about using enums and enum inputs.
Reference types
All the types covered in the following sections are reference types. These types do not directly represent values. Instead, scripts use them to create objects: logical entities that store data in a distinct location. Variables of reference types hold references, also known as IDs, that identify objects in memory and enable access to their data.
In contrast to value types, which support any type qualifier, instances of a reference type automatically inherit the “series” qualifier, because each instance is unique. Additionally, because reference types do not represent values, they are not compatible with any arithmetic or logical operators.
For advanced information about how these types differ from value types, see the Value vs. reference types section at the bottom of the page.
plot and hline
Pine Script uses the “plot” and “hline” types to display plots and horizontal levels on the chart. The plot() and hline() functions create instances of these types. Each call to these functions returns a reference (ID) to a specific “plot” or “hline” instance. Scripts can assign the references returned by these functions to variables for use with the fill() function, which colors the space between two displayed plots or levels.
The following example calculates two EMAs, and then uses two plot() calls to display their values on the chart. It assigns the “plot” IDs returned by the function calls to variables, then uses those variables in a call to fill() to color the visual space between the displayed plots:
Note that:
- Pine does not include type keywords for specifying variables of the “plot” or “hline” type. Variables of these types never hold na, so Pine can always determine their type information automatically.
- A single fill() function call cannot use both a “plot” and “hline” ID. The function requires two IDs of the same type.
In addition to displaying the complete history of “series” values on the chart, “plot” objects enable indicator-on-indicator functionality. Scripts can access values from another script’s plots for their calculations by using the input.source() function. See the Source input section of the Inputs page to learn more.
Drawing types
Pine’s drawing types serve as structures for creating drawing objects, which scripts use to display custom chart visuals. The available drawing types are line, linefill, box, polyline, label, and table.
Each drawing type has an associated namespace with the same name. This namespace contains all the available built-ins for creating and managing drawing objects. For example, the label
namespace contains all the built-in functions and variables for creating and managing labels. To create new instances of any drawing type, scripts can use the following *.new()
functions from each type’s namespace: line.new(), linefill.new(), box.new(), polyline.new(), label.new(), and table.new().
Each of these *.new()
functions creates a new drawing object on every call, and it returns the ID (reference) of that specific object. The other functions in the type’s namespace require this ID to access and delete, copy, or modify the drawing. For example, a script can use the ID returned by line.new() later to delete the underlying line object with line.delete(), copy the object with line.copy(), or update the drawing’s color with line.set_color().
For detailed information about lines, boxes, and polylines, see the Lines and boxes page. To learn more about tables and labels, see the Tables page and the Labels section of the Text and shapes page.
Chart points
The chart.point type is a special type that scripts use to generate chart points. Chart points are objects that contain chart coordinates. Scripts use information from these objects to position lines, boxes, polylines, and labels on the chart.
Objects of the chart.point type contain three fields: time
, index
, and price
. The time
and index
fields both represent horizontal locations (x-coordinates). The price
field represents the vertical location (y-coordinate). Whether a drawing instance uses the time
or index
field from a chart point as an x-coordinate depends on the drawing’s xloc
property. By default, drawings use the index
field from a chart point and ignore the time
field.
Multiple functions in the chart.point
namespace create chart points:
- The chart.point.new() function creates a new chart point containing specified
time
,index
, andprice
values. - The chart.point.now() function creates a chart point with a specified
price
value. The object’stime
andindex
field automatically contain the time and bar_index values from the bar on which the function call occurs. - The chart.point.from_index() function creates a chart point with only specified
price
andindex
values. Thetime
field of the created object is na. Therefore, all chart points from this function are intended for use with drawings whosexloc
property is xloc.bar_index. - The chart.point.from_time() function creates a chart point with only specified
price
andtime
values. Theindex
field of the created object is na. Therefore, all chart points from this function are intended for use with drawings whosexloc
property is xloc.bar_time. - The chart.point.copy() function creates a new chart point with the same
time
,index
, andprice
values as the one referenced by the specifiedid
argument.
The following script draws a new line from the previous bar’s high value to the current bar’s low value on each execution. It also displays labels at both points of the line. The script sets the coordinates of the line and label drawings using data from chart points created by the chart.point.from_index() and chart.point.now() functions:
Refer to the Lines and boxes page for additional examples of using chart points.
Collections
Pine Script collections (arrays, matrices, and maps) are objects that store values or the IDs (references) of other objects as elements. Collection types enable scripts to group multiple values or IDs in a single location and perform advanced calculations. Arrays and matrices contain elements of one specific type. Maps can contain data of two types: one type for the keys, and another for the corresponding value elements. The array
, matrix
, and map
namespaces include all the built-in functions for creating and managing collections.
A collection’s type identifier consists of two parts: a keyword defining the collection’s category (array, matrix, or map), and a type template specifying the types of elements that the collection stores. The type template for array or matrix types consists of a single type keyword enclosed in angle brackets (e.g., <int>
for a collection of “int” values). The type template for a map type consists of two comma-separated keywords surrounded by angle brackets (e.g., <string, int>
for a map of “string” keys and “int” values).
Below, we list some examples of collection type identifiers and the types that they represent:
array<int>
— an array type for storing “int” values.array<label>
— an array type for storing label IDs.array<myUDT>
— an array type for storing references to objects of amyUDT
user-defined type.matrix<float>
— a matrix type for storing “float” values.matrix<line>
— a matrix type for storing line IDs.map<string, float>
— a map type for storing key-value pairs with “string” keys and “float” value elements.map<int, myUDT>
— a map type for storing “int” values as keys, and references tomyUDT
objects as value elements.
Scripts also use type templates in the *.new*()
functions that create new collections. For example, a call to array.new<int>()
creates an array that stores “int” values, and a call to map.new<int, color>()
creates a map that stores “int” keys and corresponding “color” values.
Programmers can explicitly define variables, parameters, and fields that accept references to objects of specific collection types by using the type identifier as the type keyword in the declaration. The following code snippet declares variables that hold references to collections of the type array<int>
, array<float>
, and matrix<float>
:
Scripts can construct collections and type templates for most available types, including:
- All value types: int, float, bool, color, string, and enum types.
- The following special types: line, linefill, box, polyline, label, table, and chart.point.
- User-defined types (UDTs).
Note that maps can use any of these types as value elements, but they can store only value types as keys. See the Maps page to learn more.
Collections cannot store elements of any of the following types:
- The unique types for specific built-ins, such as “plot_style”, “plot_display”, and “barmerge_gaps”.
- The “plot” or “hline” type.
- Any collection type.
User-defined types
The type keyword enables the creation of user-defined types (UDTs). UDTs are composite types; they can contain an arbitrary number of fields that can be of any supported type, including collection types and other user-defined types. Scripts use UDTs to create custom objects that can store multiple types of data in a single location.
The syntax to declare a user-defined type is as follows:
[export ]type <UDT_identifier> [field_type] <field_name>[ = <value>] ...
Where:
- export is the optional keyword for exporting the UDT from a library, enabling its use in other scripts. See the User-defined types and objects section of the Libraries page to learn more.
UDT_identifier
is the name of the user-defined type.field_type
is a type keyword or identifier, which defines the field’s type.field_name
is the name of the field.value
is an optional default value for the field. Each time that the script creates a new instance of the UDT, it initializes the field with the specified value. If not specified, the field’s default value is na, orfalse
if the field’s type is “bool”. Note that the default value cannot be the result of a function call or any other expression; only a literal value or a compatible built-in variable is allowed.
The following example declares a UDT named pivotPoint
. The type contains two fields for storing pivot data: pivotTime
and priceLevel
. The pivotTime
field is of the type “int”, and priceLevel
is of the type “float”:
User-defined types can contain fields for referencing other UDT objects. Additionally, UDTs support type recursion, meaning a UDT can include fields for referencing objects of the same UDT. Below, we added a nextPivot
field to our pivotPoint
type. Objects of this version of the UDT can store a reference (ID) to a separate object of the same pivotPoint
type in this field:
Every user-defined type includes built-in *.new()
and *.copy()
functions for creating objects or copying existing ones. Both functions construct a new object on every call and return that object’s ID. For example, pivotPoint.new()
creates a new instance of our pivotPoint
type and returns its ID for use in other parts of the script.
To learn more about objects of UDTs and how to use them, see the Objects page.
void
Pine Script includes some built-in functions that produce side effects — such as creating triggers for alerts, generating chart visuals, or modifying collections — without returning any value or reference. The return type of these functions is “void”, which represents the absence of usable data. The “void” type applies to every function that performs actions without returning anything that the script can use elsewhere in the code.
For example, plotshape() performs an action (plots shapes on the chart), but it does not return a usable ID like the plot() function does. Therefore, its return type is “void”. Another example is the alert() function. The function creates an alert trigger without returning any data that the script can use elsewhere, so it also has the “void” return type.
Because “void” represents the absence of usable data, scripts cannot call functions that return “void” in other calculations or assign their results to variables. Additionally, there is no available keyword to specify that an expression returns the “void” type.
na
value
Pine Script includes a special value called na, which is an abbreviation for “not available”. Scripts use na to represent an undefined value or reference. It is similar to null
in Java or NONE
in Python.
Pine can automatically cast na values to almost any type. The type assigned to an na value depends on how the code uses it. However, in some cases, more than one type might be valid for a piece of code that includes na, and the compiler cannot determine which type to assign in those cases.
For example, this line of code declares a myVar
variable with an initial value of na. This line causes a compilation error, because the type of data the variable might hold later is uncertain. It might store a “float” value for plotting, a “string” value for setting text in a label, or maybe even a reference to a drawing object:
To resolve this error, we must explicitly define the variable’s type in the code. For instance, if the myVar
variable will store “float” values, we can prefix the variable with the float keyword to specify its type as “float”:
Alternatively, we can use the float() function to explicitly cast the na value’s type to “float”, causing the variable to automatically inherit the “float” type:
Scripts can test whether the result from a variable or expression is na by using the na() function. The function returns true
if the value or reference is undefined. Otherwise, it returns false
. For example, the following ternary operation returns 0 if the value of myVar
is na, or close if the value is defined:
It is crucial to note that scripts cannot directly compare values to na, because by definition, na values are undefined. The ==, != operators, and all other comparison operators always return false
if at least one of the operands is a variable with an na value. Therefore, na comparisons can cause unexpected results. Additionally, if a script attempts to use na directly as an operand in any comparison operation, it causes a compilation error. For example:
Best practices often involve replacing occurrences of undefined values in the code to prevent them from propagating in a script’s calculations. There are three ways to replace na values with defined values in a script’s calculations, depending on the type:
- For the “int”, “float”, and “color” types, scripts can use the nz() function to replace na values with a type-specific default value (
0
for “int”,0.0
for “float”, or#00000000
for “color”) or a specified replacement. - Alternatively, scripts can use the fixnan() function to replace na values of the above types in a series with the latest non-na value from that series’ history.
- For other types such as “string”, scripts can test for an undefined value using the na() function and replace it if the function returns
true
.
The following line of code uses the nz() function to replace the value of close[1]
with the current bar’s open value if the expression’s result is na. This logic prevents the code from returning na on the first bar, where there is no previous close value for the [] operator to access:
Replacing na values to avoid unintended results is especially helpful when a calculation involves data that can persist across bars.
For example, the script below declares a global allTimeHigh
variable using the var keyword, meaning the variable is initialized only on the first bar and persists on all subsequent bars. On each bar, the script updates the variable with the result of a math.max() call that returns the maximum between the current allTimeHigh
and high values, then plots the result.
This script plots na instead of the chart’s all-time high on every bar. The allTimeHigh
variable has an initial value of na, and the math.max() function cannot compare na to the current value of high. Therefore, the function call consistently returns na:
To fix this script’s behavior and enable it to calculate the chart’s all-time high as intended, we must stop the script from passing an na value to the math.max() call. In the version below, we included the expression nz(allTimeHigh, high)
as the first argument in the function call. Now, on any execution where the allTimeHigh
value is na, the script replaces it with the value of high, preventing na values from persisting in the calculation:
Note that:
- An alternative way to fix this script’s behavior is to initialize the
allTimeHigh
variable using the value of high. The fix works in this case because the script does not use na later in its calculations.
Type casting
Pine Script can convert (cast) values of one type to another type either by using specific functions, or automatically.
The automatic type-casting process can cast “int” values to the “float” type when necessary. All variables, function parameters, fields, and expressions that allow the “float” type can also accept “int” values, because any integer is equivalent to a floating-point number with its fractional part set to 0. If a script assigns an “int” value to a variable, function parameter, or field declared with the float keyword, the assigned value’s type automatically changes to “float”. Likewise, Pine converts “int” values to “float” in arithmetic or comparison operations that include a “float” operand.
For example, the following line of code uses the addition operator + with “int” and “float” operands. Pine automatically casts the “int” value to the “float” type before performing the addition operation, and thus the expression returns a “float” result:
Sometimes, a script must cast data of one type to another. Scripts can cast na
values, or numeric values, to specific types by using the following type-casting functions: int(), float(), bool(), color(), string(), line(), linefill(), label(), box(), and table().
For example, the script below declares a LENGTH
variable of the “const float” type, then attempts to use that variable as the length
argument of a ta.sma() function call:
The above code causes the following compilation error:
Cannot call `ta.sma()` with the argument `length = LENGTH`. An argument of "const float" type was used but a "series int" is expected.
This error tells us that the code uses a “float” value where an “int” value is required. There is no automatic rule to cast “float” to “int”, so we must resolve the error manually. In this version of the code, we used the int() function to cast the “float” value of the LENGTH
variable to the “int” type in the ta.sma() call. Now, the script compiles successfully:
Note that:
- The int() function removes all fractional information from a “float” value without rounding. For instance, a call such as
int(10.5)
returns a value of 10, not 11. To round a “float” value to the nearest whole number before converting it to “int”, use math.round().
For most available types, explicit type casting is useful when defining variables that have initial na values or references, as explained in the previous section, na
value.
For example, a script can declare a variable that holds an na reference of the label type in either of the following, equivalent ways:
Tuples
A tuple is a comma-separated list of expressions enclosed in square brackets (e.g., [expr1, expr2, expr3]
). If a structure that creates a local scope, such as a function, method, conditional structure, or loop, returns more than one value, the code lists those values in the form of a tuple.
For example, the following user-defined function returns a tuple containing two values. The first item in the tuple is the sum of the function’s a
and b
arguments, and the second is the product of those two values:
When calling this function later in the code, the script must use a tuple declaration containing one new variable for each value returned by the function to use its data. For example, the hlSum
and hlProduct
variables in the following tuple declaration hold the sum
and product
values returned by a calcSumAndProduct()
call:
Note that:
- In contrast to individual variable declarations, tuple declarations do not support type keywords. The compiler automatically determines the type of each variable in a declared tuple.
If a script’s calculations do not require all the values returned by a function or structure, programmers can use an underscore as the identifier for one or more returned items in the tuple declaration. If a variable’s identifier is an underscore, that variable is not usable elsewhere in the code, such as in comparisons or arithmetic expressions.
For example, if we do not require the product
value returned by our calcSumAndProduct()
function, we can replace the hlProduct
variable with _
in our declared tuple:
In the above examples, the resulting tuple contains two items of the same type (“float”). However, Pine does not restrict tuples to only one type; a single tuple can contain multiple items of different types. For example, the custom chartInfo()
function below returns a five-item tuple containing “int”, “float”, “bool”, “color”, and “string” values:
Scripts can also pass tuples to the expression
parameter of request.*()
functions, enabling them to retrieve multiple series from a single function call. A single call to request.security() or another request.*()
function that requests a tuple of data still counts as one data request, not multiple. See the Other timeframes and data page to learn more about request.*()
functions and the data that they can retrieve.
The following code snippet defines a roundedOHLC()
function that returns a tuple of OHLC prices rounded to the nearest values that are divisible by the symbol’s minimum tick size. We use this function as the expression
argument in a request.security() call to retrieve a tuple containing the symbol’s rounded price values on the “1D” timeframe:
An alternative way to perform the same request is to pass the tuple of rounded values directly to the expression
parameter of the request.security() call. For example:
Note that:
- Only the
request.*()
functions that have anexpression
parameter and theinput.*()
functions that include anoptions
parameter support this argument format. No other functions can use tuples as arguments.
Conditional structures and loops can use tuples as their return expressions, enabling them to return multiple values at once after the script exits their scopes. For example, the if statement below returns a two-item tuple from one of its local blocks:
The following switch statement is equivalent to the above if statement:
It’s crucial to emphasize that only the local scopes of functions, conditional structures, or loops can return tuples. In contrast to if and switch statements, ternary operations are not conditional structures; they are expressions that do not create local scopes. Therefore, they cannot return tuples.
For example, this line of code attempts to return a tuple from a ternary operation, causing a compilation error:
Although all items in a tuple do not have to be of the same type, it’s important to note that every item inherits the same type qualifier. All items within a tuple returned by a local scope inherit either the “simple” or “series” qualifier, depending on the structure and the items’ types. Therefore, because “series” is the stronger qualifier, all other items in the returned tuple automatically inherit the “series” qualifier if at least one item is qualified as “series”.
For example, the script below defines a getParameters()
function that returns a two-item tuple. The script attempts to use the values returned by the function as arguments in a ta.ema() function call, causing a compilation error. The ta.ema() function requires a length
argument of the type “simple int”, but the len
variable’s type is “series int”. The value assigned to len
automatically inherits the “series” qualifier because the source
argument of the getParameters()
call is of the type “series float”:
Value vs. reference types
Every type in Pine Script, excluding void, is either a value type or a reference type.
All fundamental types, enum types, and the unique types for specific function parameters are value types. These types directly represent values, which scripts can use in arithmetic, comparison, or logical operations. Variables of these types store values. Likewise, expressions that return these types return values. Values can become available at compile time, input time, or runtime. Therefore, they can inherit any type qualifier, depending on their use in the code.
By contrast, user-defined types (UDTs) and special types — including label, line, linefill, polyline, box, table, chart.point, and collection types — are reference types. These types serve as structures for creating objects. An object is not a value; it is a logical entity that stores data in a distinct memory location. Each separate object has a unique associated reference, similar to a pointer, which identifies the object in memory and enables the script to access its data. Variables of reference types hold these object references; they do not store objects directly.
Scripts create objects exclusively at runtime, using the available constructor functions from the type’s namespace (e.g., label.new()). Every call to these functions creates a new object with a unique reference. Therefore, unlike value types, reference types automatically inherit the “series” qualifier; they never inherit weaker qualifiers such as “simple” or “const”.
For example, the following script declares a myLabel
variable and assigns it the result of a label.new() function call with constant x
and y
arguments on the first bar. Although the script calls label.new() only once, with “const” arguments, the variable’s type is “series label”. The type is not “const label”, because every call to the function returns a new, unique label reference, which no other call can reproduce:
Note that:
- The script creates a label only on the first bar because the variable that stores its reference is declared in the global scope using the var keyword. See the Declaration modes section of the Variable declarations page to learn more.
Modifying variables vs. objects
Each variable of a value type holds an independent value, and the only way to modify that variable’s data is by using the reassignment or compound assignment operators. Each use of these operators directly overwrites the stored value, thus removing it from the current execution.
Scripts can also modify variables of reference types with the := operator, but not a compound assignment operator such as +=, because object references are not compatible with arithmetic or logical operations. However, it’s crucial to note that reassigning a variable of a reference type does not directly affect any object; it only assigns another reference to that variable. The object referenced before the operation can remain in memory and affect the script’s results, depending on the type and the script’s logic.
To understand this distinction, consider the following script, which uses a variable to store label references on the last historical bar. First, the script initializes the myLabel
variable with the result of one label.new() call. Then, it uses the := operator to assign the variable the result of a second label.new() call. Reassigning the myLabel
variable only changes the variable’s stored reference; it does not overwrite the first label object. The first label still exists in memory. Consequently, this script displays two separate drawings:
Note that:
- Objects remain in memory until a script no longer uses them. For drawing types, the runtime system automatically maintains a limited number of active objects. It begins deleting those objects, starting with the oldest ones, only if a script reaches its drawing limit (e.g., ~50 labels by default).
- A script can also explicitly delete objects of drawing types by using the built-in
*.delete()
functions, such as label.delete(). For example, if we add the calllabel.delete(myLabel)
before the final line in the code above, the script removes the first label before assigning the second label’s reference to themyLabel
variable.
Because objects are not values, but entities that store data separately, scripts do not modify their data by reassigning the variables that reference them. To access or modify an object’s data, programmers must do either of the following, depending on the type:
- Use the built-in getter and setter functions available for most special types. For example, label.get_x() retrieves the
x
value from a label object, and label.set_x() updates a label’sx
value. - Use dot notation syntax on a variable of a UDT or the chart.point type to access the object’s field. Then, to change the field’s assigned data, use a reassignment or compound assignment operator after the syntax. For example,
myObj.price
retrieves theprice
field of the object referenced by themyObj
variable, andmyObj.price := 10
sets that field’s value to 10.
The example below creates a chart point and a label instance on the first bar, and then modifies the two objects on every bar. With each execution, the script updates the price
(“float”) and index
(“int”) fields of the chart point, then uses its reference in a label.set_point() call to change the label’s coordinates. Lastly, the script uses label.get_y() to get the label’s y
value (“float”), then uses a plot to display the value:
Note that:
- The label.set_point() call in this example uses the
index
field of the chart point to set the label’sx
value, and it uses theprice
field to set they
value. It does not use thetime
field from the chart point for thex
value, because the defaultxloc
property for labels is xloc.bar_index.
Copies vs. shared references
Variables of value types hold values that act as independent copies, because the only way to modify their data is through reassignment. If a script directly assigns one variable’s value to another variable, it can change either variable’s data later without affecting the other variable’s data in any way.
For example, the following script initializes a myVar1
variable with a value of 10, and then initializes a myVar2
variable using myVar1
. Afterward, the script adds 10 to myVar1
with the += operator, and plots the values of both variables on the chart. The script plots two different values (20 and 10), because changes to the value of myVar1
do not affect the data accessed by myVar2
:
The same behavior does not apply to variables of reference types. Assigning the reference stored by one variable to another does not create a new copy of an object. Instead, both variables refer to the same object in memory. As a result, the script can access or change that object’s data through either variable and produce the same results.
The following example demonstrates this behavior. On the last historical bar, the script creates a new label with label.new() and assigns the returned reference to the myLabel1
variable. Then, it initializes the myLabel2
variable using myLabel1
. The script calls label.set_color() to modify the label referenced by myLabel1
, and then calls label.set_style() and label.set_text() to modify the one referenced by myLabel2
.
A newcomer to reference types might expect this script to display two separate labels, with different colors, orientation, and text. However, the script shows only one label on the chart, and that label includes the changes from all label.set_*()
calls. Modifying the label referenced by myLabel2
directly affects the one referenced by myLabel1
, and vice versa, because both variables refer to the same label object:
Most reference types, including user-defined types, feature a built-in *.copy()
function. This function creates a new, independent object that contains the same data as the original object, and that new object has a unique reference. The script can modify the copied object’s data without directly affecting the original.
In the following example, we changed the previous script to initialize myLabel2
using the expression label.copy(myLabel1)
, which creates an independent copy of the label referenced by myLabel1
and returns a new reference. Now, myLabel1
and myLabel2
refer to two separate labels, and changes to the label referenced by one of the variables do not affect the other:
Using const
with reference types
Scripts can use the const keyword when declaring variables of most reference types, except for plot, hline, and user-defined types. However, with reference types, the keyword behaves differently than it does with value types.
Recall that for a variable of a value type, the const keyword directly restricts the qualifier of that variable to “const”, and it prevents the script from using the reassignment or compound assignment operators to modify that variable — even if the assigned value from those operations is otherwise a constant.
For variables of reference types, using the const keyword to declare them also prevents a script from reassigning those variables. However, in contrast to its behavior with value types, the keyword does not set the qualifier of a reference-type variable to “const”. As explained in previous sections, reference types automatically inherit the “series” qualifier, because each call to a function that creates objects produces a new object with a unique reference — any call to the function in the code never returns the same object reference more than once.
For example, the script below creates an array of random “float” values using array.from(), and then assigns the returned reference to a variable declared using the const keyword on each bar. During each execution, the array.from() call creates a new array and returns a unique “series” reference. However, this script does not cause an error, even though the variable’s qualifier is “series”, because the variable’s assigned reference remains consistent for the rest of each execution:
However, if we use the := operator to reassign the randArray
variable, a compilation error occurs, because the const keyword prevents the script from assigning another array reference to the variable during each execution. For example:
It’s important to note that the const keyword does not directly prevent a script from modifying a collection or drawing object referenced by a variable or function parameter. Scripts can still use the available setter functions to change that object’s data, because those functions do not affect the identifier’s assigned reference.
Below, we edited our script by including a call to array.set(). The call sets the first element of the array referenced by randArray
to 0. Although the contents of the array change after each randArray
declaration, the variable’s assigned reference does not, so no error occurs: