Pinescript v6 User Manual
Pinescript v6 User Manual
# Requirements
Itʼs our explicit goal to keep Pine Script™ accessible and easy to understand for
the broadest
possible audience. Pine Script™ is cloud-based and therefore different from client-
side
programming languages. While we likely wonʼt develop Pine Script™ into a full-
fledged
language, we do constantly improve it and are always happy to consider requests for
new
features.
Because each script uses computational resources in the cloud, we must impose
limits in
order to share these resources fairly among our users. We strive to set as few
limits as
possible, but will of course have to implement as many as needed for the platform
to run
smoothly. Limitations apply to the amount of data requested from additional
symbols,
execution time, memory usage and script size.
```
Next
First steps
```
# First steps
## ## Introduction
Welcome to the Pine Script™ v 6 User Manual, which will accompany you in your
journey to
learn to program your own trading tools in Pine Script™. Welcome also to the very
active
community of Pine Script™ programmers on TradingView.
In this page, we present a step-by-step approach that you can follow to gradually
become
more familiar with indicators and strategies (also called _scripts_ ) written in
the Pine Script™
programming language on TradingView. We will get you started on your journey to:
```
. Use some of the tens of thousands of existing scripts on the platform.
. Read the Pine Script™ code of existing scripts.
. Write Pine Script™ scripts.
```
If you are already familiar with the use of Pine scripts on TradingView and are now
ready to
learn how to write your own, then jump to the Writing scripts section of this page.
## ## Using scripts
```
By using the chartʼs “Indicators, Metrics & Strategies” button, or
By browsing TradingViewʼs Community Scripts, the largest repository of trading
scripts
in the world, with more than 100,000 scripts, most of which are free and open-
source,
which means you can see their Pine Script™ code.
```
If you can find the tools you need already written for you, it can be a good way to
get started
and gradually become proficient as a script user, until you are ready to start your
programming journey in Pine Script™.
To explore and load scripts from your chart, use the “Indicators, Metrics &
Strategies” button:
The dialog box presents different categories of scripts in its left pane:
```
Favorites lists the scripts you have “favorited” by clicking on the star that
appears to
the left of its name when you mouse over it.
Personal displays the scipts you have written and saved in the Pine Editor. They
are
saved in TradingViewʼs cloud.
Technicals groups most TradingView built-ins organized in four categories:
indicators,
strategies, profiles, and patterns. Most are written in Pine Script™ and available
for free.
Financials contains all built-in indicators that display financial metrics. The
contents of
that tab and the subcategories they are grouped into depend on the symbol currently
open on the chart.
Community is where you can search from the 100,000+ published scripts written by
TradingView users. The scripts can be sorted by one of the three different filters
—
Editorʼs Picks only shows open-source scripts hand-picked my our script moderatots,
Top shows the most popular scripts of all time, and Trending displays the most-
popular
scripts that were published recently.
Invite-only contains the list of the invite-only scripts you have been granted
access to
by their authors.
```
Here, the section containing the TradingView built-ins is selected:
When you click on one of the indicators or strategies (the ones marked with a
symbol with
two arrows), it loads on your chart.
From TradingViewʼs homepage, you can bring up the Community Scripts stream from the
“Community” menu. Here, we are pointing to the “Editorsʼ Picks” section, but there
are many
other categories you can choose from:
You can also search for scripts using the homepageʼs “Search” field, and filter
scripts using
different criteria. The Help Center has a page explaining the different types of
scripts that are
available.
The scripts stream shows script _widgets_ , i.e., placeholders showing a miniature
view of each
publicationʼs chart and description, and its author. By clicking on it you will
open the _scriptʼs
page_ , where you can see the script on a chart, read the authorʼs description,
like the script,
leave comments or read the scriptʼs source code if it was published open-source.
Once you find an interesting script in the Community Scripts, follow the
instructions in the
Help Center to load it on your chart.
Once a script is loaded on the chart, you can double-click on its name or hover
over the name
and press the “Settings” button to bring up its “Settings/Inputs” tab:
The “Inputs” tab allows you to change the settings which the scriptʼs author has
decided to
make editable. You can configure some of the scriptʼs visuals using the “Style” tab
of the
same dialog box, and which timeframes the script should appear on using the
“Visibility” tab.
Other settings are available to all scripts from the buttons that appear to the
right of its name
when you mouse over it, and from the “More” menu (the three dots):
## ## Reading scripts
Reading code written by **good** programmers is the best way to develop your
understanding of
the language. This is as true for Pine Script™ as it is for all other programming
languages.
Finding good open-source Pine Script™ code is relatively easy. These are reliable
sources of
code written by good programmers on TradingView:
```
The TradingView built-in indicators
Scripts selected as Editorsʼ Picks
Scripts by the authors the PineCoders account follows
Many scripts by authors with high reputation and open-source publications.
```
Reading code from Community Scripts is easy; if you donʼt see a grey or red “lock”
icon in the
upper-right corner of the scriptʼs widget, this indicates the script is open-
source. By opening
its script page, you will be able to see its source.
To see the code of TradingView built-ins, load the indicator on your chart, then
hover over its
name and select the “Source code” curly braces icon (if you donʼt see it, itʼs
because the
indicatorʼs source is unavailable). When you click on the icon, the Pine Editor
will open and
from there, you can see the scriptʼs code. If you want to play with it, you will
need to press the
“create a working copy” button. You will then be able to modify and save the code.
Because
you will have created a different version of the script, you will need to use the
Editorʼs “Add to
Chart” button to add that new copy to the chart.
This shows the Pine Editor having just opened after we selected the “View source”
button
from the indicator on our chart. We are about to create a working copy of its
source because
it is read-only for now, as indicated by the orange warning text:
You can also open TradingView built-in indicators from the Pine Editor (accessible
from the
“Pine Editor” tab at the bottom of the chart) by using the “Open” -> “Built-in
script...” menu
selection.
## ## Writing scripts
We have built Pine Script™ to empower both budding and seasoned traders to create
their
own trading tools. We have designed it so it is relatively easy to learn for first-
time
programmers — although learning a first programming language, like trading, is
rarely **very**
easy for anyone — yet powerful enough for knowledgeable programmers to build tools
of
moderate complexity.
```
Indicators like RSI, MACD, etc.
Strategies which include logic to issue trading orders and can be backtested and
forward-tested.
Libraries which are used by more advanced programmers to package oft-used
functions that can be reused by other scripts.
```
The next step we recommend is to write your first indicator.
```
Next
First indicator
```
# First indicator
The Pine Editor is where you will be working on your scripts. While you can use any
text editor
you want to write your Pine scripts, using our Editor has many advantages:
```
It highlights your code following Pine Script™ syntax.
It pops up syntax reminders when you hover over language constructs.
It provides quick access to the Pine Script™ Reference Manual popup when you ctrl /
cmd + click on built-in Pine Script™ constructs, and opens the library publication
page when doing the same with construsts imported from libraries.
It provides an auto-complete feature that you can activate with ctrl + space or cmd
+ I , depending on your OS.
It makes the write/compile/run cycle fast because saving a new version of a script
loaded on the chart also executes it immediately.
```
To open the Editor, click on the “Pine Editor” tab at the bottom of your
TradingView chart.
This will open up the Editorʼs pane.
## ## First version
We will now create our first working Pine script, an implementation of the MACD
indicator in
Pine Script™:
```
Start by bringing up the “Open” dropdown menu at the top right of the Editor and
choose “New indicator”.
Copy the example script above. The button on the top-right of the code widget
allows
you to do it with a single click.
Select all the code already in the editor and replace it with the example script.
Click “Save” and choose a name for your script. Your script is now saved in
TradingViewʼs cloud, but under your accountʼs name. Nobody but you can use it.
Click “Add to Chart” in the Editorʼs menu bar. The MACD indicator appears in a
separate
pane under your chart.
```
Your first Pine script is running on your chart, which should look like this:
```
This is a compiler annotation telling the compiler the script will use version 6 of
Pine
Script™.
```
Line 2: **indicator("MACD #1")**
```
Defines the name of the script that will appear on the chart as “MACD”.
```
Line 3: **fast = 12**
```
Defines a fast integer variable which will be the length of the fast EMA.
```
Line 4: **slow = 26**
```
Defines a slow integer variable which will be the length of the slow EMA.
```
Line 5: **fastMA = ta.ema(close, fast)**
```
Defines the variable fastMA , containing the result of the EMA calculation
(Exponential
Moving Average) with a length equal to fast (12), on the close series, i.e., the
closing
price of bars.
```
Line 6: **slowMA = ta.ema(close, slow)**
```
Defines the variable slowMA , containing the result of the EMA calculation with a
length
equal to slow (26), from close.
```
Line 7: **macd = fastMA - slowMA**
```
Defines the variable macd as the difference between the two EMAs.
```
Line 8: **signal = ta.ema(macd, 9)**
```
Defines the variable signal as a smoothed value of macd using the EMA algorithm
(Exponential Moving Average) with a length of 9.
```
Line 9: **plot(macd, color = color.blue)**
```
Calls the plot function to output the variable macd using a blue line.
```
Line 10: **plot(signal, color = color.orange)**
```
Calls the plot function to output the variable signal using an orange line.
```
## ## Second version
The first version of our script calculated MACD “manually”, but because Pine
Script™ is
designed to write indicators and strategies, built-in Pine Script™ functions exist
for many
common indicators, including one for MACD: ta.macd().
```
Added inputs so we can change the lengths for the MAs
We now use the ta.macd() built-in to calculate our MACD, which saves us three line
and
makes our code easier to read.
```
Letʼs repeat the same process as before to copy that code in a new indicator:
```
Start by bringing up the “Open” dropdown menu at the top right of the Editor and
choose “New indicator”.
Then copy the example script above.
Select all the code already in the editor and replace it with the second version of
our
script.
Click “Save” and choose a name for your script different than the previous one.
Click “Add to Chart” in the Editorʼs menu bar. The “MACD #2” indicator appears in a
separate pane under the “MACD #1” indicator.
```
Your second Pine script is running on your chart. If you double-click on the
indicatorʼs name
on your chart, you will bring up the scriptʼs “Settings/Inputs” tab, where you can
now change
the slow and fast lengths:
Letʼs look at the lines that have changed in the second version of our script:
```
We have changed #1 to #2 so the second version of our indicator displays a
different
name on the chart.
```
Line 3: **fastInput = input(12, "Fast length")**
```
Instead of assigning a constant value to a variable, we have used the input()
function so we
can change the value in our scriptʼs “Settings/Inputs” tab. 12 will be the default
value and
the fieldʼs label will be "Fast length". If the value is changed in the “Inputs”
tab, the
fastInput variableʼs content will contain the new value and the script will re-
execute on
the chart with that new value. Note that, as our Pine Script™ Style Guide
recommends, we
add Input to the end of the variableʼs name to remind us, later in the script, that
its value
comes from a user input.
```
Line 4: **slowInput = input(26, "Slow length")**
```
We do the same for the slow length, taking care to use a different variable name,
default
value and text string for the fieldʼs label.
```
Line 5: **[macdLine, signalLine, histLine] = ta.macd(close, fastInput, slowInput,
9)**
```
This is where we call the ta.macd() built-in to perform all the first versionʼs
calculations in
one line only. The function requires four parameters (the values after the function
name,
enclosed in parentheses). It returns three values into the three variables instead
of only
one, like the functions we used until now, which is why we need to enclose the list
of three
variables receiving the functionʼs result in square brackets, to the left of the =
sign. Note
that two of the values we pass to the function are the “input” variables containing
the fast
and slow lengths: fastInput and slowInput.
```
Line 6 and 7:
```
The variable names we are plotting there have changed, but the lines are doing the
same
thing as in our first version.
```
Our second version performs the same calculations as our first, but we can change
the two
lengths used to calculate it. Our code is also simpler and shorter by three lines.
We have
improved our script.
## ## Next
```
Previous
First steps
```
```
Next
Next steps
```
```
Pine Script™
// @version= 6
indicator("MACD #1")
fast =
slow =
fastMA = ta.ema(close, fast)
slowMA = ta.ema(close, slow)
macd = fastMA - slowMA
signal = ta.ema(macd, 9 )
plot(macd, color = color.blue)
plot(signal, color = color.orange)
```
```
Pine Script™
// @version= 6
indicator("MACD #2")
fastInput = input( 12 , "Fast length")
slowInput = input( 26 , "Slow length")
[macdLine, signalLine, histLine] = ta.macd(close, fastInput, slowInput, 9 )
plot(macdLine, color = color.blue)
plot(signalLine, color = color.orange)
```
# Next steps
After your first steps and your first indicator, let us explore a bit more of the
Pine Script™
landscape by sharing some pointers to guide you in your journey to learn Pine
Script™.
## ## “indicators” vs “strategies”
Pine Script™ strategies are used to backtest on historical data and forward test on
open
markets. In addition to indicator calculations, they contain **strategy.*()** calls
to send trade
orders to Pine Script™‘s broker emulator, which can then simulate their execution.
Strategies
display backtest results in the “Strategy Tester” tab at the bottom of the chart,
next to the
“Pine Editor” tab.
Both indicators and strategies can run in either overlay mode (over the chartʼs
bars) or pane
mode (in a separate section below or above the chart). Both can also plot
information in their
respective space, and both can generate alert events.
A Pine script is **not** like programs in many programming languages that execute
once and
then stop. In the Pine Script™ _runtime_ environment, a script runs in the
equivalent of an
invisible loop where it is executed once on each bar of whatever chart you are on,
from left to
right. Chart bars that have already closed when the script executes on them are
called
_historical bars_. When execution reaches the chartʼs last bar and the market is
open, it is on
the _realtime bar_. The script then executes once every time a price or volume
change is
detected, and one last time for that realtime bar when it closes. That realtime bar
then
becomes an _elapsed realtime bar_. Note that when the script executes in realtime,
it does not
recalculate on all the chartʼs historical bars on every price/volume update. It has
already
calculated once on those bars, so it does not need to recalculate them on every
chart tick.
See the Execution model page for more information.
When a script executes on a historical bar, the close built-in variable holds the
value of that
barʼs close. When a script executes on the realtime bar, close returns the
**current** price of the
symbol until the bar closes.
## ## Time series
The main data structure used in Pine Script™ is called a time series. Time series
contain one
value for each bar the script executes on, so they continuously expand as the
script executes
on more bars. Past values of the time series can be referenced using the history-
referencing
operator: []. **close[1]** , for example, refers to the value of close on the bar
preceding the one
where the script is executing.
While this indexing mechanism may remind many programmers of arrays, a time series
is
different and thinking in terms of arrays will be detrimental to understanding this
key Pine
Script™ concept. A good comprehension of both the execution model and time series
is
essential in understanding how Pine scripts work. If you have never worked with
data
organized in time series before, you will need practice to put them to work for
you. Once you
familiarize yourself with these key concepts, you will discover that by combining
the use of
time series with our built-in functions specifically designed to handle them
efficiently, much
can be accomplished in very few lines of code.
## ## Publishing scripts
If want to use Pine scripts for your own use, simply write them in the Pine Editor
and add them
to your chart from there; you donʼt have to publish them to use them. If you want
to share
your scripts with just a few friends, you can publish them privately and send your
friends the
browserʼs link to your private publication. See the page on Publishing for more
information.
While reading code from published scripts is no doubt useful, spending time in our
documentation will be necessary to attain any degree of proficiency in Pine
Script™. Our two
main sources of documentation on Pine Script™ are:
```
This Pine Script™ v 6 User Manual
Our Pine Script™ v 6 Reference Manual
```
The Pine Script™ v 6 User Manual, which is located on its separate page and in
English only.
The Pine Script™ v 6 Reference Manual documents what each language construct does.
It is
an essential tool for all Pine Script™ programmers; your life will be miserable if
you try to write
scripts of any reasonable complexity without consulting it. It exists in two
formats: a separate
page linked above, and the popup version, which can be accessed from the Pine
Editor, by
either **ctrl** / **cmd** + **clicking** on a keyword, or by using the Editorʼs
“More/Reference
Manual...” menu. The Reference Manual is translated in other languages.
There are five different versions of Pine Script™. Ensure the documentation you use
corresponds to the Pine Script™ version you are coding with.
This Pine Script™ v 6 User Manual contains numerous examples of code used to
illustrate the
concepts we discuss. By going through it, you will be able to both learn the
foundations of
Pine Script™ and study the example scripts. Reading about key concepts and trying
them out
right away with real code is a productive way to learn any programming language. As
you
hopefully have already done in the First indicator page, copy this documentationʼs
examples
in the Editor and play with them. Explore! You wonʼt break anything.
This is how the Pine Script™ v 6 User Manual you are reading is organized:
```
The Language section explains the main components of the Pine Script™ language and
how scripts execute.
The Concepts section is more task-oriented. It explains how to do things in Pine
Script™.
The Writing section explores tools and tricks that will help you write and publish
scripts.
The FAQ section answers common questions from Pine Script™ programmers.
The Error messages page documents causes and fixes for the most common runtime
and compiler errors.
The Release Notes page is where you can follow the frequent updates to Pine
Script™.
The Migration guides section explains how to port between different versions of
Pine
Script™.
The Where can I get more information page lists other useful Pine Script™-related
content, including where to ask questions when you are stuck on code.
```
We wish you a successful journey with Pine Script™... and trading!
```
Previous
First indicator
```
# Execution model
The execution model of the Pine Script™ runtime is intimately linked to Pine
Script™‘s time
series and type system. Understanding all three is key to making the most of the
power of
Pine Script™.
The execution model determines how your script is executed on charts, and thus how
the
code you write in scripts works. Your code would do nothing were it not for Pine
Script™‘s
runtime, which kicks in after your code has compiled and it is executed on your
chart because
one of the events triggering the execution of a script has occurred.
When a Pine script is loaded on a chart it executes once on each historical bar
using the
available OHLCV (open, high, low, close, volume) values for each bar. Once the
scriptʼs
execution reaches the rightmost bar in the dataset, if trading is currently active
on the chartʼs
symbol, then Pine Script™ _indicators_ will execute once every time an _update_
occurs, i.e., price
or volume changes. Pine Script™ _strategies_ will by default only execute when the
rightmost
bar closes, but they can also be configured to execute on every update, like
indicators do.
All symbol/timeframe pairs have a dataset comprising a limited number of bars. When
you
scroll a chart to the left to see the datasetʼs earlier bars, the corresponding
bars are loaded on
the chart. The loading process stops when there are no more bars for that
particular
symbol/timeframe pair or the maximum number of bars your account type permits has
been
loaded. You can scroll the chart to the left until the very first bar of the
dataset, which has an
index value of 0 (see bar_index).
When the script first runs on a chart, all bars in a dataset are _historical
bars_ , except the
rightmost one if a trading session is active. When trading is active on the
rightmost bar, it is
called the _realtime bar_. The realtime bar updates when a price or volume change
is detected.
When the realtime bar closes, it becomes an _elapsed realtime bar_ and a new
realtime bar
opens.
Letʼs take a simple script and follow its execution on historical bars:
On historical bars, a script executes at the equivalent of the barʼs close, when
the OHLCV
values are all known for that bar. Prior to execution of the script on a bar, the
built-in variables
such as **open** , **high** , **low** , **close** , **volume** and **time** are set
to values corresponding to
those from that bar. A script executes **once per historical bar**.
Our example script is first executed on the very first bar of the dataset at index
0. Each
statement is executed using the values for the current bar. Accordingly, on the
first bar of the
dataset, the following statement:
initializes the variable **src** with the **close** value for that first bar, and
each of the next lines
is executed in turn. Because the script only executes once for each historical bar,
the script
will always calculate using the same **close** value for a specific historical bar.
The execution of each line in the script produces calculations which in turn
generate the
indicatorʼs output values, which can then be plotted on the chart. Our example uses
the **plot**
and **plotshape** calls at the end of the script to output some values. In the case
of a strategy,
the outcome of the calculations can be used to plot values or dictate the orders to
be placed.
After execution and plotting on the first bar, the script is executed on the
datasetʼs second
bar, which has an index of 1. The process then repeats until all historical bars in
the dataset
are processed and the script reaches the rightmost bar on the chart.
The behavior of a Pine script on the realtime bar is very different than on
historical bars.
Recall that the realtime bar is the rightmost bar on the chart when trading is
active on the
chartʼs symbol. Also, recall that strategies can behave in two different ways in
the realtime
bar. By default, they only execute when the realtime bar closes, but the
**calc_on_every_tick**
parameter of the **strategy** declaration statement can be set to true to modify
the strategyʼs
behavior so that it executes each time the realtime bar updates, as indicators do.
The
behavior described here for indicators will thus only apply to strategies using
**calc_on_every_tick=true**.
**Note:** In the realtime bar, the **close** variable always represents the
**current price**. Similarly,
the **high** and **low** built-in variables represent the highest high and lowest
low reached since
the realtime barʼs beginning. Pine Script™‘s built-in variables will only represent
the realtime
barʼs final values on the barʼs last update.
When the script arrives on the realtime bar it executes a first time. It uses the
current values
of the built-in variables to produce a set of results and plots them if required.
Before the script
executes another time when the next update happens, its user-defined variables are
reset to a
known state corresponding to that of the last _commit_ at the close of the previous
bar. If no
commit was made on the variables because they are initialized every bar, then they
are
reinitialized. In both cases their last calculated state is lost. The state of
plotted labels and
lines is also reset. This resetting of the scriptʼs user-defined variables and
drawings prior to
each new iteration of the script in the realtime bar is called _rollback_. Its
effect is to reset the
script to the same known state it was in when the realtime bar opened, so
calculations in the
realtime bar are always performed from a clean state.
When the realtime bar closes, the script executes a last time. As usual, variables
are rolled
back prior to execution. However, since this iteration is the last one on the
realtime bar,
variables are committed to their final values for the bar when calculations are
completed.
```
A script executes at the open of the realtime bar and then once per update.
Variables are rolled back before every realtime update.
Variables are committed once at the closing bar update.
```
## ## Events triggering the execution of a script
A script is executed on the complete set of bars on the chart when one of the
following events
occurs:
```
A new symbol or timeframe is loaded on a chart.
A script is saved or added to the chart, from the Pine Script™ Editor or the
chartʼs
“Indicators & strategies” dialog box.
A value is modified in the scriptʼs “Settings/Inputs” dialog box.
A value is modified in a strategyʼs “Settings/Properties” dialog box.
A browser refresh event is detected.
```
A script is executed on the realtime bar when trading is active and:
```
One of the above conditions occurs, causing the script to execute on the open of
the
realtime bar, or
The realtime bar updates because a price or volume change was detected.
```
Note that when a chart is left untouched when the market is active, a succession of
realtime
bars which have been opened and then closed will trail the current realtime bar.
While these
_elapsed realtime bars_ will have been _confirmed_ because their variables have all
been
committed, the script will not yet have executed on them in their _historical_
state, since they
did not exist when the script was last run on the chartʼs dataset.
When an event triggers the execution of the script on the chart and causes it to
run on those
bars which have now become historical bars, the scriptʼs calculation can sometimes
vary from
what they were when calculated on the last closing update of the same bars when
they were
realtime bars. This can be caused by slight variations between the OHLCV values
saved at the
close of realtime bars and those fetched from data feeds when the same bars have
become
historical bars. This behavior is one of the possible causes of _repainting_.
## ## More information
```
The built-in barstate.* variables provide information on the type of bar or the
event
where the script is executing. The page where they are documented also contains a
script that allows you to visualize the difference between elapsed realtime and
historical
bars, for example.
The Strategies page explains the details of strategy calculations, which are not
identical
to those of indicators.
```
## ## Historical values of functions
Every function call in Pine leaves a trail of historical values that a script can
access on
subsequent bars using the [] operator. The historical series of functions depend on
successive calls to record the output on every bar. When a script does not call
functions on
each bar, it can produce an inconsistent history that may impact calculations and
results,
namely when it depends on the continuity of their historical series to operate as
expected.
The compiler warns users in these cases to make them aware that the values from a
function,
whether built-in or user-defined, might be misleading.
To demonstrate, letʼs write a script that calculates the index of the current bar
and outputs
that value on every second bar. In the following script, weʼve defined a
**calcBarIndex()**
function that adds 1 to the previous value of its internal **index** variable on
every bar. The
script calls the function on each bar that the **condition** returns **true** on
(every other bar) to
update the **customIndex** value. It plots this value alongside the built-in
**bar_index** to validate
the output:
**Note that:**
```
The nz() function replaces na values with a specified replacement value (0 by
default).
On the first bar of the script, when the index series has no history, the na value
is
replaced with -1 before adding 1 to return an initial value of 0.
```
Upon inspecting the chart, we see that the two plots differ wildly. The reason for
this behavior
is that the script called **calcBarIndex()** within the scope of an if structure on
every other bar,
resulting in a historical output inconsistent with the **bar_index** series. When
calling the
function once every two bars, internally referencing the previous value of
**index** gets the
value from two bars ago, i.e., the last bar the function executed on. This behavior
results in a
**customIndex** value of half that of the built-in **bar_index**.
To align the **calcBarIndex()** output with the **bar_index** , we can move the
function call to the
scriptʼs global scope. That way, the function will execute on every bar, allowing
its entire
history to be recorded and referenced rather than only the results from every other
bar. In the
code below, weʼve defined a **globalScopeBarIndex** variable in the global scope
and assigned
it to the return from **calcBarIndex()** rather than calling the function locally.
The script sets
the **customIndex** to the value of **globalScopeBarIndex** on the occurrence of
the **condition** :
This behavior can also radically impact built-in functions that reference history
internally. For
example, the ta.sma() function references its past values “under the hood”. If a
script calls this
function conditionally rather than on every bar, the values within the calculation
can change
significantly. We can ensure calculation consistency by assigning ta.sma() to a
variable in the
global scope and referencing that variableʼs history as needed.
This behavior is required because forcing the execution of functions on each bar
would lead
to unexpected results in those functions that produce side effects, i.e., the ones
that do
something aside from returning the value. For example, the label.new() function
creates a
label on the chart, so forcing it to be called on every bar even when it is inside
of an if
structure would create labels where they should not logically appear.
## ## Exceptions
Not all built-in functions use their previous values in their calculations, meaning
not all require
execution on every bar. For example, math.max() compares all arguments passed into
it to
return the highest value. Such functions that do not interact with their history in
any way do
not require special treatment.
If the usage of a function within a conditional block does not cause a compiler
warning, itʼs
safe to use without impacting calculations. Otherwise, move the function call to
the global
scope to force consistent execution. When keeping a function call within a
conditional block
despite the warning, ensure the output is correct at the very least to avoid
unexpected
results.
```
Next
Time series
```
```
Pine Script™
// @version= 6
indicator("My Script", overlay = true)
src = close
a = ta.sma(src, 5 )
b = ta.sma(src, 50 )
c = ta.cross(a, b)
plot(a, color = color.blue)
plot(b, color = color.black)
plotshape(c, color = color.red)
```
```
Pine Script™
src = close
```
```
Pine Script™
// @version= 6
indicator("My script")
// @function
Calculates the index of the current bar by adding 1 to its own value fro
// The first bar will have an index of 0.
calcBarIndex() =>
int index = na
index := nz(index[ 1 ], replacement = -1) +
// @variable Returns `true` on every other bar.
condition = bar_index % 2 ==
int customIndex = na
// Call `calcBarIndex()` when the `condition` is `true`. This prompts the compiler
t
if condition
customIndex := calcBarIndex()
plot(bar_index, "Bar index", color = color.green)
plot(customIndex, "Custom index", color = color.red, style = plot.style_cross)
```
```
Pine Script™
// @version= 6
indicator("My script")
// @function
Calculates the index of the current bar by adding 1 to its own value fro
// The first bar will have an index of 0.
calcBarIndex() =>
int index = na
index := nz(index[ 1 ], replacement = -1) +
// @variable Returns `true` on every second bar.
condition = bar_index % 2 ==
globalScopeBarIndex = calcBarIndex()
int customIndex = na
// Assign `customIndex` to `globalScopeBarIndex` when the `condition` is `true`. Th
i
if condition
customIndex := globalScopeBarIndex
plot(bar_index, "Bar index", color = color.green)
plot(customIndex, "Custom index", color = color.red, style = plot.style_cross)
```
```
Pine Script™
// @version= 6
indicator("My script")
// @variable Returns `true` on every second bar.
condition = bar_index % 2 ==
controlSMA = ta.sma(close, 20 )
float globalSMA = na
float localSMA = na
// Update `globalSMA` and `localSMA` when `condition` is `true`.
if condition
globalSMA := controlSMA // No warning.
localSMA := ta.sma(close, 20 ) // Raises warning. This function depends on its h
plot(controlSMA, "Control SMA", color = color.green)
plot(globalSMA, "Global SMA", color = color.blue, style = plot.style_cross)
plot(localSMA, "Local SMA", color = color.red, style = plot.style_cross)
```
# Time series
Much of the power of Pine Script™ stems from the fact that it is designed to
process _time
series_ efficiently. Time series are not a qualified type; they are the fundamental
structure Pine
Script™ uses to store the successive values of a variable over time, where each
value is
tethered to a point in time. Since charts are composed of bars, each representing a
particular
point in time, time series are the ideal data structure to work with values that
may change with
time.
The notion of time series is intimately linked to Pine Script™‘s execution model
and type
system concepts. Understanding all three is key to making the most of the power of
Pine
Script™.
Take the built-in open variable, which contains the “open” price of each bar in the
dataset, the
_dataset_ being all the bars on any given chart. If your script is running on a 5
min chart, then
each value in the open time series is the “open” price of the consecutive 5 min
chart bars.
When your script refers to open, it is referring to the “open” price of the bar the
script is
executing on. To refer to past values in a time series, we use the [] history-
referencing
operator. When a script is executing on a given bar, **open[1]** refers to the
value of the open
time series on the previous bar.
While time series may remind programmers of arrays, they are totally different.
Pine Script™
does use an array data structure, but it is a completely different concept than a
time series.
Time series in Pine Script™, combined with its special type of runtime engine and
built-in
functions, are what makes it easy to compute the cumulative total of close values
without
using a for loop, with only **ta.cum(close)**. This is possible because although
**ta.cum(close)**
appears rather static in a script, it is in fact executed on each bar, so its value
becomes
increasingly larger as the close value of each new bar is added to it. When the
script reaches
the rightmost bar of the chart, **ta.cum(close)** returns the sum of the close
value from all bars
on the chart.
Similarly, the mean of the difference between the last 14 high and low values can
be
expressed as **ta.sma(high - low, 14)** , or the distance in bars since the last
time the chart
made five consecutive higher highs as **barssince(rising(high, 5))**.
Even the result of function calls on successive bars leaves a trace of values in a
time series
that can be referenced using the [] history-referencing operator. This can be
useful, for
example, when testing the close of the current bar for a breach of the highest high
in the last
10 bars, but excluding the current bar, which we could write as **breach = close >
highest(close, 10)[1]**. The same statement could also be written as **breach =
close >
highest(close[1], 10)**.
The same looping logic on all bars is applied to function calls such as
**plot(open)** which will
repeat on each bar, successively plotting on the chart the value of open for each
bar.
Do not confuse “time series” with the “series” qualifier. The _time series_ concept
explains how
consecutive values of variables are stored in Pine Script™; the “series” qualifier
denotes
variables whose values can change bar to bar. Consider, for example, the
timeframe.period
built-in variable which has the “simple” qualifier and “string” type, meaning it is
of the “simple
string” qualified type. The “simple” qualifier entails that the variableʼs value is
established on
bar zero (the first bar where the script executes) and will not change during the
scriptʼs
execution on any of the chartʼs bars. The variableʼs value is the chartʼs timeframe
in string
format, so **"D"** for a 1 D chart, for example. Even though its value cannot
change during the
script, it would be syntactically correct in Pine Script™ (though not very useful)
to refer to its
value 10 bars ago using **timeframe.period[10]**. This is possible because the
successive
values of timeframe.period for each bar are stored in a time series, even though
all the values
in that particular time series are the same. Note, however, that when the []
operator is used to
access past values of a variable, it yields a “series” qualified value, even when
the variable
without an offset uses a different qualifier, such as “simple” in the case of
timeframe.period.
When you grasp how time series can be efficiently handled using Pine Script™‘s
syntax and
its execution model, you can define complex calculations using little code.
```
Previous
Execution model
```
```
Next
Script structure
```
# Script structure
A Pine script follows this general structure:
## ## Version
A compiler annotation in the following form tells the compiler which of the
versions of Pine
Script™ the script is written in:
```
The version number is a number from 1 to 6.
The compiler annotation is not mandatory. When omitted, version 1 is assumed. It is
strongly recommended to always use the latest version of the language.
While it is synctactically correct to place the version compiler annotation
anywhere in
the script, it is much more useful to readers when it appears at the top of the
script.
```
Notable changes to the current version of Pine Script™ are documented in the
Release notes.
## ## Declaration statement
All Pine scripts must contain one declaration statement, which is a call to one of
these
functions:
```
indicator()
strategy()
library()
```
The declaration statement:
```
Identifies the type of the script, which in turn dictates which content is allowed
in it, and
how it can be used and executed.
Sets key properties of the script such as its name, where it will appear when it is
added
to a chart, the precision and format of the values it displays, and certain values
that
govern its runtime behavior, such as the maximum number of drawing objects it will
display on the chart. With strategies, the properties include parameters that
control
backtesting, such as initial capital, commission, slippage, etc.
```
Each type of script has distinct requirements:
```
Indicators must contain at least one function call which produces output on the
chart
(e.g., plot(), plotshape(), barcolor(), line.new(), etc.).
Strategies must contain at least one strategy.*() call, e.g., strategy.entry().
Libraries must contain at least one exported function or user-defined type.
```
## ## Code
Lines in a script that are not comments or compiler annotations are _statements_ ,
which
implement the scriptʼs algorithm. A statement can be one of these:
```
variable declaration
variable reassignement
function declaration
built-in function call, user-defined function call or a library function call
if, for, while, switch, type, or enum structure.
```
Statements can be arranged in multiple ways:
```
Some statements can be expressed in one line, like most variable declarations,
lines
containing only a function call or single-line function declarations. Lines can
also be
wrapped (continued on multiple lines). Multiple one-line statements can be
concatenated on a single line by using the comma as a separator.
Others statements such as structures or multi-line function declarations always
require
multiple lines because they require a local block. A local block must be indented
by a tab
or four spaces. Each local block defines a distinct local scope.
Statements in the global scope of the script (i.e., which are not part of local
blocks)
cannot begin with white space (a space or a tab). Their first character must also
be the
lineʼs first character. Lines beginning in a lineʼs first position become by
definition part of
the scriptʼs global scope.
```
A simple valid Pine Script™ indicator can be generated in the Pine Script™ Editor
by using the
“Open” button and choosing “New blank indicator”:
This indicator includes three local blocks, one in the **barIsUp()** function
declaration, and two
in the variable declaration using an if structure:
You can bring up a simple Pine Script™ strategy by selecting “New blank strategy”
instead:
## ## Comments
Double slashes ( **//** ) define comments in Pine Script™. Comments can begin
anywhere on
the line. They can also follow Pine Script™ code on the same line:
## ## Line wrapping
Long lines can be split on multiple lines, or “wrapped”. Wrapped lines must be
indented with
any number of spaces, provided itʼs not a multiple of four (those boundaries are
used to
indent local blocks):
## ## Compiler annotations
Compiler annotations are comments that issue special instructions for a script:
```
//@version= specifies the PineScript™ version that the compiler will use. The
number
in this annotation should not be confused with the scriptʼs version number, which
updates on every saved change to the code.
//@description sets a custom description for scripts that use the library()
declaration
statement.
//@function , //@param and //@returns add custom descriptions for a user-defined
function or method, its parameters, and its result when placed above the function
declaration.
//@type adds a custom description for a user-defined type (UDT) when placed above
the type declaration.
//@enum adds a custom description for an enum types when placed above the enum
declaration.
//@field adds a custom description for the field of a user-defined type (UDT) or an
enum types when placed above the type or enum declaration.
//@variable adds a custom description for a variable when placed above its
declaration.
//@strategy_alert_message provides a default message for strategy scripts to pre-
fill
the “Message” field in the alert creation dialog.
```
The Pine Editor also features two specialized annotations, **//#region** and
**//#endregion** ,
that create _collapsible_ code regions. Clicking the dropdown arrow next to a
**//#region** line
collapses all the code between that line and the nearest **//#endregion**
annotation below it.
This example draws a triangle using three interactively selected points on the
chart. The
script illustrates how one can use compiler and Editor annotations to document code
and
make it easier to navigate:
```
Previous
Time series
```
```
Next
Identifiers
```
```
<version>
<declaration_statement>
<code>
```
```
Pine Script™
// @version= 6
```
```
Pine Script™
// @version= 6
indicator("My Script")
plot(close)
```
```
Pine Script™
// @version= 6
indicator("", "", true) // Declaration statement (global scope)
barIsUp() => // Function declaration (global scope)
close > open // Local block (local scope)
plotColor = if barIsUp() // Variable declaration (global scope)
color.green // Local block (local scope)
else
color.red // Local block (local scope)
bgcolor(color.new(plotColor, 70 )) // Call to a built-in function (global scope)
```
```
Pine Script™
// @version= 6
strategy("My Strategy", overlay=true, margin_long= 100 , margin_short= 100 )
longCondition = ta.crossover(ta.sma(close, 14 ), ta.sma(close, 28 ))
if (longCondition)
strategy.entry("My Long Entry Id", strategy.long)
shortCondition = ta.crossunder(ta.sma(close, 14 ), ta.sma(close, 28 ))
if (shortCondition)
strategy.entry("My Short Entry Id", strategy.short)
```
```
Pine Script™
// @version= 6
indicator("")
// This line is a comment
a = close // This is also a comment
plot(a)
```
```
Pine Script™
a = open + high + low + close
```
```
Pine Script™
a = open +
high +
low +
close
```
```
Pine Script™
plot(ta.correlation(src, ovr, length),
color = color.new(color.purple, 40 ),
style = plot.style_area,
trackprice = true)
```
```
Pine Script™
updown(s) =>
isEqual = s == s[ 1 ]
isGrowing = s > s[ 1 ]
ud = isEqual?
0 :
isGrowing?
(nz(ud[ 1 ]) <= 0 ?
1 :
nz(ud[ 1 ])+1) :
(nz(ud[ 1 ]) >= 0 ?
-1 :
nz(ud[ 1 ])-1)
```
```
Pine Script™
// @version= 6
indicator("")
c = open > close? color.red :
high > high[ 1 ]? color.lime : // A comment
low < low[ 1 ]? color.blue : color.black
bgcolor(c)
```
```
Pine Script™
// @version= 6
indicator("Triangle", "", true)
//#region ———————————————————— Constants and inputs
int TIME_DEFAULT =
float PRICE_DEFAULT = 0.
x1Input = input.time(TIME_DEFAULT, "Point 1", inline = "1", confirm = true)
y1Input = input.price(PRICE_DEFAULT, "", inline = "1", tooltip = "Pick point
x2Input = input.time(TIME_DEFAULT, "Point 2", inline = "2", confirm = true)
y2Input = input.price(PRICE_DEFAULT, "", inline = "2", tooltip = "Pick point
x3Input = input.time(TIME_DEFAULT, "Point 3", inline = "3", confirm = true)
y3Input = input.price(PRICE_DEFAULT, "", inline = "3", tooltip = "Pick point
//#endregion
//#region ———————————————————— Types and functions
// @type
Used to represent the coordinates and color to draw a triangle.
// @field time1 Time of first point.
// @field time2 Time of second point.
// @field time3 Time of third point.
// @field price1 Price of first point.
// @field price2 Price of second point.
// @field price3 Price of third point.
// @field lineColor Color to be used to draw the triangle lines.
type Triangle
int time
int time
int time
float price
float price
float price
color lineColor
// @function Draws a triangle using the coordinates of the `t` object.
// @param t (Triangle) Object representing the triangle to be drawn.
// @returns The ID of the last line drawn.
drawTriangle( Triangle t) =>
line.new(t.time1, t.price1, t.time2, t.price2, xloc = xloc.bar_time, color = t.l
line.new(t.time2, t.price2, t.time3, t.price3, xloc = xloc.bar_time, color = t.l
line.new(t.time1, t.price1, t.time3, t.price3, xloc = xloc.bar_time, color = t.l
//#endregion
//#region ———————————————————— Calculations
// Draw the triangle only once on the last historical bar.
if barstate.islastconfirmedhistory
// @variable Used to hold the Triangle object to be drawn.
Triangle triangle = Triangle.new()
triangle.time1 := x1Input
triangle.time2 := x2Input
triangle.time3 := x3Input
triangle.price1 := y1Input
triangle.price2 := y2Input
triangle.price3 := y3Input
triangle.lineColor := color.purple
drawTriangle(triangle)
//#endregion
```
# Identifiers
```
They must begin with an uppercase ( A-Z ) or lowercase ( a-z ) letter, or an
underscore
( _ ).
The next characters can be letters, underscores or digits ( 0-9 ).
They are case-sensitive.
```
Here are some examples:
The Pine Script™ Style Guide recommends using uppercase SNAKE_CASE for constants,
and
camelCase for other identifiers:
```
Previous
Script structure
```
```
Next
Operators
```
```
Pine Script™
myVar
_myVar
my123Var
functionName
MAX_LEN
max_len
maxLen
3barsDown // NOT VALID!
```
```
Pine Script™
GREEN_COLOR = #4CAF
MAX_LOOKBACK =
int fastLength =
// Returns 1 if the argument is `true`, 0 if it is `false` or `na`.
zeroOne(boolValue) => boolValue? 1 :
```
# Operators
## ## Introduction
```
Arithmetic operators
Comparison operators
Logical operators
The ?: ternary operator
The [] history-referencing operator
```
Other operators are used to assign values to variables:
```
= is used to assign a value to a variable, but only when you declare the variable
(the
first time you use it)
:= is used to assign a value to a previously declared variable. The following
operators can also be used in such a way: += , -= , *= , /= , %=
```
As is explained in the Type system page, _qualifiers_ and _types_ play a critical
role in determining
the type of results that expressions yield. This, in turn, has an impact on how and
with what
functions you will be allowed to use those results. Expressions always return a
value with the
strongest qualifier used in the expression, e.g., if you multiply an “input int”
with a “series int”,
the expression will produce a “series int” result, which you will not be able to
use as the
argument to **length** in ta.ema().
```
Using another moving average function that supports a “series int” length, such as
ta.sma(), or
Not using a calculation producing a “series int” value for our length.
```
## ## Arithmetic operators
```
Operator Meaning
+ Addition and string concatenation
```
**-** Subtraction
```
* Multiplication
/ Division
```
```
% Modulo (remainder after division)
```
The arithmetic operators above are all binary (means they need two _operands_ — or
values —
to work on, like in **1 + 2** ). The **+** and **-** also serve as unary operators
(means they work on
one operand, like **-1** or **+1** ).
If both operands are numbers but at least one of these is of float type, the result
will also be a
float. If both operands are of int type, the result will also be an int. If at
least one operand is
na, the result is also na.
The **+** operator also serves as the concatenation operator for strings.
**"EUR"+"USD"** yields
the **"EURUSD"** string.
The **%** operator calculates the modulo by rounding down the quotient to the
lowest possible
value. Here is an easy example that helps illustrate how the modulo is calculated
behind the
scenes:
## ## Comparison operators
There are six comparison operators in Pine Script™:
```
Operator Meaning
< Less Than
```
```
<= Less Than or Equal To
!= Not Equal
```
```
== Equal
```
```
> Greater Than
>= Greater Than or Equal To
```
Comparison operations are binary. If both operands have a numerical value, the
result will be
of type _bool_ , i.e., **true** , **false** or na.
Examples:
## ## Logical operators
```
Operator Meaning
```
```
not Negation
and Logical Conjunction
```
```
or Logical Disjunction
```
The operator **not** is unary. When applied to a **true** , operand the result will
be **false** , and
vice versa.
```
and operator truth table:
```
```
a b a and b
true true true
```
```
true false false
```
```
false true false
false false false
```
```
or operator truth table:
```
```
a b a or b
true true true
```
```
true false true
```
```
false true true
false false false
```
## ## `?:` ternary operator
The ternary operator returns a result that depends on the value of **condition**.
If it is **true** ,
then **valueWhenConditionIsTrue** is returned. If **condition** is **false** or na,
then
**valueWhenConditionIsFalse** is returned.
```
If timeframe.isintraday is true , then color.red is returned. If it is false , then
timeframe.isdaily is evaluated.
If timeframe.isdaily is true , then color.green is returned. If it is false , then
timeframe.ismonthly is evaluated.
If timeframe.ismonthly is true , then color.blue is returned, otherwise na is
returned.
```
Note that the return values on each side of the **:** are expressions --- not local
blocks, so
they will not affect the limit of 500 local blocks per scope.
## ## `[ ]` history-referencing operator
The [] operator is used after a variable, expression or function call. The value
used inside the
square brackets of the operator is the offset in the past we want to refer to. To
refer to the
value of the volume built-in variable two bars away from the current bar, one would
use
**volume[2]**.
Because series grow dynamically, as the script moves on sucessive bars, the offset
used with
the operator will refer to different bars. Letʼs see how the value returned by the
same offset is
dynamic, and why series are very different from arrays. In Pine Script™, the close
variable, or
**close[0]** which is equivalent, holds the value of the current barʼs “close”. If
your code is now
executing on the **third** bar of the _dataset_ (the set of all bars on your
chart), **close** will
contain the price at the close of that bar, **close[1]** will contain the price at
the close of the
preceding bar (the datasetʼs second bar), and **close[2]** , the first bar.
**close[3]** will return
na because no bar exists in that position, and thus its value is _not available_.
When the same code is executed on the next bar, the **fourth** in the dataset,
**close** will now
contain the closing price of that bar, and the same **close[1]** used in your code
will now refer
to the “close” of the third bar in the dataset. The close of the first bar in the
dataset will now
be **close[3]** , and this time **close[4]** will return na.
In the Pine Script™ runtime environment, as your code is executed once for each
historical bar
in the dataset, starting from the left of the chart, Pine Script™ is adding a new
element in the
series at index 0 and pushing the pre-existing elements in the series one index
further away.
Arrays, in comparison, can have constant or variable sizes, and their content or
indexing
structure is not modified by the runtime environment. Pine Script™ series are thus
very
different from arrays and only share familiarity with them through their indexing
syntax.
When the market for the chartʼs symbol is open and the script is executing on the
chartʼs last
bar, the _realtime bar_ , close returns the value of the current price. It will
only contain the actual
closing price of the realtime bar the last time the script is executed on that bar,
when it closes.
Pine Script™ has a variable that contains the number of the bar the script is
executing on:
bar_index. On the first bar, bar_index is equal to 0 and it increases by 1 on each
successive
bar the script executes on. On the last bar, bar_index is equal to the number of
bars in the
dataset minus one.
There is another important consideration to keep in mind when using the **[]**
operator in Pine
Script™. We have seen cases when a history reference may return the na value. na
represents
a value which is not a number and using it in any expression will produce a result
that is also
na (similar to NaN). Such cases often happen during the scriptʼs calculations in
the early bars
of the dataset, but can also occur in later bars under certain conditions. If your
code does not
explicitly provide for handling these special cases, they can introduce invalid
results in your
scriptʼs calculations which can ripple through all the way to the realtime bar. The
na and nz
functions are designed to allow for handling such cases.
Note that the [] operator can only be used once on the same value. This is not
allowed:
## ## Operator precedence
```
Precedence Operator
```
```
9 []
8 unary + , unary - , not
```
```
7 * , / , %
6 + , -
```
```
5 > , < , >= , <=
```
```
4 == , !=
3 and
```
```
2 or
```
```
1 ?:
```
If in one expression there are several operators with the same precedence, then
they are
calculated left to right.
See the Variable declarations page for more information on how to declare
variables.
The **:=** is used to _reassign_ a value to an existing variable. It says _use this
variable that was
declared earlier in my script, and give it a new value_.
Variables which have been first declared, then reassigned using **:=** , are called
_mutable_
variables. All the following examples are valid variable reassignments. You will
find more
information on how var works in the section on the `var` declaration mode:
Note that:
```
We declare pHi with this code: var float pHi = na. The var keyword tells Pine
Script™ that we only want that variable initialized with na on the datasetʼs first
bar. The
float keyword tells the compiler we are declaring a variable of type “float”. This
is
necessary because, contrary to most cases, the compiler cannot automatically
determine the type of the value on the right side of the = sign.
While the variable declaration will only be executed on the first bar because it
uses var,
the pHi := nz(ta.pivothigh(5, 5), pHi) line will be executed on all the chartʼs
bars.
On each bar, it evaluates if the pivothigh() call returns na because that is what
the
function does when it hasnʼt found a new pivot. The nz() function is the one doing
the
“checking for na” part. When its first argument ( ta.pivothigh(5, 5) ) is na, it
returns
the second argument ( pHi ) instead of the first. When pivothigh() returns the
price
point of a newly found pivot, that value is assigned to pHi. When it returns na
because
no new pivot was found, we assign the previous value of pHi to itself, in effect
preserving its previous value.
```
The output of our script looks like this:
Note that:
```
The line preserves its previous value until a new pivot is found.
Pivots are detected five bars after the pivot actually occurs because our
ta.pivothigh(5, 5) call says that we require five lower highs on both sides of a
high
point for it to be detected as a pivot.
```
See the Variable reassignment section for more information on how to reassign
values to
variables.
```
Previous
Identifiers
```
```
Next
Variable declarations
```
```
Pine Script™
// @version= 6
indicator("")
lenInput = input.int( 14 , "Length")
factor = year > 2020 ? 3 :
adjustedLength = lenInput * factor
ma = ta.ema(close, adjustedLength) // Compilation error!
plot(ma)
```
```
Pine Script™
// @version= 6
indicator("Modulo function")
modulo( series int a, series int b) =>
a - b * math.floor(nz(a/b))
plot(modulo(-1, 100 ))
```
```
Pine Script™
1 > 2 // false
1 != 1 // false
close >= open // Depends on values of `close` and `open`
```
```
Pine Script™
condition? valueWhenConditionIsTrue : valueWhenConditionIsFalse
```
```
Pine Script™
timeframe.isintraday? color.red : timeframe.isdaily? color.green : timeframe.ismon
```
```
Pine Script™
high[ 10 ]
ta.sma(close, 10 )[ 1 ]
ta.highest(high, 10 )[ 20 ]
close > nz(close[ 1 ], open)
```
```
Pine Script™
close[ 1 ][ 2 ] // Error: incorrect use of [] operator
```
```
Pine Script™
i =
MS_IN_ONE_MINUTE = 1000 *
showPlotInput = input.bool(true, "Show plots")
pHi = pivothigh( 5 , 5 )
plotColor = color.green
```
```
Pine Script™
// @version= 6
indicator("", "", true)
// Declare `pHi` and initilize it on the first bar only.
var float pHi = na
// Reassign a value to `pHi`
pHi := nz(ta.pivothigh( 5 , 5 ), pHi)
plot(pHi)
```
# Variable declarations
## ## Introduction
Variables are identifiers that hold values. They must be _declared_ in your code
before you use
them. The syntax of variable declarations is:
or
where:
```
| means “or”, and parts enclosed in square brackets ( [] ) can appear zero or one
time.
<declaration_mode> is the variableʼs declaration mode. It can be var or varip, or
nothing.
<type> is optional, as in almost all Pine Script™ variable declarations (see
types).
<identifier> is the variableʼs name.
<expression> can be a literal, a variable, an expression or a function call.
<structure> can be an if, for, while or switch structure.
<tuple_declaration> is a comma-separated list of variable names enclosed in square
brackets ( [] ), e.g., [ma, upperBand, lowerBand].
```
These are all valid variable declarations. The last one requires four lines:
In the first line of the example, the compiler cannot determine the type of the
**baseLine**
variable because na is a generic value of no particular type. The declaration of
the **baseLine**
variable is correct because its float type is declared explicitly. The declaration
of the
**baseLine2** variable is also correct because its type can be derived from the
expression
**float(na)** , which is an explicit cast of the na value to the float type. The
declarations of
**baseLine1** and **baseLine2** are equivalent.
## ## Tuple declarations
Function calls or structures are allowed to return multiple values. When we call
them and want
to store the values they return, a _tuple declaration_ must be used, which is a
comma-
separated set of one or more values enclosed in brackets. This allows us to declare
multiple
variables simultaneously. As an example, the ta.bb() built-in function for
Bollinger bands
returns three values:
This is particularly useful when a tuple returns unneeded values. Letʼs write
another Bollinger
Bands script. Here, we only need the bands themselves, without the center line:
## ## Variable reassignment
Note that:
```
We initialize maColor on the first bar only, so it preserves its value across bars.
On every bar, the if statement checks if the MA has been rising or falling for the
user-
specified number of bars (the default is 2). When that happens, the value of
maColor
must be reassigned a new value from within the if local blocks. To do this, we use
the :=
reassignment operator.
If we did not use the := reassignment operator, the effect would be to initialize a
new
maColor local variable which would have the same name as that of the global scope,
but actually be a very confusing independent entity that would persist only for the
length of the local block, and then disappear without a trace.
```
All user-defined variables in Pine Script™ are _mutable_ , which means their value
can be
changed using the := reassignment operator. Assigning a new value to a variable may
change
its _type qualifier_ (see the page on Pine Script™‘s type system for more
information). A variable
can be assigned a new value as many times as needed during the scriptʼs execution
on one
bar, so a script can contain any number of reassignments of one variable. A
variableʼs
declaration mode determines how new values assigned to a variable will be saved.
## ## Declaration modes
Understanding the impact that declaration modes have on the behavior of variables
requires
prior knowledge of Pine Script™‘s execution model.
```
“On each bar”, when none is specified
var
varip
```
## ## On each bar
## ## `var`
When the var keyword is used, the variable is only initialized once, on the first
bar if the
declaration is in the global scope, or the first time the local block is executed
if the declaration
is inside a local block. After that, it will preserve its last value on successive
bars, until we
reassign a new value to it. This behavior is very useful in many cases where a
variableʼs value
must persist through the iterations of a script across successive bars. For
example, suppose
weʼd like to count the number of green bars on the chart:
Without the **var** modifier, variable **count** would be reset to zero (thus
losing its value) every
time a new bar update triggered a script recalculation.
Declaring variables on the first bar only is often useful to manage drawings more
efficiently.
Suppose we want to extend the last barʼs close line to the right of the right
chart. We could
write:
but this is inefficient because we are creating and deleting the line on each
historical bar and
on each update in the realtime bar. It is more efficient to use:
Note that:
```
We initialize closeLine on the first bar only, using the var declaration mode
We restrict the execution of the rest of our code to the chartʼs last bar by
enclosing our
code that updates the line in an ifbarstate.islast structure.
```
There is a very slight penalty performance for using the var declaration mode. For
that reason,
when declaring constants, it is preferable not to use var if performance is a
concern, unless
the initialization involves calculations that take longer than the maintenance
penalty, e.g.,
functions with complex code or string manipulations.
## ## `varip`
Understanding the behavior of variables using the varip declaration mode requires
prior
knowledge of Pine Script™‘s execution model and bar states.
The varip keyword can be used to declare variables that escape the _rollback
process_ , which is
explained in the page on Pine Script™‘s execution model.
Whereas scripts only execute once at the close of historical bars, when a script is
running in
realtime, it executes every time the chartʼs feed detects a price or volume update.
At every
realtime update, Pine Script™‘s runtime normally resets the values of a scriptʼs
variables to
their last committed value, i.e., the value they held when the previous bar closed.
This is
generally handy, as each realtime script execution starts from a known state, which
simplifies
script logic.
Sometimes, however, script logic requires code to be able to save variable values
**between
different executions** in the realtime bar. Declaring variables with varip makes
that possible.
The “ip” in varip stands for _intrabar persist_.
Letʼs look at the following code, which does not use varip:
On historical bars, barstate.isnew is always true, so the plot shows a value of “1”
because the
**else** part of the if structure is never executed. On realtime bars,
barstate.isnew is only true
when the script first executes on the barʼs “open”. The plot will then briefly
display “1” until
subsequent executions occur. On the next executions during the realtime bar, the
second
branch of the if statement is executed because barstate.isnew is no longer true.
Since
**updateNo** is initialized to na at each execution, the **updateNo + 1**
expression yields na, so
nothing is plotted on further realtime executions of the script.
If we now use varip to declare the **updateNo** variable, the script behaves very
differently:
The difference now is that **updateNo** tracks the number of realtime updates that
occur on
each realtime bar. This can happen because the varip declaration allows the value
of
**updateNo** to be preserved between realtime updates; it is no longer rolled back
at each
realtime execution of the script. The test on barstate.isnew allows us to reset the
update
count when a new realtime bar comes in.
Because varip only affects the behavior of your code in the realtime bar, it
follows that
backtest results on strategies designed using logic based on varip variables will
not be able to
reproduce that behavior on historical bars, which will invalidate test results on
them. This also
entails that plots on historical bars will not be able to reproduce the scriptʼs
behavior in
realtime.
```
Previous
Operators
```
```
Next
Conditional structures
```
```
[<declaration_mode>] [<type>] <identifier> = <expression> | <structure>
```
```
<tuple_declaration> = <function_call> | <structure>
```
```
Pine Script™
BULL_COLOR = color.lime
i =
len = input( 20 , "Length")
float f = 10.
closeRoundedToTick = math.round_to_mintick(close)
st = ta.supertrend( 4 , 14 )
var barRange = float(na)
var firstBarOpen = open
varip float lastClose = na
[macdLine, signalLine, histLine] = ta.macd(close, 12 , 26 , 9 )
plotColor = if close > open
color.green
else
color.red
```
```
Notice! The above statements all contain the = assignment operator because they
are variable declarations. When you see similar lines using the := reassignment
operator, the code is reassigning a value to a variable that was already declared.
Those are variable reassignments. Be sure you understand the distinction as this is
a
common stumbling block for newcomers to Pine Script™. See the next Variable
reassignment section for details.
```
```
<variable_declaration>
[<declaration_mode>] [<type>] <identifier> = <expression> | <structure>
|
<tuple_declaration> = <function_call> | <structure>
```
```
<declaration_mode>
var | varip
```
```
<type>
int | float | bool | color | string | line | linefill | label | box |
table | array<type> | matrix<type> | UDF
```
```
Pine Script™
baseLine0 = na // compile time error!
float baseLine1 = na // OK
baseLine2 = float(na) // OK
```
```
Pine Script™
[bbMiddle, bbUpper, bbLower] = ta.bb(close, 5 , 4 )
```
```
Pine Script™
// @version= 6
indicator("Underscore demo")
// We do not need the middle Bollinger Bands value, and do not use it.
// To make this clear, we assign it to the `_` identifier.
[_, bbUpper, bbLower] = ta.bb(close, 5 , 4 )
// We can continue to use `_` in the same code without causing compilation errors:
[bbMiddleLong, _, _] = ta.bb(close, 20 , 2 )
plot(bbUpper)
```
```
Pine Script™
// @version= 6
indicator("", "", true)
sensitivityInput = input.int( 2 , "Sensitivity", minval = 1 , tooltip =
"Higher values
ma = ta.sma(close, 20 )
maUp = ta.rising(ma, sensitivityInput)
maDn = ta.falling(ma, sensitivityInput)
// On first bar only, initialize color to gray
var maColor = color.gray
if maUp
// MA has risen for two bars in a row; make it lime.
maColor := color.lime
else if maDn
// MA has fallen for two bars in a row; make it fuchsia.
maColor := color.fuchsia
plot(ma, "MA", maColor, 2 )
```
```
Pine Script™
BULL_COLOR = color.lime
i =
len = input( 20 , "Length")
float f = 10.
closeRoundedToTick = math.round_to_mintick(close)
st = ta.supertrend( 4 , 14 )
[macdLine, signalLine, histLine] = ta.macd(close, 12 , 26 , 9 )
plotColor = if close > open
color.green
else
color.red
```
```
Pine Script™
// @version= 6
indicator("Green Bars Count")
var count =
isGreen = close >= open
if isGreen
count := count +
plot(count)
```
```
Pine Script™
// @version= 6
indicator("Inefficient version", "", true)
closeLine = line.new(bar_index - 1 , close, bar_index, close, extend =
extend.right,
line.delete(closeLine[ 1 ])
```
```
Pine Script™
// @version= 6
indicator("Efficient version", "", true)
var closeLine = line.new(bar_index - 1 , close, bar_index, close, extend =
extend.rig
if barstate.islast
line.set_xy1(closeLine, bar_index - 1 , close)
line.set_xy2(closeLine, bar_index, close)
```
```
Pine Script™
// @version= 6
indicator("")
int updateNo = na
if barstate.isnew
updateNo :=
else
updateNo := updateNo +
plot(updateNo, style = plot.style_circles)
```
```
Pine Script™
// @version= 6
indicator("")
varip int updateNo = na
if barstate.isnew
updateNo :=
else
updateNo := updateNo +
plot(updateNo, style = plot.style_circles)
```
# Conditional structures
## ## Introduction
The conditional structures in Pine Script™ are if and switch. They can be used:
```
For their side effects, i.e., when they donʼt return a value but do things, like
reassign
values to variables or call functions.
To return a value or a tuple which can then be assigned to one (or more, in the
case of
tuples) variable.
```
Conditional structures, like the for and while structures, can be embedded; you can
use an if
or switch inside another structure.
Some Pine Script™ built-in functions cannot be called from within the local blocks
of
conditional structures. They are: alertcondition(), barcolor(), fill(), hline(),
indicator(), library(),
plot(), plotbar(), plotcandle(), plotchar(), plotshape(), strategy(). This does not
entail their
functionality cannot be controlled by conditions evaluated by your script — only
that it cannot
be done by including them in conditional structures. Note that while **input*.()**
function calls
are allowed in local blocks, their functionality is the same as if they were in the
scriptʼs global
scope.
## ## `if` structure
An if structure used for its side effects has the following syntax:
where:
```
Parts enclosed in square brackets ( [] ) can appear zero or one time, and those
enclosed in curly braces ( {} ) can appear zero or more times.
<expression> must be of “bool” type or be auto-castable to that type, which is only
possible for “int” or “float” values (see the Type system page).
<local_block> consists of zero or more statements followed by a return value, which
can
be a tuple of values. It must be indented by four spaces or a tab.
There can be zero or more else if clauses.
There can be zero or one else clause.
```
When the <expression> following the if evaluates to true, the first local block is
executed, the
if structureʼs execution ends, and the value(s) evaluated at the end of the local
block are
returned.
When the <expression> following the if evaluates to false, the successive **else
if** clauses
are evaluated, if there are any. When the <expression> of one evaluates to true,
its local block
is executed, the if structureʼs execution ends, and the value(s) evaluated at the
end of the
local block are returned.
When no <expression> has evaluated to true and an **else** clause exists, its local
block is
executed, the if structureʼs execution ends, and the value(s) evaluated at the end
of the local
block are returned.
Using if structures for their side effects can be useful to manage the order flow
in strategies,
for example. While the same functionality can often be achieved using the **when**
parameter in
**strategy.*()** calls, code using if structures is easier to read:
Restricting the execution of your code to specific bars ican be done using if
structures, as we
do here to restrict updates to our label to the chartʼs last bar:
Note that:
```
We initialize the ourLabel variable on the scriptʼs first bar only, as we use the
var
declaration mode. The value used to initialize the variable is provided by the
label.new()
function call, which returns a label ID pointing to the label it creates. We use
that call to
set the labelʼs properties because once set, they will persist until we change
them.
What happens next is that on each successive bar the Pine Script™ runtime will skip
the
initialization of ourLabel , and the if structureʼs condition (barstate.islast) is
evaluated. It
returns false on all bars until the last one, so the script does nothing on most
historical
bars after bar zero.
On the last bar, barstate.islast becomes true and the structureʼs local block
executes,
modifying on each chart update the properties of our label, which displays the
number
of bars in the dataset.
We want to display the labelʼs text without a background, so we make the labelʼs
background na in the label.new() function call, and we use hl2[1] for the labelʼs y
position because we donʼt want it to move all the time. By using the average of the
previous barʼs high and low values, the label doesnʼt move until the moment when
the
next realtime bar opens.
We use bar_index + 2 in our label.set_xy() call to offset the label to the right by
two
bars.
```
## ## `if` used to return a value
An if structure used to return one or more values has the following syntax:
where:
```
Parts enclosed in square brackets ( [] ) can appear zero or one time, and those
enclosed in curly braces ( {} ) can appear zero or more times.
<declaration_mode> is the variableʼs declaration mode
<type> is optional, as in almost all Pine Script™ variable declarations (see types)
<identifier> is the variableʼs name
<expression> can be a literal, a variable, an expression or a function call.
<local_block> consists of zero or more statements followed by a return value, which
can
be a tuple of values. It must be indented by four spaces or a tab.
The value assigned to the variable is the return value of the <local_block>, or na
if no
local block is executed. If other local blocks return “bool” values, false will be
returned
instead.
```
This is an example:
Scripts can contain **if** structures with nested **if** and other conditional
structures. For
example:
The switch structure exists in two forms. One switches on the different values of a
key
expression:
The other form does not use an expression as a key; it switches on the evaluation
of different
expressions:
where:
```
Parts enclosed in square brackets ( [] ) can appear zero or one time, and those
enclosed in curly braces ( {} ) can appear zero or more times.
<declaration_mode> is the variableʼs declaration mode
<type> is optional, as in almost all Pine Script™ variable declarations (see types)
<identifier> is the variableʼs name
<expression> can be a literal, a variable, an expression or a function call.
<local_block> consists of zero or more statements followed by a return value, which
can
be a tuple of values. It must be indented by four spaces or a tab.
The value assigned to the variable is the return value of the <local_block>, or na
if no
local block is executed.
The => <local_block> at the end allows you to specify a return value which acts as
a
default to be used when no other case in the structure is executed.
```
Only one local block of a switch structure is executed. It is thus a _structured
switch_ that
doesnʼt _fall through_ cases. Consequently, **break** statements are unnecessary.
Note that:
```
The expression we are switching on is the variable maType , which is of “input int”
type
(see here for an explanation of what the “input” qualifier is). Since it cannot
change
during the execution of the script, this guarantees that whichever MA type the user
selects will be executing on each bar, which is a requirement for functions like
ta.ema()
which require a “simple int” argument for their length parameter.
If no matching value is found for maType , the switch executes the last local block
introduced by => , which acts as a catch-all. We generate a runtime error in that
block.
We also end it with float(na) so the local block returns a value whose type is
compatible with that of the other local blocks in the structure, to avoid a
compilation
error.
```
## ## `switch` without an expression
Note that:
```
We are using the switch to select the appropriate strategy order to emit, depending
on
whether the longCondition or shortCondition “bool” variables are true.
The building conditions of longCondition and shortCondition are exclusive. While
they can both be false simultaneously, they cannot be true at the same time. The
fact that only one local block of the switch structure is ever executed is thus not
an
issue for us.
We evaluate the calls to ta.crossover() and ta.crossunder() prior to entry in the
switch
structure. Not doing so, as in the following example, would prevent the functions
to be
executed on each bar, which would result in a compiler warning and erratic
behavior:
```
## ## Matching local block type requirement
When multiple local blocks are used in structures, the type of the return value of
all its local
blocks must match. This applies only if the structure is used to assign a value to
a variable in a
declaration, because a variable can only have one type, and if the statement
returns two
incompatible types in its branches, the variable type cannot be properly
determined. If the
structure is not assigned anywhere, its branches can return different values.
This code compiles fine because close and open are both of the **float** type:
This code does not compile because the first local block returns a **float** value,
while the
second one returns a **string** , and the result of the **if** - statement is
assigned to the **x**
variable:
```
Previous
Variable declarations
```
```
Next
Loops
```
```
if <expression>
<local_block>
{else if <expression>
<local_block>}
[else
<local_block>]
```
```
Pine Script™
if (ta.crossover(source, lower))
strategy.entry("BBandLE", strategy.long, stop=lower,
oca_name="BollingerBands",
oca_type=strategy.oca.cancel, comment="BBandLE")
else
strategy.cancel(id="BBandLE")
```
```
Pine Script™
// @version= 6
indicator("", "", true)
var ourLabel = label.new(bar_index, na, na, color = color(na), textcolor =
color.ora
if barstate.islast
label.set_xy(ourLabel, bar_index + 2 , hl2[ 1 ])
label.set_text(ourLabel, str.tostring(bar_index + 1 , "# bars in chart"))
```
```
[<declaration_mode>] [<type>] <identifier> = if <expression>
<local_block>
{else if <expression>
<local_block>}
[else
<local_block>]
```
```
Pine Script™
// @version= 6
indicator("", "", true)
string barState = if barstate.islastconfirmedhistory
"islastconfirmedhistory"
else if barstate.isnew
"isnew"
else if barstate.isrealtime
"isrealtime"
else
"other"
f_print(_text) =>
var table _t = table.new(position.middle_right, 1 , 1 )
table.cell(_t, 0 , 0 , _text, bgcolor = color.yellow)
f_print(barState)
```
```
Pine Script™
x = if close > open
close
```
```
Pine Script™
if condition
if condition
if condition
expression
```
```
Pine Script™
if condition1 and condition2 and condition
expression
```
```
[[<declaration_mode>] [<type>] <identifier> = ]switch <expression>
{<expression> => <local_block>}
=> <local_block>
```
```
[[<declaration_mode>] [<type>] <identifier> = ]switch
{<expression> => <local_block>}
=> <local_block>
```
```
Pine Script™
// @version= 6
indicator("Switch using an expression", "", true)
string maType = input.string("EMA", "MA type", options = ["EMA", "SMA", "RMA",
"WMA"
int maLength = input.int( 10 , "MA length", minval = 2 )
float ma = switch maType
"EMA" => ta.ema(close, maLength)
"SMA" => ta.sma(close, maLength)
"RMA" => ta.rma(close, maLength)
"WMA" => ta.wma(close, maLength)
=>
runtime.error("No matching MA type found.")
float(na)
plot(ma)
```
```
Pine Script™
// @version= 6
strategy("Switch without an expression", "", true)
bool longCondition = ta.crossover( ta.sma(close, 14 ), ta.sma(close, 28 ))
bool shortCondition = ta.crossunder(ta.sma(close, 14 ), ta.sma(close, 28 ))
switch
longCondition => strategy.entry("Long ID", strategy.long)
shortCondition => strategy.entry("Short ID", strategy.short)
```
```
Pine Script™
// @version= 6
strategy("Switch without an expression", "", true)
switch
// Compiler warning! Will not calculate correctly!
ta.crossover( ta.sma(close, 14 ), ta.sma(close, 28 )) =>
strategy.entry("Long ID",
ta.crossunder(ta.sma(close, 14 ), ta.sma(close, 28 )) =>
strategy.entry("Short ID"
```
```
Pine Script™
x = if close > open
close
else
open
```
```
Pine Script™
// Compilation error!
x = if close > open
close
else
"open"
```
# Loops
## ## Introduction
Every loop structure in Pine Script™ consists of two main parts: a _loop header_
and a _loop
body_. The loop header determines the criteria under which the loop executes. The
loop body
is the indented block of code (local block) that the script executes on each loop
cycle
( _iteration_ ) as long as the headerʼs conditions remain valid. See the Common
characteristics
section to learn more.
Understanding when and how to use loops is essential for making the most of the
power of
Pine Script™. Inefficient or unnecessary usage of loops can lead to suboptimal
runtime
performance. However, effectively using loops when necessary enables scripts to
perform a
wide range of calculations that would otherwise be impractical or impossible
without them.
Pineʼs execution model and time series structure make loops _unnecessary_ in many
situations.
When a user adds a Pine script to a chart, it runs within the equivalent of a
_large loop_ ,
executing its code once on _every_ historical bar and realtime tick in the
available data. Scripts
can access the values from the executions on previous bars with the history-
referencing
operator, and calculated values can _persist_ across executions when assigned to
variables
declared with the var or varip keywords. These capabilities enable scripts to
utilize bar-by-bar
calculations to accomplish various tasks instead of relying on explicit loops.
Note that:
```
Users can see the substantial difference in efficiency between these two example
scripts by analyzing their performance with the Pine Profiler.
```
## ## When loops are necessary
Although Pineʼs execution model, time series, and available built-ins often
eliminate the need
for loops in many cases, not all iterative tasks have loop-free alternatives. Loops
_are
necessary_ for several types of tasks, including:
```
Iterating through or manipulating collections (arrays, matrices, and maps)
Performing calculations that one cannot accomplish with loop-free expressions or
the
available built-ins
Looking back through history to analyze past bars with a reference value only
available
on the current bar
```
For example, a loop is _necessary_ to identify which past barsʼ high values are
above the
current barʼs high because the current value is **not** obtainable during a
scriptʼs executions on
previous bars. The script can only access the current barʼs value while it executes
on that bar,
and it must _look back_ through the historical series during that execution to
compare the
previous values.
The script below uses a for loop to compare the high values of **lengthInput**
previous bars
with the last historical barʼs high. Within the loop, it calls label.new() to draw
a circular label
above each past bar that has a high value exceeding that of the last historical
bar:
Note that:
```
Each iteration of the for loop retrieves a previous barʼs high with the history-
referencing
operator [], using the loopʼs counter ( i ) as the historical offset. The
label.new() call
also uses the counter to determine each labelʼs x-coordinate.
The indicator declaration statement includes max_labels_count = 500 , meaning the
script can show up to 500 labels on the chart.
The script calls barcolor() to highlight the last historical chart bar, and it
draws a
horizontal line at that barʼs high for visual reference.
```
## ## Common characteristics
The for, while, and for...in loop statements all have similarities in their
structure, syntax, and
general behavior. Before we explore each specific loop type, letʼs familiarize
ourselves with
these characteristics.
In any loop statement, programmers define the criteria under which a script remains
in a loop
and performs _iterations_ , where an iteration refers to _one execution_ of the
code within the
loopʼs local block ( _body_ ). These criteria are part of the _loop header_. A
script evaluates the
headerʼs criteria _before_ each iteration, only allowing new iterations to occur
while they remain
valid. When the headerʼs criteria are no longer valid, the script _exits_ the loop
and skips over its
body.
The specific header syntax varies with each loop statement (for, while, or
for...in) because
each uses _distinct_ criteria to control its iterations. Effective use of loops
entails choosing the
structure with control criteria best suited for a scriptʼs required tasks. See the
`for` loops,
`while` loops, and `for...in` loops sections below for more information on each
loop statement
and its control criteria.
All loop statements in Pine Script™ follow the same general syntax:
Where:
```
loop_header refers to the loop structureʼs header statement, which defines the
criteria
that control its iterations.
statements refers to the code statements and expressions within the loopʼs body,
i.e.,
the indented block of code beneath the loop header. All statements within the body
belong to the loopʼs local scope.
continue and break are loop-specific keywords that control the flow of a loopʼs
iterations. The continue keyword instructs the script to skip the remainder of the
current loop iteration and continue to the next iteration. The break keyword
prompts
the script to stop the current iteration and exit the loop entirely. See this
section for
more information.
return_expression represents the last code line or block within the loopʼs body.
The
loop returns the results from this code after the final iteration. To use the
values from a
loopʼs return expression, assign the results to a variable or tuple.
var_declaration is an optional variable or tuple declaration. If a loop statement
includes a declared variable or tuple, the return_expression determines the
assigned
values. If the headerʼs conditions do not allow the loop to iterate, the assigned
values
are na.
```
## ## Scope
All code lines that a script will execute within a loop must have an indentation of
_four spaces_
or a _tab_ relative to the loopʼs header. The indented lines following the header
define the loopʼs
body. This code represents a _local block_ , meaning that all the definitions
within the body are
accessible only during the loopʼs execution. In other words, the code within the
loopʼs body is
part of its _local scope_.
Scripts can modify and reassign most variables from _outer_ scopes inside a loop.
However, any
variables declared within the loopʼs body strictly belong to that loopʼs local
scope. A script
**cannot** access a loopʼs declared variables _outside_ its local block.
Note that:
```
Variables declared within a loopʼs header are also part of the local scope. For
instance, a
script cannot use the counter variable in a for loop anywhere but within the loopʼs
local
block.
```
The body of any Pine loop statement can include conditional structures and _nested_
loop
statements. When a loop includes nested structures, each structure within the body
maintains
a _distinct_ local scope. For example, variables declared within an _outer_ loopʼs
scope are
accessible to an _inner_ loop. However, any variables declared within the inner
loopʼs scope are
**not** accessible to the outer loop.
The simple example below demonstrates how a loopʼs local scope works. This script
calls
label.new() within a for loop on the last historical bar to draw labels above
**lengthInput** past
bars. The color of each label depends on the **labelColor** variable declared
_within_ the loopʼs
local block, and each labelʼs location depends on the loop counter ( **i** ):
In the above code, the **i** and **labelColor** variables are only accessible to
the for loopʼs local
scope. They are **not** usable within any outer scopes. Here, we added a
label.new() call _after_
the loop with **bar_index - i** as the **x** argument and **labelColor** as the
**color** argument.
This code causes a _compilation error_ because neither **i** nor **labelColor** are
valid variables
in the outer scope:
Every loop in Pine Script™ implicitly _returns_ a result. The values a loop returns
come from the
_latest_ execution of the _last_ expression or nested structure within the loop
body as of the _final_
iteration. Loops return na values when no iterations occur. A loopʼs returned
results are _usable_
only if they are not of the void type and the script assigns a variable or tuple
declaration to the
loop statement. The declared variables hold the values from the return expression
for use in
additional calculations outside the loopʼs scope.
The values a loop returns may come from evaluating the last written expression or
nested
code block on the _final_ iteration. However, a loopʼs body can include
**continue** and **break**
keywords to control the flow of iterations beyond the criteria the loop header
specifies, which
can also affect the returned results. Programmers often include these keywords
within
conditional structures to control how iterations behave when certain conditions
occur.
The **continue** keyword instructs a script to _skip_ the remaining statements and
expressions in
the current loop iteration, re-evaluate the loop headerʼs criteria, and proceed to
the _next_
iteration. The script _exits_ the loop if the headerʼs criteria do not allow
another iteration.
The **break** keyword instructs a script to _stop_ the loop entirely and
immediately _exit_ at that
point without allowing any subsequent iterations. After breaking the loop, the
script skips any
remaining code within the loopʼs body and _does not_ re-evaluate the headerʼs
criteria.
If neither of the if statementʼs conditions occur, the script evaluates the _last
expression_ within
the loopʼs body (i.e., the return expression), which converts the current
**number** to a “string”
and concatenates the result with the **tempString** value. The loop returns the
_last evaluated
result_ from this expression after termination. The script assigns the returned
value to the
**finalLabelText** variable and uses that variable as the **text** argument in the
label.new() call:
Note that:
```
The label displays only odd numbers from the array because the script does not
reassign the tempString when the loop iterationʼs number is even. However, it does
not
include the last odd number from the array (15) because the loop stops when number
==
8 , preventing iteration over the remaining randomArray elements.
When the script exits the loop due to the break keyword, the loopʼs return value
becomes the last evaluated result from the tempString reassignment expression. In
this case, the last time that code executes is on the iteration where number == 9.
```
## ## `for` loops
The for loop statement creates a _count-controlled_ loop, which uses a _counter_
variable to
manage the executions of its local code block over a _defined number_ of
iterations.
```
counter represents the variable whose value the loop increments after each
iteration.
from_num determines the counter variableʼs initial value , i.e., the value on the
first
iteration.
to_num determines the counter variableʼs final value , i.e., the maximum value of
the
loop counter that the header allows a new iteration for. The loop increments the
counter value by a fixed amount until it reaches or passes this value.
step_num is the amount by which the counter value increases or decreases after each
iteration until it reaches or passes the to_num. Specifying this value is optional.
The
default value is 1.
```
See the Common characteristics section above for detailed information about the
**var_declaration** , **statements** , **continue** , **break** , and
**return_expression** parts of the
loopʼs syntax.
This simple script demonstrates a for loop that draws several labels at future bar
indices
during its execution on the last historical chart bar:
Note that:
```
The i variable represents the loopʼs counter. This variable is local to the loopʼs
scope,
meaning no outer scopes can access it. The code uses the variable within the loopʼs
body to determine the location and text of each label drawing.
Programmers often use i , j , and k as loop counter identifiers. However, any valid
variable name is allowed. For example, this code would behave the same if we named
the counter level instead of i.
The for loop structure automatically manages the counter variable. We do not need
to
define code in the loopʼs body to increment its value.
The loopʼs header specifies that the counter starts at 0 when the script enters the
loop,
and its value increases by 1 after each iteration until it reaches 10, at which
point the last
iteration occurs. In other words, the loop executes 11 iterations in total.
```
A for loopʼs local block repeatedly executes for the number of times required for
its counter
variableʼs value to reach the specified **to_num** boundary. The counterʼs defined
boundaries
and step size directly control the number of loop iterations. Therefore, a script
establishes the
expected number of iterations _before_ the loop starts. As such, for loops are best
suited for
iterative tasks where the maximum number of iterations is knowable _in advance_.
The example below calculates and plots the volume-weighted moving average (VWMA) of
open prices across **maLengthInput** chart bars, and it analyzes the differences
between the
current **vwmaOpen** and past **vwmaOpen** values within a for loop on the last
historical chart bar.
On each loop iteration, the script retrieves a previous barʼs **vwmaOpen** value,
takes the
difference between that value and the current **vwmaOpen** , and displays the
result in a label at
the corresponding historical barʼs opening price:
Note that:
```
The script uses the loopʼs counter ( i ) to within the history-referencing operator
to
retrieve past values of the vwmaOpen series. It also uses the counter to determine
the
location of each label drawing.
The loop counterʼs value starts at the lookbackInput and decreases by 1 until i ==
1.
```
Programmers can use for loops to iterate through collections, such as arrays and
matrices.
The loopʼs counter can serve as an _index_ for retrieving or modifying a
collectionʼs contents.
For example, this code block uses array.get() inside a for loop to successively
retrieve
elements from an array:
Note that:
```
Array indexing starts from 0, but the array.size() function counts the elements
(i.e.,
starting from 1). Therefore, we have to subtract one from the arrayʼs size to get
the
maximum index value. This way, the loop counter avoids representing an array index
that
is out of bounds on the last loop iteration.
The for...in loop statement is often the preferred way to loop through collections.
However, programmers may prefer a for loop for some tasks, such as looping through
stepped index values, iterating over a collectionʼs contents in reverse or a
nonlinear
order, and more. See the Looping through arrays and Looping through matrices
sections
to learn more about the best practices for looping through these collection types.
```
The script below calculates the RSI and momentum of close prices over three
different
lengths (10, 20, and 50) and displays their values within a table on the last chart
bar. It stores
“string” values for the header title within arrays and the “float” values of the
calculated
indicators within a 2 x 3 matrix. The script uses a for loop to access the elements
in the arrays
and initialize the **displayTable** header cells. It then uses _nested_ for loops
to iterate over the
_row_ and _column_ indices in the **taMatrix** to access its elements, convert
their values to
strings, and populate the remaining table cells:
Note that:
```
Both arrays of header names ( sideHeaderTitles and topHeaderTitles ) contain the
same number of elements, which allows us to iterate through their contents
simultaneously using a single for loop.
The nested for loops iterate over all the index values in the taMatrix. The outer
loop
iterates over each row index, and the inner loop iterates over every column index
on
each outer loop iteration.
The script creates and displays the table only on the last historical bar and all
realtime
bars because the historical states of tables are never visible. See this section of
the
Profiling and optimization page for more information.
```
## ## `while` loops
See the Common characteristics section above for detailed information about the
**var_declaration** , **statements** , **continue** , **break** , and
**return_expression** parts of the
loopʼs syntax.
Depending on the specified condition in the loop header, a while loop can behave
similarly to
a for loop, continuing iteration until a _counter_ variable reaches a specified
limit. For example,
the following script uses a for loop and while loop to perform the same task. Both
loops draw
a label displaying their respective counter value on each iteration:
Note that:
```
When a while loop uses count-based logic, it must explicitly manage the user-
specified
counter within the local block. In contrast, a for loop increments its counter
automatically.
The script declares the variable the while loop uses as a counter outside the
loopʼs
scope, meaning its value is usable in additional calculations after the loop
terminates.
If this code did not increment the j variable within the while loopʼs body, the
value
would never reach 10, meaning the loop would run indefinitely until causing a
runtime
error.
```
Because a while loopʼs execution depends on its condition remaining **true** and
the condition
may not change on a specific iteration, the _precise_ number of expected iterations
may **not** be
knowable _before_ the loop begins, unlike a for loop. Therefore, while loops are
advantageous in
scenarios where the exact loop boundaries are _unknown_.
The script below tracks when the chartʼs close crosses outside Keltner Channels
with a user-
specified length and channel width. When the price crosses outside the current
barʼs channel,
the script draws a box highlighting all the previous _consecutive_ bars with close
values within
that price window. The script uses a while loop to analyze past barsʼ prices and
incrementally
adjust the left side of each new box until the drawing covers all the latest
consecutive bars in
the current range.
A while loop is necessary in this case because the current barʼs channel values are
_not_
knowable in advance, and we **cannot** predict the precise number of iterations
required to
encapsulate all the consecutive bars within the channelʼs range:
Note that:
```
The left and right edges of boxes sit within the horizontal center of their
respective bars,
meaning that each drawing spans from the middle of the first consecutive bar to the
middle of the last bar within each window.
This script uses the i variable as a history-referencing index within the
conditional
expression the while loop checks on each iteration. The variable does not behave as
a
loop counter, as the iteration boundaries are unknown. The loop executes its local
block
repeatedly until the condition becomes false.
```
## ## `for...in` loops
The for...in loop statement creates a _collection-controlled_ loop, which uses the
_contents_ of a
collection to control its iterations. This loop structure is often the preferred
approach for
looping through arrays, matrices, and maps.
A for...in loop traverses a collection _in order_ , retrieving one of its stored
items on each
iteration. Therefore, the loopʼs boundaries depend directly on the number of
_items_ (array
_elements_ , matrix _rows_ , or map _key-value pairs_ ). As such, similar to a for
loop, a script
establishes the expected number of for...in loop iterations _before_ the loop
starts.
Pine Script™ features _two_ general forms of the for...in loop statement. The
_first form_ uses the
following syntax:
Where **item** is a _variable_ that holds sequential values from the specified
**collection_id**.
The variableʼs value starts with the collectionʼs _first item_ and takes on
successive items in
order after each iteration. This form is convenient when a script must access
values from an
array or matrix iteratively but does not require the itemʼs index in its
calculations.
The _second form_ has a slightly different syntax that includes a tuple in its
_header_ :
Where **index** is a variable that contains the _index_ or _key_ of the retrieved
**item**. This form is
convenient when a task requires using a collectionʼs items _and_ their indices in
iterative
calculations. This form of the for...in loop is required when directly iterating
through the
contents of a map. See this section for more information.
See the Common characteristics section above for detailed information about the
**var_declaration** , **statements** , **continue** , **break** , and
**return_expression** parts of the
loopʼs syntax.
The iterative behavior of a for...in loop depends on the _type_ of collection the
header specifies
as the **collection_id** :
```
When using an array in the header, the loop performs element-wise iteration,
meaning
the retrieved item on each iteration is one of the arrayʼs elements.
When using a matrix in the header, the loop performs row-wise iteration, which
means
that each item represents a row array.
When using a map in the header, the loop performs pair-wise iteration, which
retrieves a
key and corresponding value on each iteration.
```
## ## Looping through arrays
Pine scripts can iterate over the elements of arrays using any loop structure.
However, the
for...in loop is typically the most convenient because it automatically verifies
the size of an
array when controlling iterations. With other loop structures, programmers must
carefully set
the headerʼs boundaries or conditions to _prevent_ the loop from attempting to
access an
element at a _nonexistent_ index.
For example, one can use a for loop to access an arrayʼs elements using the counter
variable
as the lookup index in functions such as array.get(). However, to prevent out-of-
bounds
errors, programmers must ensure the counter always represents a _valid index_.
Additionally, if
an array might be _empty_ , programmers must set conditions to prevent the loopʼs
execution
entirely.
The code below shows a for loop whose counter boundaries depend on the number of
elements in an array. If the array is empty, containing 0 elements, the headerʼs
final counter
value is na, which _prevents_ iteration. Otherwise, the final value is _one less_
than the arrayʼs size
(i.e., the index of the last element):
The following example examines bars on a lower timeframe to gauge the strength of
_intrabar_
trends within each chart bar. The script uses a request.security_lower_tf() call to
retrieve an
array of intrabar hl 2 prices from a calculated **lowerTimeframe**. Then, it uses a
for...in loop to
access each **price** within the **intrabarPrices** array and compare the value to
the current
close to calculate the barʼs **strength**. The script plots the **strength** as
columns in a
separate pane:
The second form of the for...in loop is a convenient solution when a scriptʼs
calculations
require accessing each element _and_ corresponding index within an array:
For example, suppose we want to display a _numerated_ list of array elements within
a label
while excluding values at specific indices. We can use the second form of the
for...in loop
structure to accomplish this task. The simple script below declares a
**stringArray** variable
that references an array of predefined “string” values. On the last historical bar,
the script
uses a for...in loop to access each **index** and **element** in the
**stringArray** to construct the
**labelText** , which it uses in a label.new() call after the loop ends:
Note that:
```
This example adds 1 to the index in the str.tostring() call to start the numerated
list
with a value of “1”, as array indexing always begins at 0.
On the third loop iteration, when index == 2 , the script adds an “ELEMENT SKIPPED”
message to the labelText instead of the retrieved element and uses the continue
keyword to skip the remainder of the iteration. See this section above to learn
more
about loop keywords.
```
Letʼs explore an advanced example demonstrating the utility of for...in loops. The
following
indicator draws a fixed number of horizontal lines at calculated pivot high levels,
and it
analyzes the lines within a loop to determine which ones represent active
( _uncrossed_ ) pivots.
Each time the script detects a new pivot high point, it creates a new line,
_inserts_ that line at
the beginning of the **pivotLines** array, then removes the oldest element and
deletes its ID.
The script accesses each line within the array using a for...in loop, analyzing and
modifying
the properties of the **pivotLine** retrieved on each iteration. When the current
high crosses
above the **pivotLine** , the script changes its style to signify that it is no
longer an active level.
Otherwise, it extends the lineʼs **x2** coordinate and uses its price to calculate
the average
_active_ pivot value. The script also plots each pivot high value and the average
active pivot for
visual reference:
Note that:
```
The loop in this example executes on every bar because it has to compare each
active
pivotLine ʼs price with the current high value, and it uses the prices to calculate
the
avgActivePivot on each bar.
Pine Script™ features several ways to calculate averages, many of which do not
require
a loop. However, a loop is necessary in this example because the script uses
information
only available on the current bar to determine which prices contribute toward the
average.
The first form of the for...in loop is the most convenient option in this example
because
we need direct access to the lines within the pivotLines array, but we do not need
the
corresponding index values.
```
## ## Looping through matrices
Pine scripts can iterate over the contents of a matrix in several different ways.
Unlike arrays,
matrices use _two_ indices to reference their elements because they store data in a
_rectangular_
format. The first index refers to _rows_ , and the second refers to _columns_. If a
programmer opts
to use for or while loops to iterate through matrices instead of using for...in,
they must
carefully define the loop boundaries or conditions to avoid out-of-bounds errors.
This code block shows a for loop that performs _row-wise_ iteration, looping
through each _row
index_ in a matrix and using the value in a matrix.row() call to retrieve a row
array. If the matrix
is empty, the loop statement uses a final loop counter value of na to _prevent_
iteration.
Otherwise, the final counter is _one less_ than the row count, which represents the
_last_ row
index:
Note that:
```
If we replace the matrix.rows() and matrix.row() calls with matrix.columns() and
matrix.col(), the loop performs column-wise iteration instead.
```
The for...in loop statement is the more convenient approach to loop over and access
the rows
of a matrix in order, as it automatically validates the number of rows and
retrieves an array of
the current rowʼs elements on each iteration:
When a scriptʼs calculations require access to each row from a matrix and its
corresponding
_index_ , programmers can use the second form of the for...in loop:
Note that:
```
The for...in loop only performs row-wise iteration on matrices. To emulate column-
wise
iteration, programmers can use a for...in loop on a transposed copy.
```
The following example displays a custom “string” representing the rows of a matrix
with extra
information, which it displays within a label. When the script executes on the last
historical
bar, it creates a 3 x 3 **randomMatrix** populated with random values. Then, using
the first form
of the for...in loop, the script iterates through each **row** in the
**randomMatrix** to create a
“string” representing the rowʼs contents, its average, and whether the average is
above 0.5,
and it concatenates that “string” with the **labelText**. After the loop ends, the
script creates a
label displaying the **labelText** value:
Working with matrices often entails iteratively accessing their _elements_ , not
just their rows
and columns, typically using _nested loops_. For example, this code block uses an
outer for
loop to iterate over row indices. The inner for loop iterates over column indices
on _each_ outer
loop iteration and calls matrix.get() to access an element:
Alternatively, a more convenient approach for this type of task is to use nested
for...in loops.
The outer for...in loop in this code block retrieves each row array in a matrix,
and the inner
for...in statement loops through that array:
The script below creates a 3 x 2 matrix, then accesses and modifies its elements
within nested
for...in loops. Both loops use the second form of the for...in statement to
retrieve index values
and corresponding items. The outer loop accesses a row index and row array from the
matrix.
The inner loop accesses each index and respective element from that array.
Within the nested loopʼs iterations, the script converts each **element** to a
“string” and
initializes a table cell at the **rowIndex** row and **colIndex** column. Then, it
uses the loop
header variables within matrix.set() to update the matrix element. After the outer
loop
terminates, the script displays a “string” representation of the _updated_ matrix
within a label:
The for...in loop statement is the primary, most convenient approach for iterating
over the data
within Pine Script™ maps.
Unlike arrays and matrices, maps are _unordered collections_ that store data in
_key-value pairs_.
Rather than traversing an internal lookup index, a script references the _keys_
from the pairs
within a map to access its _values_. Therefore, when looping through a map, scripts
must
perform _pair-wise_ iteration, which entails retrieving key-value pairs across
iterations rather
than indexed elements or rows.
Note that:
```
Although maps are unordered collections, Pine Script™ internally tracks the
insertion
order of their key-value pairs.
```
One way to access the data from a map is to use the map.keys() function, which
returns an
array containing all the _keys_ from the map, sorted in their insertion order. A
script can use the
for...in structure to loop through the array of keys and call map.get() to retrieve
corresponding
values:
Note that:
```
The second form of the for...in loop is the only way to iterate directly through a
map. A
script cannot directly loop through this collection type without retrieving a key
and value
on each iteration.
```
Letʼs consider a simple example demonstrating how a for...in loop works on a map.
When the
script below executes on the last historical bar, it declares a **simpleMap**
variable with an
assigned map of “string” keys and “float” values. The script puts the keys from the
**newKeys**
array into the collection with corresponding random values. It then uses a for...in
loop to
iterate through the key-value pairs from the **simpleMap** and construct the
**displayText**.
After the loop ends, the script shows the **displayText** within a label to
visualize the result:
Note that:
```
This script utilizes both forms of the for...in loop statement. The first loop
iterates
through the “string” elements of the newKeys array to put key-value pairs into the
simpleMap , and the second iterates directly through the mapʼs key-value pairs to
construct the custom displayText.
```
```
Previous
Conditional structures
```
```
Next
Type system
```
```
Pine Script™
// @version= 6
indicator("Unnecessary loops demo", overlay = true)
// @variable The number of bars in the calculation window.
int lengthInput = input.int(defval = 20 , title = "Length")
// @variable The sum of `close` values over `lengthInput` bars.
float closeSum =
// Loop over the most recent `lengthInput` bars, adding each bar's `close` to the `
c
for i = 0 to lengthInput -
closeSum += close[i]
// @variable The average `close` value over `lengthInput` bars.
float avgClose = closeSum / lengthInput
// Plot the `avgClose`.
plot(avgClose, "Average close", color.orange, 2 )
```
```
Pine Script™
// @version= 6
indicator("Unnecessary loops corrected demo", overlay = true)
// @variable The number of bars in the calculation window.
int lengthInput = input.int(defval = 20 , title = "Length")
// @variable The average `close` value over `lengthInput` bars.
float avgClose = ta.sma(close, lengthInput)
// Plot the `avgClose`.
plot(avgClose, "Average close", color.blue, 2 )
```
```
Pine Script™
// @version= 6
indicator("Necessary loop demo", overlay = true, max_labels_count = 500 )
// @variable
The number of previous `high` values to compare to the last historical b
int lengthInput = input.int( 20 , "Length", 1 , 500 )
if barstate.islastconfirmedhistory
// Draw a horizontal line segment at the last historical bar's `high` to visuali
line.new(bar_index - lengthInput, high, bar_index, high, color = color.gray, sty
// Create a `for` loop that counts from 1 to `lengthInput`.
for i = 1 to lengthInput
// Draw a circular `label` above the bar from `i` bars ago if that bar's `hi
if high[i] > high
label.new(
bar_index - i, na, "", yloc = yloc.abovebar, color = color.purple,
style = label.style_circle, size = size.tiny
)
// Highlight the last historical bar.
barcolor(barstate.islastconfirmedhistory? color.orange : na, title = "Last historic
```
```
[var_declaration =] loop_header
statements | continue | break
return_expression
```
```
Pine Script™
// @version= 6
indicator("Loop scope demo", overlay = true)
// @variable The number of bars in the calculation.
int lengthInput = input.int( 20 , "Lookback length", 1 )
if barstate.islastconfirmedhistory
for i = 1 to lengthInput
// @variable Has a value of `color.blue` if `close[i]` is above the current `
// This variable is LOCAL to the `for` loop's scope.
color labelColor = close[i] > close? color.blue : color.orange
// Display a colored `label` on the historical `high` from `i` bars back, us
label.new(bar_index - i, high[i], "", color = labelColor, size = size.normal
```
```
Pine Script™
// @version= 6
indicator("Loop scope demo", overlay = true)
// @variable The number of bars in the calculation.
int lengthInput = input.int( 20 , "Lookback length", 1 )
if barstate.islastconfirmedhistory
for i = 1 to lengthInput
// @variable Has a value of `color.blue` if `close[i]` is above the current `
// This variable is LOCAL to the `for` loop's scope.
color labelColor = close[i] > close? color.blue : color.orange
// Display a colored `label` on the historical `high` from `i` bars back, us
label.new(bar_index - i, high[i], "", color = labelColor, size = size.normal
// Call `label.new()` to using the `i` and `labelColor` variables outside the lo
// This code causes a compilation error because these variables are not accessib
label.new(
bar_index - i, low, "Scope test", textcolor = color.white, color = labelCol
)
```
```
Pine Script™
// @version= 6
indicator("Loop keywords and variable assignment demo")
// @variable
An `array` of arbitrary "int" values to selectively convert to "string"
var array < int > randomArray = array.from( 1 , 5 , 2 , -3, 14 , 7 , 9 , 8 ,
15 , 12 )
// Label creation logic.
if barstate.islastconfirmedhistory
// @variable A "string" containing representations of selected values from the `r
string tempString = ""
// @variable The final text to display in the `label`. The `for..in` loop returns
string finalLabelText = for number in randomArray
// Stop the current iteration and exit the loop if the `number` from the `ra
if number == 8
break
// Skip the rest of the current iteration and proceed to the next iteration
else if number % 2 == 0
continue
// Convert the `number` to a "string", append ", ", and concatenate the resu
// This code represents the loop's return expression.
tempString += str.tostring(number) + ", "
// Display the value of the `finalLabelText` within a `label` on the current bar
label.new(bar_index, 0 , finalLabelText, color = color.blue, textcolor = color.wh
```
```
[var_declaration =] for counter = from_num to to_num [by step_num]
statements | continue | break
return_expression
```
```
Pine Script™
// @version= 6
indicator("Simple `for` loop demo")
if barstate.islastconfirmedhistory
// Define a `for` loop that iterates from `i == 0` to `i == 10`.
for i = 0 to
// Draw a new `label` on the current bar at the `i` level.
label.new(bar_index + i, 0 , str.tostring(i), textcolor = color.white, size =
```
```
Pine Script™
// @version= 6
indicator("`for` loop demo", "VWMA differences", true, max_labels_count = 500 )
// @variable Display color for indicator visuals.
const color DISPLAY_COLOR = color.rgb( 17 , 127 , 218 )
// @variable The number of bars in the `vwmaOpen` calculation.
int maLengthInput = input.int( 20 , "VWMA length", 1 )
// @variable
The number of past bars to look back through and compare to the current
int lookbackInput = input.int( 15 , "Lookback length", 1 , 500 )
// @variable The volume-
weighted moving average of `open` values over `maLengthInput`
float vwmaOpen = ta.vwma(open, maLengthInput)
//On last bar, loop through `lengthInput` most recent historical bars and output `l
a
if barstate.islastconfirmedhistory
for i = lookbackInput to
// @variable The difference between the `vwmaOpen` from `i` bars ago and the
float vwmaDifference = vwmaOpen[i] - vwmaOpen
// @variable A "string" representation of the `vwmaDifference` with sign spec
string displayText = (vwmaDifference > 0 ? "+" : "") + str.tostring(vwmaDiff
// Draw a `label` showing the `displayText` at the `open` of the bar from `i
label.new(
bar_index - i, open[i], displayText, textcolor = color.white, color = D
style = label.style_label_lower_right, size = size.normal
)
// Plot the `vwmaOpen`.
plot(vwmaOpen, "VWMA", color = DISPLAY_COLOR, linewidth = 2 )
```
```
Pine Script™
for i = 0 to array.size(myArray) – 1
element = array.get(i)
```
```
Pine Script™
// @version= 6
indicator("`for` loop with collections demo", "Table of TA Indexes", overlay =
true)
// Calculate the RSI and momentum of `close` values with constant lengths of 10, 20
,
float rsi10 = ta.rsi(close, 10 )
float rsi20 = ta.rsi(close, 20 )
float rsi50 = ta.rsi(close, 50 )
float mom10 = ta.mom(close, 10 )
float mom20 = ta.mom(close, 20 )
float mom50 = ta.mom(close, 50 )
if barstate.islast
// @variable A `table` that displays indicator values in the top-right corner of
var table displayTable = table.new(
position.top_right, columns = 5 , rows = 4 , border_color = color.black, bord
)
// @variable An array containing the "string" titles to display within the side h
array < string > sideHeaderTitles = array.from("TA Index", "RSI", "Momentum")
// @variable An array containing the "string" titles to representing the length o
array < string > topHeaderTitles = array.from("10", "20", "50")
// @variable A matrix containing the values to display within the table.
matrix < float > taMatrix = matrix.new< float >()
// Populate the `taMatrix` with indicator values. The first row contains RSI dat
taMatrix.add_row( 0 , array.from(rsi10, rsi20, rsi50, mom10, mom20, mom50))
taMatrix.reshape( 2 , 3 )
// Initialize top header cells.
displayTable.cell( 1 , 0 , "Bars Length", text_color = color.white, bgcolor =
color
displayTable.merge_cells( 1 , 0 , 3 , 0 )
// Initialize additional header cells within a `for` loop.
for i = 0 to
displayTable.cell( 0 , i + 1 , sideHeaderTitles.get(i), text_color = color.whit
displayTable.cell(i + 1 , 1 , topHeaderTitles.get(i), text_color = color.white
// Use nested `for` loops to iterate through the row and column indices of the `
for i = 0 to taMatrix.rows() - 1
for j = 0 to taMatrix.columns() -
// @variable The value stored in the `taMatrix` at the `i` row and `j` co
float elementValue = taMatrix.get(i, j)
// Initialize a cell in the `displayTable` at the `i + 2` row and `j +
// representation of the `elementValue`.
displayTable.cell(
column = j + 1 , row = i + 2 , text = str.tostring(elementValue, "#.#
)
```
```
[var_declaration =] while condition
statements | continue | break
return_expression
```
```
Pine Script™
// @version= 6
indicator("`while` loop with a counter condition demo")
if barstate.islastconfirmedhistory
// A `for` loop that creates blue labels displaying each `i` value.
for i = 0 to
label.new(
bar_index + i, 0 , str.tostring(i), color = color.blue, textcolor = colo
size = size.large, style = label.style_label_down
)
// @variable An "int" to use as a counter within a `while` loop.
int j =
// A `while` loop that creates orange labels displaying each `j` value.
while j <=
label.new(
bar_index + j, 0 , str.tostring(j), color = color.orange, textcolor = co
size = size.large, style = label.style_label_up
)
// Update the `j` counter within the local block.
j +=
```
```
Pine Script™
// @version= 6
indicator("`while` loop demo", "Price window boxes", true)
// @variable The length of the channel.
int lengthInput = input.int( 20 , "Channel length", 1 , 4999 )
// @variable The width multiplier of the channel.
float widthInput = input.float(2.0, "Width multiplier", 0 )
// @variable The `lengthInput`-bar EMA of `close` prices.
float ma = ta.ema(close, lengthInput)
// @variable The `lengthInput`-bar ATR, multiplied by the `widthInput`.
float atr = ta.atr(lengthInput) * widthInput
// @variable The lower bound of the channel.
float channelLow = ma - atr
// @variable The upper bound of the channel.
float channelHigh = ma + atr
// @variable
Is `true` when the `close` price is outside the current channel range, `
bool priceOutsideChannel = close < channelLow or close > channelHigh
// Check if the `close` crossed outside the channel range, then analyze the past ba
r
if priceOutsideChannel and not priceOutsideChannel[ 1 ]
// @variable A box that highlights consecutive past bars within the current chann
box windowBox = box.new(
bar_index, channelHigh, bar_index, channelLow, border_width = 2 , bgcolor =
)
// @variable The lookback index for box adjustment. The `while` loop increments t
int i =
// Use a `while` loop to look backward through close` prices. The loop iterates
// from `i` bars ago is between the current bar's `channelLow` and `channelHigh`
while close[i] >= channelLow and close[i] <= channelHigh
// Adjust the left side of the box.
windowBox.set_left(bar_index - i)
// Add 1 to the `i` value to check the `close` from the next bar back on the
i +=
// Plot the `channelLow` and `channelHigh` for visual reference.
plot(channelLow, "Channel low")
plot(channelHigh, "Channel high")
```
```
[var_declaration =] for item in collection_id
statements | continue | break
return_expression
```
```
[var_declaration =] for [index, item] in collection_id
statements | continue | break
return_expression
```
```
Pine Script™
for index = 0 to (array.size(myArray) == 0 ? na : array.size(myArray) - 1 )
element = array.get(myArray, index)
```
```
Pine Script™
for element in myArray
```
```
Pine Script™
// @version= 6
indicator("`for element in array` demo", "Intrabar strength")
// @variable A valid timeframe closest to one-
tenth of the current chart's timeframe,
var string lowerTimeframe =
timeframe.from_seconds(math.max(int(timeframe.in_seconds
// @variable
An array of intrabar `hl2` prices calculated from the `lowerTimeframe`.
array < float > intrabarPrices = request.security_lower_tf("", lowerTimeframe, hl2)
// @variable The excess trend strength of `intrabarPrices`.
float strength = 0.
// Loop directly through the `intrabarPrices` array. Each iteration's `price` repre
s
for price in intrabarPrices
// Subtract 1 from the `strength` if the retrieved `price` is above the current
if price > close
strength -=
// Add 1 to the `strength` if the retrieved `price` is below the current bar's `
else if price < close
strength +=
// @variable
Is `color.teal` when the `strength` is positive, `color.maroon` otherwis
color strengthColor = strength > 0 ? color.teal : color.maroon
// Plot the `strength` as columns colored by the `strengthColor`.
plot(strength, "Intrabar strength", strengthColor, 1 , plot.style_columns)
```
```
Pine Script™
for [index, element] in myArray
```
```
Pine Script™
// @version= 6
indicator("`for [index, item] in array` demo", "Array numerated output")
// @variable An array of "string" values to display as a numerated list.
var array < string > stringArray = array.from("First", "Second", "Third",
"Before Last"
if barstate.islastconfirmedhistory
// @variable A "string" modified within a loop to display within the `label`.
string labelText = "Array values: \n"
// Loop through the `stringArray`, accessing each `index` and corresponding `ele
for [index, element] in stringArray
// Skip the third `element` (at `index == 2`) in the `labelText`. Include an
if index ==
labelText += "-- ELEMENT SKIPPED -- \n"
continue
labelText += str.tostring(index + 1 ) + ": " + element + "\n"
// Display the `labelText` within a `label`.
label.new(
bar_index, 0 , labelText, textcolor = color.white, size = size.huge,
style = label.style_label_center, textalign = text.align_left
)
```
```
Pine Script™
// @version= 6
indicator("`for...in` loop with arrays demo", "Active high pivots", true,
max_lines_
// @variable
The number of bars required on the left and right to confirm a pivot poi
int pivotBarsInput = input.int( 5 , "Pivot leg length", 1 )
// @variable
The number of recent pivot lines to analyze. Controls the size of the `p
int maxRecentLines = input.int( 20 , "Maximum recent lines", 1 , 500 )
// @variable
An array that acts as a queue holding the most recent pivot high lines.
var array < line > pivotLines = array.new< line >(maxRecentLines)
// @variable The pivot high price, or `na` if no pivot is found.
float highPivotPrice = ta.pivothigh(pivotBarsInput, pivotBarsInput)
// @variable The average active `highPivotPrice`.
float avgHiPivot = ta.sma(highPivotPrice, maxRecentLines)
if not na(highPivotPrice)
// @variable The `chart.point` for the start of the line. Does not contain `time`
firstPoint = chart.point.from_index(bar_index - pivotBarsInput, highPivotPrice)
// @variable The `chart.point` for the end of each line. Does not contain `time`
secondPoint = chart.point.from_index(bar_index, highPivotPrice)
// @variable A horizontal line at the new pivot level.
line hiPivotLine = line.new(firstPoint, secondPoint, width = 2 , color = color.gr
// Insert the `hiPivotLine` at the beginning of the `pivotLines` array.
pivotLines.unshift(hiPivotLine)
// Remove the oldest line from the array and delete its ID.
line.delete(pivotLines.pop())
// @variable The sum of active pivot prices.
float activePivotSum = 0.
// @variable The number of active pivot high levels.
int numActivePivots =
// Loop through the `pivotLines` array, directly accessing each `pivotLine` element
.
for pivotLine in pivotLines
// @variable The `x2` coordinate of the `pivotline`.
int lineEnd = pivotLine.get_x2()
// Move to the next `pivotline` in the array if the current line is inactive.
if pivotLine.get_x2() < bar_index -
continue
// @variable The price value of the `pivotLine`.
float pivotPrice = pivotLine.get_price(bar_index)
// Change the style of the `pivotLine` and stop extending its display if the `hi
if high > pivotPrice
pivotLine.set_color(color.maroon)
pivotLine.set_style(line.style_dotted)
pivotLine.set_width( 1 )
continue
// Extend the `pivotLine` and add the `pivotPrice` to the `activePivotSum` when
pivotLine.set_x2(bar_index)
activePivotSum += pivotPrice
numActivePivots +=
// @variable The average active pivot high value.
float avgActivePivot = activePivotSum / numActivePivots
// Plot crosses at the `highPivotPrice`, offset backward by the `pivotBarsInput`.
plot(highPivotPrice, "High pivot marker", color.green, 3 , plot.style_cross,
offset =
// Plot the `avgActivePivot` as a line with breaks.
plot(avgActivePivot, "Avg. active pivot", color.orange, 3 , plot.style_linebr)
```
```
Pine Script™
for rowIndex = 0 to (myMatrix.rows() == 0 ? na : myMatrix.rows() - 1 )
rowArray = myMatrix.row(rowIndex)
```
```
Pine Script™
for rowArray in myMatrix
```
```
Pine Script™
for [rowIndex, rowArray] in myMatrix
```
```
Pine Script™
// @version= 6
indicator("`for row in matrix` demo", "Custom matrix label")
// @variable
Generates a random value between 0 and 1, rounded to 4 decimal places.
rand() =>
math.round(math.random(), 4 )
if barstate.islastconfirmedhistory
// @variable A matrix of randomized values to format and display in a `label`.
matrix < float > randomMatrix = matrix.new< float >()
// Add a row of 9 randomized values and reshape the matrix to 3x3.
randomMatrix.add_row(
0 , array.from(rand(), rand(), rand(), rand(), rand(), rand(), rand(),
rand(
)
randomMatrix.reshape( 3 , 3 )
// @variable A custom "string" representation of `randomMatrix` information. Modi
string labelText = "Matrix rows: \n"
// Loop through the rows in the `randomMatrix`.
for row in randomMatrix
// @variable The average element value within the `row`.
float rowAvg = row.avg()
// @variable An upward arrow when the `rowAvg` is above 0.5, a downward arrow
string directionChar = rowAvg > 0.5? "⬆" : "⬇"
// Add a "string" representing the `row` array, its average, and the `direct
labelText += str.format("Row: {0} Avg: {1} {2}\n", row, rowAvg, directionCha
// Draw a `label` displaying the `labelText` on the current bar.
label.new(
bar_index, 0 , labelText, color = color.purple, textcolor = color.white, siz
style = label.style_label_center, textalign = text.align_left
)
```
```
Pine Script™
for rowIndex = 0 to (myMatrix.rows() == 0 ? na : myMatrix.rows() - 1 )
for columnIndex = 0 to myMatrix.columns() – 1
element = myMatrix.get(rowIndex, columnIndex)
```
```
Pine Script™
for rowArray in myMatrix
for element in rowArray
```
```
Pine Script™
// @version= 6
indicator("Nested `for...in` loops on matrices demo")
if barstate.islastconfirmedhistory
// @variable A matrix containing numbers to display.
matrix < float > displayNumbers = matrix.new< float >()
// Populate the `displayNumbers` matrix and reshape to 3x2.
displayNumbers.add_row( 0 , array.from( 1 , 2 , 3 , 4 , 5 , 6 ))
displayNumbers.reshape( 3 , 2 )
// @variable A table that displays the elements of the `displayNumbers` before mo
table displayTable = table.new(
position = position.middle_center, columns = displayNumbers.columns(), rows
bgcolor = color.purple, border_color = color.white, border_width =
)
// Loop through the `displayNumbers`, retrieving the `rowIndex` and the current
for [rowIndex, row] in displayNumbers
// Loop through the current `row` on each outer loop iteration to retrieve t
for [colIndex, element] in row
// Initialize a table cell at the `rowIndex` row and `colIndex` column d
displayTable.cell(column = colIndex, row = rowIndex, text = str.tostring
text_color = color.white, text_size = size.huge
)
// Update the `displayNumbers` value at the `rowIndex` and `colIndex`.
displayNumbers.set(rowIndex, colIndex, math.round(math.exp(element), 3 ))
// Draw a `label` to display a "string" representation of the updated `displayNu
label.new(
x = bar_index, y = 0 , text = "Matrix now modified: \n" + str.tostring(displ
textcolor = color.white, size = size.huge, style = label.style_label_up
)
```
```
Pine Script™
for key in myMap.keys()
value = myMap.get(key)
```
```
Pine Script™
for [key, value] in myMap
```
```
Pine Script™
// @version= 6
indicator("Looping through map demo")
if barstate.islastconfirmedhistory
// @variable A map of "string" keys and "float" values to render within a `label`
map < string , float > simpleMap = map.new< string , float >()
// @variable An array of "string" values representing the keys to put into the ma
array < string > newKeys = array.from("A", "B", "C", "D", "E")
// Put key-value pairs into the `simpleMap`.
for key in newKeys
simpleMap.put(key, math.random( 1 , 20 ))
// @variable A "string" representation of the `simpleMap` contents. Modified with
string displayText = "simpleMap content: \n "
// Loop through each key-value pair within the `simpleMap`.
for [key, value] in simpleMap
// Add a "string" representation of the pair to the `displayText`.
displayText += key + ": " + str.tostring(value, "#.## ##") + "\n "
// Draw a `label` showing the `displayText` on the current bar.
label.new(
x = bar_index, y = 0 , text = displayText, color = color.green, textcolor =
size = size.huge, textalign = text.align_left, style = label.style_label_ce
)
```
```
Notice!
Scripts can modify the sizes of arrays and matrices while iterating through them
with a
for...in loop. However, itʼs crucial to note that while a script iterates through a
map with
a for...in loop, that mapʼs size cannot change. Attempting to add or remove key-
value
pairs while looping directly through a map will cause a runtime error.
To modify a mapʼs size within a loop, programmers can do any of the following:
Make a copy of the map and loop through that copied instance.
Use a for...in loop to iterate through the map.keys() array.
Use a for or while loop instead of a for...in loop.
```
# 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
```
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
All _literal_ values and the results returned by expressions involving only values
qualified as
“const” automatically adopt the “const” qualifier.
```
literal int : 1 , -1 , 42
literal float : 1. , 1.0 , 3.14 , 6.02E-23 , 3e
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:
## ## 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:
## ## 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.
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**.
## ## 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 and BB 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) where 00 is
invisible and FF opaque. When the literal does not include an AA pair, the script
treats
it as fully opaque (the same as using FF ).
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.
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
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:
See our User Manualʼs page on Text and shapes for more information about displaying
“string” values from a script.
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().
## ## 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 , and
price.
chart.point.now() - Creates a new chart.point with a specified price y-coordinate.
The
time and index 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 and
price y-coordinate. The time field of the resulting instance is na, meaning it will
not
work with drawing objects that use an xloc value of xloc.bar_time.
chart.point.from_time() - Creates a new chart.point with a time x-coordinate and
price y-coordinate. The index field of the resulting instance is na, meaning it
will not
work with drawing objects that use an xloc value of xloc.bar_index.
chart.point.copy() - Creates a new chart.point containing the same time , index ,
and
price information as the id 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.
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
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 the
enumName 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.
## ## `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:
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.
```
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
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.
## ## Tuples
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:
We can also achieve the same result by directly passing a tuple of rounded values
as the
**expression** in the request.security() call:
and:
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:
```
Previous
Loops
```
```
Next
Built-ins
```
```
Notice! For the sake of brevity, we often use “type” to refer to a “qualified
type”.
```
```
Pine Script™
// @version= 6
// The following global variables are all of the "const string" qualified type:
// @variable The title of the indicator.
INDICATOR_TITLE = "const demo"
// @variable The title of the first plot.
var PLOT1_TITLE = "High"
// @variable The title of the second plot.
const string PLOT2_TITLE = "Low"
// @variable The title of the third plot.
PLOT3_TITLE = "Midpoint between " + PLOT1_TITLE + " and " + PLOT2_TITLE
indicator(INDICATOR_TITLE, overlay = true)
plot(high, PLOT1_TITLE)
plot(low, PLOT2_TITLE)
plot(hl2, PLOT3_TITLE)
```
```
Pine Script™
// @version= 6
// @variable The title in the `indicator()` call.
var NAME = "My indicator for " + syminfo.ticker
indicator(NAME, "",
true) // Causes an error because `NAME` is qualified as a "simpl
plot(close)
```
```
Pine Script™
// @version= 6
indicator("Cannot reassign const demo")
// @variable A "float" variable declared as `const`, preventing reassignment.
const float myVar = 0.
myVar +=
1.0 // Causes an error. Reassignment and compound assignments are not allow
plot(myVar)
```
```
Pine Script™
// @version= 6
indicator("Constant reference to 'series' ID demo")
// @variable A `label` variable declared as `const`, preventing reassignment.
// Although the reference is constant, the ID of the `label` is a "series"
const label myVar = label.new(bar_index, close)
```
```
Notice! The input.source() and input.enum() functions are exceptions in the input.*
() namespace, as they do not return values qualified as “input”. The input.source()
function returns “series” values since built-in variables such as open and close,
as well
as the values from another scriptʼs plots, have the “series” qualifier. The
input.enum()
function returns a “simple” result because all values belonging to enums are
available
on the first script execution. See this manualʼs Inputs page for more information.
```
```
Pine Script™
// @version= 6
indicator("input demo", overlay = true)
// @variable The symbol to request data from. Qualified as "input string".
symbolInput = input.symbol("AAPL", "Symbol")
// @variable The timeframe of the data request. Qualified as "input string".
timeframeInput = input.timeframe("D", "Timeframe")
// @variable The source of the calculation. Qualified as "series float".
sourceInput = input.source(close, "Source")
// @variable
The `sourceInput` value from the requested context. Qualified as "series
requestedSource = request.security(symbolInput, timeframeInput, sourceInput)
plot(requestedSource)
```
```
Pine Script™
// @version= 6
indicator("simple demo", overlay = true)
// @variable Is `true` when the current chart is non-
standard. Qualified as "simple b
isNonStandard = not chart.is_standard
// @variable Is orange when the the current chart is non-
standard. Qualified as "simp
simple color warningColor = isNonStandard? color.new(color.orange, 70 ) : na
// Colors the chart's background to warn that it's a non-standard chart type.
bgcolor(warningColor, title = "Non-standard chart color")
```
```
Pine Script™
// @version= 6
indicator("series demo", overlay = true)
// @variable The source value to calculate on. Qualified as "series float".
series float sourceInput = input.source(close, "Source")
// @variable The number of bars in the calculation. Qualified as "input int".
lengthInput = input.int( 20 , "Length")
// @variable
The highest `sourceInput` value over `lengthInput` bars. Qualified as "s
series float highest = ta.highest(sourceInput, lengthInput)
// @variable
The lowest `sourceInput` value over `lengthInput` bars. Qualified as "se
lowest = ta.lowest(sourceInput, lengthInput)
plot(highest, "Highest source", color.green)
plot(lowest, "Lowest source", color.red)
```
```
Pine Script™
// @version= 6
indicator("Types demo", overlay = true)
// @variable A value of the "const string" type for the `ma` plot's title.
string MA_TITLE = "MA"
// @variable A value of the "input int" type. Controls the length of the average.
int lengthInput = input.int( 100 , "Length", minval = 2 )
// @variable
A "series float" value representing the last `close` that crossed over t
var float crossValue = na
// @variable A "series float" value representing the moving average of `close`.
float ma = ta.sma(close, lengthInput)
// @variable
A "series bool" value that's `true` when the `close` crosses over the `m
bool crossUp = ta.crossover(close, ma)
// @variable
A "series color" value based on whether `close` is above or below its `m
color maColor = close > ma? color.lime : color.fuchsia
// Update the `crossValue`.
if crossUp
crossValue := close
plot(ma, MA_TITLE, maColor)
plot(crossValue, "Cross value", style = plot.style_circles)
plotchar(crossUp, "Cross Up", "▲", location.belowbar, size = size.small)
```
```
Pine Script™
1
```
-
750
```
Pine Script™
3.14159 // Rounded value of Pi (π)
```
- 3.
6.02e23 // 6.02 * 10^23 (a very large value)
1.6e-19 // 1.6 * 10^-19 (a very small value)
```
Pine Script™
true // true value
false // false value
```
```
Pine Script™
#000000 // black color
#FF0000 // red color
#00FF00 // green color
#0000FF // blue color
#FFFFFF // white color
#808080 // gray color
#3ff7a0 // some custom color
#FF000080 // 50% transparent red color
#FF0000ff // same as #FF0000, fully opaque red color
#FF000000 // completely transparent red color
```
```
Pine Script™
// @version= 6
indicator("Shading the chart's background", overlay = true)
// @variable A "const color" value representing the base for each day's color.
color BASE_COLOR = color.rgb( 0 , 99 , 165 )
// @variable
A "series int" value that modifies the transparency of the `BASE_COLOR`
int transparency = 50 + int( 40 * dayofweek / 7 )
// Color the background using the modified `BASE_COLOR`.
bgcolor(color.new(BASE_COLOR, transparency))
```
```
Pine Script™
"This is a string literal using double quotes."
'This is a string literal using single quotes.'
```
```
Pine Script™
"It's an example"
'The "Star" indicator'
```
```
Pine Script™
'It\'s an example'
"The \"Star\" indicator"
```
```
Pine Script™
"This\nString\nHas\nOne\nWord\nPer\nLine"
```
```
Pine Script™
"This is a " + "concatenated string."
```
```
Pine Script™
// @version= 6
indicator("Formatted string demo", overlay = true)
// @variable A "series string" value representing the bar's OHLC data.
string ohlcString = str.format("Open: {0}\nHigh: {1}\nLow: {2}\nClose: {3}", open,
h
// Draw a label containing the `ohlcString`.
label.new(bar_index, high, ohlcString, textcolor = color.white)
```
```
Pine Script™
// @version= 6
indicator("plot fill demo", overlay = true)
// @variable A "series float" value representing a 10-bar EMA of `close`.
float emaFast = ta.ema(close, 10 )
// @variable A "series float" value representing a 20-bar EMA of `close`.
float emaSlow = ta.ema(close, 20 )
// @variable The plot of the `emaFast` value.
emaFastPlot = plot(emaFast, "Fast EMA", color.orange, 3 )
// @variable The plot of the `emaSlow` value.
emaSlowPlot = plot(emaSlow, "Slow EMA", color.gray, 3 )
// Fill the space between the `emaFastPlot` and `emaSlowPlot`.
fill(emaFastPlot, emaSlowPlot, color.new(color.purple, 50 ), "EMA Fill")
```
```
Pine Script™
// @version= 6
indicator("Chart points demo", overlay = true)
// @variable A new `chart.point` at the previous `bar_index` and `high`.
firstPoint = chart.point.from_index(bar_index - 1 , high[ 1 ])
// @variable A new `chart.point` at the current bar's `low`.
secondPoint = chart.point.now(low)
// Draw a new line connecting coordinates from the `firstPoint` and `secondPoint`.
// This line uses the `index` fields from the points as x-coordinates.
line.new(firstPoint, secondPoint, color = color.purple, width = 3 )
// Draw a label at the `firstPoint`. Uses the point's `index` field as its x-
coordin
label.new(
firstPoint, str.tostring(firstPoint.price), color = color.green,
style = label.style_label_down, textcolor = color.white
)
// Draw a label at the `secondPoint`. Uses the point's `index` field as its x-
coordi
label.new(
secondPoint, str.tostring(secondPoint.price), color = color.red,
style = label.style_label_up, textcolor = color.white
)
```
```
Pine Script™
a1 = array.new< int >( 1 , 10 )
array < int > a2 = array.new< int >( 1 , 10 )
a3 = array.from( 10 )
array < int > a4 = array.from( 10 )
```
```
Pine Script™
// @type A user-defined type containing pivot information.
// @field pivotTime Contains time information about the pivot.
// @field priceLevel Contains price information about the pivot.
type pivotPoint
int pivotTime
float priceLevel
```
```
Pine Script™
// @type A user-defined type containing pivot information.
// @field pivotTime Contains time information about the pivot.
// @field priceLevel Contains price information about the pivot.
// @field nextPivot A `pivotPoint` instance containing additional pivot informat
type pivotPoint
int pivotTime
float priceLevel
pivotPoint nextPivot
```
```
Pine Script™
// @enum An enumeration of named values for moving average selection.
// @field sma Selects a Simple Moving Average.
// @field ema Selects an Exponential Moving Average.
// @field wma Selects a Weighted Moving Average.
// @field hma Selects a Hull Moving Average.
enum maChoice
sma = "Simple Moving Average"
ema = "Exponential Moving Average"
wma = "Weighted Moving Average"
hma = "Hull Moving Average"
```
```
Pine Script™
// @version= 6
indicator("Enum types demo", overlay = true)
// @enum An enumeration of named values for moving average selection.
// @field sma Selects a Simple Moving Average.
// @field ema Selects an Exponential Moving Average.
// @field wma Selects a Weighted Moving Average.
// @field hma Selects a Hull Moving Average.
enum maChoice
sma = "Simple Moving Average"
ema = "Exponential Moving Average"
wma = "Weighted Moving Average"
hma = "Hull Moving Average"
// @variable The `maChoice` member representing a selected moving average name.
maChoice maInput = input.enum(maChoice.sma, "Moving average type")
// @variable The length of the moving average.
int lengthInput = input.int( 20 , "Length", 1 , 4999 )
// @variable The moving average selected by the `maInput`.
float selectedMA = switch maInput
maChoice.sma => ta.sma(close, lengthInput)
maChoice.ema => ta.ema(close, lengthInput)
maChoice.wma => ta.wma(close, lengthInput)
maChoice.hma => ta.hma(close, lengthInput)
// Plot the `selectedMA`.
plot(selectedMA, "Selected moving average", color.teal, 3 )
```
```
Pine Script™
// Compilation error!
myVar = na
```
```
Pine Script™
float myVar = na
```
```
Pine Script™
myVar = float(na)
```
```
Pine Script™
// @variable Is 0 if the `myVar` is `na`, `close` otherwise.
float myClose = na(myVar)? 0 : close
```
```
Pine Script™
// @variable
Returns the `close` value. The script cannot compare the equality of `na
float myClose = myVar == na? 0 : close
```
```
Pine Script™
// @variable
Is `true` when the `close` exceeds the last bar's `close` (or the curren
bool risingClose = close > nz(close[ 1 ], open)
```
```
Pine Script™
// @version= 6
indicator("na protection demo", overlay = true)
// @variable The result of calculating the all-
time high price with an initial value
var float allTimeHigh = na
// Reassign the value of the `allTimeHigh`.
// Returns `na` on all bars because `math.max()` can't compare the `high` to an und
e
allTimeHigh := math.max(allTimeHigh, high)
plot(allTimeHigh) // Plots `na` on all bars.
```
```
Pine Script™
// @version= 6
indicator("na protection demo", overlay = true)
// @variable The result of calculating the all-
time high price with an initial value
var float allTimeHigh = na
// Reassign the value of the `allTimeHigh`.
// We've used `nz()` to prevent the initial `na` value from persisting throughout t
h
allTimeHigh := math.max(nz(allTimeHigh), high)
plot(allTimeHigh)
```
```
Pine Script™
// @version= 6
indicator("Type templates demo")
// @variable
A variable initially assigned to `na` that accepts arrays of "int" value
array < int > intArray = na
// @variable An empty matrix that holds "float" values.
floatMatrix = matrix.new< float >()
// @variable An empty map that holds "string" keys and "color" values.
stringColorMap = map.new< string , color >()
```
```
Pine Script™
// @version= 6
indicator("Explicit casting demo", overlay = true)
// @variable The length of the SMA calculation. Qualified as "const float".
float LENGTH = 10.
float sma = ta.sma(close,
LENGTH) // Compilation error. The `length` parameter requi
plot(sma)
```
```
Pine Script™
// @version= 6
indicator("explicit casting demo")
// @variable The length of the SMA calculation. Qualified as "const float".
float LENGTH = 10.
float sma = ta.sma(close,
int(LENGTH)) // Compiles successfully since we've converte
plot(sma)
```
```
Pine Script™
// Explicitly specify that the variable references "label" objects:
label myLabel = na
// Explicitly cast the `na` value to the "label" type:
myLabel = label(na)
```
```
Pine Script™
// @function Calculates the sum and product of two values.
calcSumAndProduct( float a, float b) =>
// @variable The sum of `a` and `b`.
float sum = a + b
// @variable The product of `a` and `b`.
float product = a * b
// Return a tuple containing the `sum` and `product`.
[sum, product]
```
```
Pine Script™
// Declare a tuple containing the sum and product of the `high` and `low`, respecti
v
[hlSum, hlProduct] = calcSumAndProduct(high, low)
```
```
Pine Script™
// @function Returns information about the current chart.
chartInfo() =>
// @variable The first visible bar's UNIX time value.
int firstVisibleTime = chart.left_visible_bar_time
// @variable The `close` value at the `firstVisibleTime`.
float firstVisibleClose = ta.valuewhen(ta.cross(time, firstVisibleTime), close,
// @variable Is `true` when using a standard chart type, `false` otherwise.
bool isStandard = chart.is_standard
// @variable The foreground color of the chart.
color fgColor = chart.fg_color
// @variable The ticker ID of the current chart.
string symbol = syminfo.tickerid
// Return a tuple containing the values.
[firstVisibleTime, firstVisibleClose, isStandard, fgColor, symbol]
```
```
Pine Script™
// @function Returns a tuple of OHLC values, rounded to the nearest tick.
roundedOHLC() =>
[math.round_to_mintick(open), math.round_to_mintick(high), math.round_to_mintick
[op, hi, lo, cl] = request.security(syminfo.tickerid, "D", roundedOHLC())
```
```
Pine Script™
[op, hi, lo, cl] = request.security(
syminfo.tickerid, "D",
[math.round_to_mintick(open), math.round_to_mintick(high), math.round_to_mintic
)
```
```
Pine Script™
[v1, v2] = if close > open
[high, close]
else
[close, low]
```
```
Pine Script™
[v1, v2] = switch
close > open => [high, close]
=> [close, low]
```
```
Pine Script™
// Not allowed.
[v1, v2] = close > open? [high, close] : [close, low]
```
```
Pine Script™
// @version= 6
indicator("Qualified types in tuples demo")
makeTicker( simple string prefix, simple string ticker) =>
tId = prefix + ":" + ticker // simple string
source = close // series float
[tId, source]
// Both variables are series now.
[tId, source] = makeTicker("BATS", "AAPL")
// Error cannot call 'request.security' with 'series string' tId.
r = request.security(tId, "", source)
plot(r)
```
# Built-ins
## ## Introduction
Pine Script™ has hundreds of _built-in_ variables and functions. They provide your
scripts with
valuable information and make calculations for you, dispensing you from coding
them. The
better you know the built-ins, the more you will be able to do with your Pine
scripts.
Variables and functions in the same family share the same _namespace_ , which is a
prefix to the
functionʼs name. The ta.sma() function, for example, is in the **ta** namespace,
which stands
for “technical analysis”. A namespace can contain both variables and functions.
```
The ta.tr variable returns the “True Range” of the current bar. The ta.tr(true)
function call
also returns the “True Range”, but when the previous close value which is normally
needed to calculate it is na, it calculates using high - low instead.
The time variable gives the time at the open of the current bar. The
time(timeframe)
function returns the time of the barʼs open from the timeframe specified, even if
the
chartʼs timeframe is different. The time(timeframe, session) function returns the
time of
the barʼs open from the timeframe specified, but only if it is within the session
time.
The time(timeframe, session, timezone) function returns the time of the barʼs open
from
the timeframe specified, but only if it is within the session time in the specified
timezone.
```
## ## Built-in variables
Built-in variables exist for different purposes. These are a few examples:
```
Price- and volume-related variables: open, high, low, close, hl 2 , hlc 3 , ohlc
4 , and volume.
Symbol-related information in the syminfo namespace: syminfo.basecurrency,
syminfo.currency, syminfo.description, syminfo.mintick, syminfo.pointvalue,
syminfo.prefix, syminfo.root, syminfo.session, syminfo.ticker, syminfo.tickerid,
syminfo.timezone, and syminfo.type.
Timeframe (a.k.a. “interval” or “resolution”, e.g., 15 sec, 30 min, 60 min, 1 D, 3
M) variables
in the timeframe namespace: timeframe.isseconds, timeframe.isminutes,
timeframe.isintraday, timeframe.isdaily, timeframe.isweekly, timeframe.ismonthly,
timeframe.isdwm, timeframe.multiplier, and timeframe.period.
Bar states in the barstate namespace (see the Bar states page):
barstate.isconfirmed,
barstate.isfirst, barstate.ishistory, barstate.islast,
barstate.islastconfirmedhistory,
barstate.isnew, and barstate.isrealtime.
Strategy-related information in the strategy namespace: strategy.equity,
strategy.initial_capital, strategy.grossloss, strategy.grossprofit,
strategy.wintrades,
strategy.losstrades, strategy.position_size, strategy.position_avg_price,
strategy.wintrades, etc.
```
## ## Built-in functions
Many functions are used for the result(s) they return. These are a few examples:
```
Math-related functions in the math namespace: math.abs(), math.log(), math.max(),
math.random(), math.round_to_mintick(), etc.
Technical indicators in the ta namespace: ta.sma(), ta.ema(), ta.macd(), ta.rsi(),
ta.supertrend(), etc.
Support functions often used to calculate technical indicators in the ta namespace:
ta.barssince(), ta.crossover(), ta.highest(), etc.
Functions to request data from other symbols or timeframes in the request
namespace: request.dividends(), request.earnings(), request.financial(),
request.quandl(), request.security(), request.splits().
Functions to manipulate strings in the str namespace: str.format(), str.length(),
str.tonumber(), str.tostring(), etc.
Functions used to define the input values that script users can modify in the
scriptʼs
“Settings/Inputs” tab, in the input namespace: input(), input.color(), input.int(),
input.session(), input.symbol(), etc.
Functions used to manipulate colors in the color namespace: color.from_gradient(),
color.new(), color.rgb(), etc.
```
Some functions do not return a result but are used for their side effects, which
means they do
something, even if they donʼt return a result:
```
Functions used as a declaration statement defining one of three types of Pine
scripts,
and its properties. Each script must begin with a call to one of these functions:
indicator(), strategy() or library().
Plotting or coloring functions: bgcolor(), plotbar(), plotcandle(), plotchar(),
plotshape(),
fill().
Strategy functions placing orders, in the strategy namespace: strategy.cancel(),
strategy.close(), strategy.entry(), strategy.exit(), strategy.order(), etc.
Strategy functions returning information on indivdual past trades, in the strategy
namespace: strategy.closedtrades.entry_bar_index(),
strategy.closedtrades.entry_price(), strategy.closedtrades.entry_time(),
strategy.closedtrades.exit_bar_index(), strategy.closedtrades.max_drawdown(),
strategy.closedtrades.max_runup(), strategy.closedtrades.profit(), etc.
Functions to generate alert events: alert() and alertcondition().
```
Other functions return a result, but we donʼt always use it, e.g.: hline(), plot(),
array.pop(),
label.new(), etc.
All built-in functions are defined in the Pine Script™ v 6 Reference Manual. You
can click on
any of the function names listed here to go to its entry in the Reference Manual,
which
documents the functionʼs signature, i.e., the list of _parameters_ it accepts and
the qualified
type of the value(s) it returns (a function can return more than one result). The
Reference
Manual entry will also list, for each parameter:
```
Its name.
The qualified type of the value it requires (we use argument to name the values
passed
to a function when calling it).
If the parameter is required or not.
```
All built-in functions have one or more parameters defined in their signature. Not
all
parameters are required for every function.
Letʼs look at the ta.vwma() function, which returns the volume-weighted moving
average of a
source value. This is its entry in the Reference Manual:
```
What the function does.
Its signature (or definition):
```
```
The parameters it includes: source and length
The qualified type of the result it returns: “series float”.
An example showing it in use: plot(ta.vwma(close, 15)).
An example showing what it does, but in long form, so you can better understand its
calculations. Note that this is meant to explain --- not as usable code, because it
is more
complicated and takes longer to execute. There are only disadvantages to using the
long form.
The “RETURNS” section explains exacty what value the function returns.
The “ARGUMENTS” section lists each parameter and gives the critical information
concerning what qualified type is required for arguments used when calling the
function.
The “SEE ALSO” section refers you to related Reference Manual entries.
```
This is a call to the function in a line of code that declares a **myVwma**
variable and assigns the
result of **ta.vwma(close, 20)** to it:
Note that:
```
We use the built-in variable close as the argument for the source parameter.
We use 20 as the argument for the length parameter.
If placed in the global scope (i.e., starting in a lineʼs first position), it will
be executed by
the Pine Script™ runtime on each bar of the chart.
```
We can also use the parameter names when calling the function. Parameter names are
called
_keyword arguments_ when used in a function call:
You can change the position of arguments when using keyword arguments, but only if
you use
them for all your arguments. When calling functions with many parameters such as
indicator(), you can also forego keyword arguments for the first arguments, as long
as you
donʼt skip any. If you skip some, you must then use keyword arguments so the Pine
Script™
compiler can figure out which parameter they correspond to, e.g.:
**When calling built-ins, it is critical to ensure that the arguments you use are
of the
required qualified type, which will vary for each parameter.**
To learn how to do this, one needs to understand Pine Script™‘s type system. The
Reference
Manual entry for each built-in function includes an “ARGUMENTS” section which lists
the
qualified type required for the argument supplied to each of the functionʼs
parameters.
```
Previous
Type system
```
```
Next
User-defined functions
```
```
ta.vwma(source, length) → series float
```
```
Pine Script™
myVwma = ta.vwma(close, 20 )
```
```
Pine Script™
myVwma = ta.vwma(source = close, length = 20 )
```
```
Pine Script™
indicator("Example", "Ex", true, max_bars_back = 100 )
```
```
Pine Script™
indicator(precision = 3 , "Example") // Compilation error!
```
# User-defined functions
## ## Introduction
User-defined functions are functions that you write, as opposed to the built-in
functions in
Pine Script™. They are useful to define calculations that you must do repetitevely,
or that you
want to isolate from your scriptʼs main section of calculations. Think of user-
defined functions
as a way to extend the capabilities of Pine Script™, when no built-in function will
do what you
need.
```
In a single line, when they are simple, or
On multiple lines
```
Functions can be located in two places:
```
If a function is only used in one script, you can include it in the script where it
is used.
See our Style guide for recommendations on where to place functions in your script.
You can create a Pine Script™ library to include your functions, which makes them
reusable in other scripts without having to copy their code. Distinct requirements
exist
for library functions. They are explained in the page on libraries.
```
Whether they use one line or multiple lines, user-defined functions have the
following
characteristics:
```
They cannot be embedded. All functions are defined in the scriptʼs global scope.
They do not support recursion. It is not allowed for a function to call itself from
within
its own code.
The type of the value returned by a function is determined automatically and
depends
on the type of arguments used in each particular function call.
A functionʼs returned value is that of the last value in the functionʼs body.
Each instance of a function call in a script maintains its own, independent
history.
```
## ## Single-line functions
Simple functions can often be written in one line. This is the formal definition of
single-line
functions:
Here is an example:
After the function **f()** has been declared, itʼs possible to call it using
different types of
arguments:
In the example above, the type of variable **a** is _series_ because the arguments
are both _series_.
The type of variable **b** is _integer_ because arguments are both _literal
integers_. The type of
variable **c** is _series_ because the addition of a _series_ and _literal integer_
produces a _series_
result.
## ## Multi-line functions
Pine Script™ also supports multi-line functions with the following syntax:
where:
The function **geom_average** has two arguments and creates two variables in the
body: **a** and
**b**. The last statement calls the function **math.sqrt** (an extraction of the
square root). The
**geom_average** call will return the value of the last expression: **(math.sqrt(a
+ b))**.
Variables declared outside the body of a function or of other local blocks belong
to the _global_
scope. User-declared and built-in functions, as well as built-in variables also
belong to the
global scope.
Each function has its own _local_ scope. All the variables declared within the
function, as well as
the functionʼs arguments, belong to the scope of that function, meaning that it is
impossible
to reference them from outside --- e.g., from the global scope or the local scope
of another
function.
In Pine Script™, nested functions are not allowed, i.e., one cannot declare a
function inside
another one. All user functions are declared in the global scope. Local scopes
cannot
intersect with each other.
## ## Limitations
User-defined functions can use any of the Pine Script™ built-ins, except:
barcolor(), fill(),
hline(), indicator(), library(), plot(), plotbar(), plotcandle(), plotchar(),
plotshape() and
strategy().
```
Previous
Built-ins
```
```
Next
Objects
```
```
<function_declaration>
<identifier>(<parameter_list>) => <return_value>
```
```
<parameter_list>
{<parameter_definition>{, <parameter_definition>}}
```
```
<parameter_definition>
[<identifier> = <default_value>]
```
```
<return_value>
<statement> | <expression> | <tuple>
```
```
Pine Script™
f(x, y) => x + y
```
```
Pine Script™
a = f(open, close)
b = f( 2 , 2 )
c = f(open, 2 )
```
```
<identifier>(<parameter_list>) =>
<local_block>
```
```
<identifier>(<list of parameters>) =>
<variable declaration>
...
<variable declaration or expression>
```
```
<parameter_list>
{<parameter_definition>{, <parameter_definition>}}
```
```
<parameter_definition>
[<identifier> = <default_value>]
```
```
Pine Script™
geom_average(x, y) =>
a = x*x
b = y*y
math.sqrt(a + b)
```
```
Pine Script™
fun(x, y) =>
a = x+y
b = x-y
[a, b]
```
```
Pine Script™
[res0, res1] = fun(open, close)
plot(res0)
plot(res1)
```
```
ADVANCED
```
# Objects
## ## Introduction
Pine Script™ objects are instances of _user-defined types_ (UDTs). They are the
equivalent of
variables containing parts called _fields_ , each able to hold independent values
that can be of
various types.
Experienced programmers can think of UDTs as methodless classes. They allow users
to
create custom types that organize different values under one logical entity.
## ## Creating objects
Before an object can be created, its type must be defined. The User-defined types
section of
the Type system page explains how to do so.
Note that:
```
We use the type keyword to declare the creation of a UDT.
We name our new UDT pivotPoint.
After the first line, we create a local block containing the type and name of each
field.
The x field will hold the x-coordinate of the pivot. It is declared as an “int”
because it
will hold either a timestamp or a bar index of “int” type.
y is a “float” because it will hold the pivotʼs price.
xloc is a field that will specify the units of x : xloc.bar_index or xloc.bar_time.
We set
its default value to xloc.bar_time by using the = operator. When an object is
created
from that UDT, its xloc field will thus be set to that value.
```
Now that our **pivotPoint** UDT is defined, we can proceed to create objects from
it. We
create objects using the UDTʼs **new()** built-in method. To create a new
**foundPoint** object
from our **pivotPoint** UDT, we use:
We can also specify field values for the created object using the following:
Or the equivalent:
At this point, the **foundPoint** objectʼs **x** field will contain the value of
the time built-in when
it is created, **y** will contain the value of high and the **xloc** field will
contain its default value
of xloc.bar_time because no value was defined for it when creating the object.
Object placeholders can also be created by declaring na object names using the
following:
This example displays a label where high pivots are detected. The pivots are
detected
**legsInput** bars after they occur, so we must plot the label in the past for it
to appear on the
pivot:
When using the var keyword while declaring a variable assigned to an object of a
user-defined
type, the keyword automatically applies to all the objectʼs fields:
Itʼs important to note that assigning an object to a variable that uses the varip
keyword does
_not_ automatically allow the objectʼs fields to persist without rolling back on
each _intrabar_
update. One must apply the keyword to each desired field in the type declaration to
achieve
this behavior. For example:
Note that:
```
We used the var keyword to specify that the Counter object assigned to the counter
variable persists throughout the scriptʼs execution.
The bars field rolls back on realtime bars, whereas the ticks field does not since
we
included varip in its declaration.
```
## ## Changing field values
The value of an objectʼs fields can be changed using the := reassignment operator.
## ## Collecting objects
Pine Script™ collections (arrays, matrices, and maps) can contain objects, allowing
users to
add virtual dimensions to their data structures. To declare a collection of
objects, pass a UDT
name into its type template.
This example declares an empty array that will hold objects of a **pivotPoint**
user-defined
type:
Letʼs use what we have learned to create a script that detects high pivot points.
The script
first collects historical pivot information in an array. It then loops through the
array on the last
historical bar, creating a label for each pivot and connecting the pivots with
lines:
## ## Copying objects
In the example below, we create a **pivot1** object and set its **x** field to
1000. Then, we
declare a **pivot2** variable containing the reference to the **pivot1** object, so
both point to
the same instance. Changing **pivot2.x** will thus also change **pivot1.x** , as
both refer to the
**x** field of the same object:
To create a copy of an object that is independent of the original, we can use the
built-in
**copy()** method in this case.
In the following example, we have defined an **InfoLabel** type with a label as one
of its fields.
The script instantiates a **shallow** copy of the **parent** object, then calls a
user-defined
**set()** method to update the **info** and **lbl** fields of each object. Since
the **lbl** field of
both objects points to the same label instance, changes to this field in either
object affect the
other:
To produce a _deep copy_ of an object with all of its special type fields pointing
to independent
instances, we must explicitly copy those fields as well.
## ## Shadowing
Only the languageʼs five primitive types cannot be used to name UDTs or objects:
int, float,
string, bool, and color.
```
Previous
User-defined functions
```
```
Next
Enums
```
```
Notice! This page contains advanced material. If you are a beginning Pine Script™
programmer, we recommend you become familiar with other, more accessible Pine
Script™ features before you venture here.
```
```
Pine Script™
type pivotPoint
int x
float y
string xloc = xloc.bar_time
```
```
Pine Script™
foundPoint = pivotPoint.new()
```
```
Pine Script™
foundPoint = pivotPoint.new(time, high)
```
```
Pine Script™
foundPoint = pivotPoint.new(x = time, y = high)
```
```
Pine Script™
pivotPoint foundPoint = na
```
```
Pine Script™
// @version= 6
indicator("Pivot labels", overlay = true)
int legsInput = input( 10 )
// Define the `pivotPoint` UDT.
type pivotPoint
int x
float y
string xloc = xloc.bar_time
// Detect high pivots.
pivotHighPrice = ta.pivothigh(legsInput, legsInput)
if not na(pivotHighPrice)
// A new high pivot was found; display a label where it occurred `legsInput` bar
foundPoint = pivotPoint.new(time[legsInput], pivotHighPrice)
label.new(
foundPoint.x,
foundPoint.y,
str.tostring(foundPoint.y, format.mintick),
foundPoint.xloc,
textcolor = color.white)
```
```
Pine Script™
foundPoint = pivotPoint.new(time[legsInput], pivotHighPrice)
```
```
Pine Script™
pivotPoint foundPoint = na
foundPoint := pivotPoint.new(time[legsInput], pivotHighPrice)
```
```
Pine Script™
// @version= 6
indicator("Objects using `var` demo")
// @type A custom type to hold index, price, and volume information.
type BarInfo
int index = bar_index
float price = close
float vol = volume
// @variable
A `BarInfo` instance whose fields persist through all iterations, starti
var BarInfo firstBar = BarInfo.new()
// @variable A `BarInfo` instance declared on every bar.
BarInfo currentBar = BarInfo.new()
// Plot the `index` fields of both instances to compare the difference.
plot(firstBar.index)
plot(currentBar.index)
```
```
Pine Script™
// @version= 6
indicator("Objects using `varip` fields demo")
// @type A custom type that counts the bars and ticks in the script's execution.
type Counter
int bars =
varip int ticks =
// @variable A `Counter` object whose reference persists throughout all bars.
var Counter counter = Counter.new()
// Add 1 to the `bars` and `ticks` fields. The `ticks` field is not subject to roll
b
counter.bars +=
counter.ticks +=
// Plot both fields for comparison.
plot(counter.bars, "Bar counter", color.blue, 3 )
plot(counter.ticks, "Tick counter", color.purple, 3 )
```
```
Pine Script™
foundPoint = pivotPoint.new(time[legsInput], pivotHighPrice)
```
```
Pine Script™
foundPoint = pivotPoint.new()
foundPoint.x := time[legsInput]
foundPoint.y := pivotHighPrice
```
```
Pine Script™
pivotHighArray = array.new< pivotPoint >()
```
```
Pine Script™
var array < pivotPoint > pivotHighArray = na
pivotHighArray := array.new< pivotPoint >()
```
```
Pine Script™
// @version= 6
indicator("Pivot Points High", overlay = true)
int legsInput = input( 10 )
// Define the `pivotPoint` UDT containing the time and price of pivots.
type pivotPoint
int openTime
float level
// Create an empty `pivotPoint` array.
var pivotHighArray = array.new< pivotPoint >()
// Detect new pivots (`na` is returned when no pivot is found).
pivotHighPrice = ta.pivothigh(legsInput, legsInput)
// Add a new `pivotPoint` object to the end of the array for each detected pivot.
if not na(pivotHighPrice)
// A new pivot is found; create a new object of `pivotPoint` type, setting its `
newPivot = pivotPoint.new(time[legsInput], pivotHighPrice)
// Add the new pivot object to the array.
array.push(pivotHighArray, newPivot)
// On the last historical bar, draw pivot labels and connecting lines.
if barstate.islastconfirmedhistory
var pivotPoint previousPoint = na
for eachPivot in pivotHighArray
// Display a label at the pivot point.
label.new(eachPivot.openTime, eachPivot.level, str.tostring(eachPivot.level,
// Create a line between pivots.
if not na(previousPoint)
// Only create a line starting at the loop's second iteration because li
line.new(previousPoint.openTime, previousPoint.level, eachPivot.openTime
// Save the pivot for use in the next iteration.
previousPoint := eachPivot
```
```
Pine Script™
// @version= 6
indicator("")
type pivotPoint
int x
float y
pivot1 = pivotPoint.new()
pivot1.x :=
pivot2 = pivot
pivot2.x :=
// Both plot the value 2000.
plot(pivot1.x)
plot(pivot2.x)
```
```
Pine Script™
// @version= 6
indicator("")
type pivotPoint
int x
float y
pivot1 = pivotPoint.new()
pivot1.x :=
pivot2 = pivotPoint.copy(pivot1)
pivot2.x :=
// Plots 1000 and 2000.
plot(pivot1.x)
plot(pivot2.x)
```
```
Pine Script™
// @version= 6
indicator("Shallow Copy")
type InfoLabel
string info
label lbl
method set( InfoLabel this, int x = na, int y = na, string info = na) =>
if not na(x)
this.lbl.set_x(x)
if not na(y)
this.lbl.set_y(y)
if not na(info)
this.info := info
this.lbl.set_text(this.info)
var parent = InfoLabel.new("", label.new( 0 , 0 ))
var shallow = parent.copy()
parent.set(bar_index, 0 , "Parent")
shallow.set(bar_index, 1 , "Shallow Copy")
```
```
Pine Script™
// @version= 6
indicator("Deep Copy")
type InfoLabel
string info
label lbl
method set( InfoLabel this, int x = na, int y = na, string info = na) =>
if not na(x)
this.lbl.set_x(x)
if not na(y)
this.lbl.set_y(y)
if not na(info)
this.info := info
this.lbl.set_text(this.info)
method deepCopy( InfoLabel this) =>
InfoLabel.new(this.info, this.lbl.copy())
var parent = InfoLabel.new("", label.new( 0 , 0 ))
var deep = parent.deepCopy()
parent.set(bar_index, 0 , "Parent")
deep.set(bar_index, 1 , "Deep Copy")
```
```
ADVANCED
```
# Enums
## ## Introduction
## ## Declaring an enum
To declare an enum, use the enum keyword with the following syntax:
Each **field** in the enum represents a unique, _named member_ (value) of the enum
type. Users
can specify optional “const string” **titles** for enum fields to add extra
information about what
their values represent. If the programmer does not specify a fieldʼs title, its
title is the “string”
representation of its name. Enum inputs display enum field titles within their
dropdown menus
in a scriptʼs “Settings/Inputs” tab. Scripts can also retrieve enum field titles
using the
str.tostring() function, allowing their use in additional calculations. See this
section below for
more information.
While the above syntax may look similar to the syntax for declaring user-defined
types
(UDTs), itʼs crucial to understand that enum types and UDTs serve different
purposes. Scripts
use UDTs to create objects with “series” fields that can hold values of _any_
specified type. In
contrast, enums are distinct groups of “simple” fields representing the specific,
_predefined
values_ of the same _unique_ type that variables, expressions, and collections can
accept.
For example, this code block declares a **Signal** enum with three fields:
**buy** , **sell** , and
**neutral**. Each field represents a distinct member (possible value) of the
**Signal** enum type:
Note that:
```
The Signal identifier represents the enumʼs name, which signifies the unique type
the
fields belong to.
We used the //@enum and //@field annotations to document the meaning of the
enum and its fields.
Unlike the buy and sell fields, the neutral field does not include a specified
title. As
such, its title is the “string” representation of its name (“neutral”).
```
To retrieve a member of an enum, reference its field name using _dot notation_
syntax, i.e.:
As with other types, scripts can assign enum members to variables, function
parameters, and
UDT fields, allowing strict control over their allowed values.
For instance, this line of code declares a **mySignal** variable whose value is the
**neutral**
member of the **Signal** enum. Any value assigned to this variable later must also
be of the
same enum type:
Note that the above line does not require declaring the variableʼs _type_ as
**Signal** because
the compiler can automatically infer that information from the assigned value. If
we use na as
the initial value instead, we must use **Signal** as the type keyword to specify
that **mySignal**
will accept a **Signal** member:
## ## Using enums
Scripts can compare enum members with the == and != operators and use them in
conditional
structures, allowing the convenient creation of logical patterns with a reduced
risk of
unintended values or operations.
The following example declares an **OscType** enum with three fields representing
different
oscillator choices: **rsi** , **mfi** , and **cci**. The **calcOscillator()**
function uses **OscType**
members within a switch structure to determine which oscillator it calculates. The
script calls
this function using the value from an enum input as the **selection** argument and
plots the
resulting oscillator:
Note that:
```
The selection parameter of the calcOscillator() function can only take on one of
four values: OscType.rsi , OscType.mfi , OscType.cci , or na.
The “Oscillator type” input in the scriptʼs “Settings/Inputs” tab displays all
OscType field
titles in its dropdown. See this section to learn more about enum inputs.
```
Itʼs crucial to note that each declared enum represents a _unique_ type. Scripts
**cannot**
compare members of different enums or use such members in expressions requiring a
specific enum type, even if the fields have identical names and titles.
In this example, we added an **OscType2** enum to the above script and changed the
**oscInput**
variable to use a member of that enum. The script now raises a _compilation error_
because it
canʼt use a member of the **OscType2** enum as the **selection** argument in the
**calcOscillator()** call:
The “string” titles of an enumʼs fields allow programmers to add extra information
to each
member. These field titles appear within a dropdown in the scriptʼs
“Settings/Inputs” tab when
calling the input.enum() function.
Scripts can also utilize enum field titles in their calculations and logic. Use the
string
conversion function (str.tostring()) on an enum field to access its title.
The following example combines different enum field titles to construct a ticker ID
for
requesting data from another context. The script declares two enums, **Exchange**
and **Pair** ,
whose respective fields represent _exchange_ and _currency pair_ names. It uses
input.enum() to
assign user-specified enum members to the **exchangeInput** and **pairInput**
variables, then
retrieves the “string” titles from those variables with str.tostring() and
concatenates them to
form an “Exchange:Symbol” pair for use in a request.security() call:
Note that:
```
None of the members of the Exchange or Pair enums have specified titles. Therefore,
each fieldʼs title is the “string” representation of its name, as shown by the
scriptʼs enum
inputs.
Calling the str.tostring() function on an enum field is the only way to retrieve
its title for
additional calculations. The str.format() and log.*() functions cannot accept enum
members. To use a fieldʼs title in a string formatting function, call
str.tostring() on the
field first, then pass the resulting “string” to the function.
```
## ## Collecting enum members
Pine Script™ collections (arrays, matrices, and maps) can store enum members,
allowing
strict control over the values they can contain. To declare a collection of enum
members,
include the enumʼs _name_ in the collectionʼs type template.
For example, this code block creates an empty array to hold members of the
**FooBar** enum.
The only values this array can allow as elements are **FooBar.foo** ,
**FooBar.bar** , **FooBar.baz** ,
and na:
Enums are particularly helpful when working with maps, as unlike other _non-
fundamental_
types, scripts can declare maps with _keys_ of an enum type, enabling strict
control over all
possible keys allowed in their key-value pairs.
The following example uses a map with enum keys and “int” values to track and count
signal
states across chart bars. The scriptʼs **Signal** enum contains five fields
representing specific
named states. The **signalCounters** map uses the **Signal** name as the _first
keyword_ in its
type template to specify that it can only accept **Signal** members as keys.
Note that:
```
The signalCounters map can contain up to six key-value pairs, as the Signal enum
has five predefined values, plus a possible value of na, and maps cannot contain
repetitive keys.
The script declares the signalCounters variable using the var keyword, signifying
that
the assigned map instance persists across executions.
On the first chart bar, the script uses five map.put() calls to establish the
insertion order
of keys in the signalCounters map. See this section of the Maps page for more
information.
To minimize resource usage , the script declares the infoTable and initializes its
cell on
the first bar , then updates the cellʼs text on the latest bar. See this section of
the
Profiling and optimization page to learn more.
```
## ## Shadowing
To avoid potential conflicts where namespaces added to Pine Script™ in the future
would
conflict with the names of enums in existing scripts, enum names can _shadow_ some
of Pineʼs
namespaces.
For example, one can declare an enum like the following, whose name shadows the
**syminfo.*** namespace:
However, using such a name for an enum is only allowed if the enumʼs fields **do
not** have
names matching any of the namespaceʼs built-ins. Otherwise, Pine will not be able
to
determine which value the script is supposed to use, resulting in a compilation
error:
Additionally, one cannot use any of Pineʼs built-in type names as the name of an
enum.
```
Previous
Objects
```
```
Next
Methods
```
```
Notice! This page contains advanced material. If you are a beginning Pine Script™
programmer, we recommend you become familiar with other, more accessible Pine
Script™ features before you venture here.
```
```
[export ]enum <enumName>
<field_1>[ = <title_1>]
<field_2>[ = <title_2>]
...
<field_N>[ = <title_N>]
```
```
Pine Script™
// @enum
An enumeration of named values representing buy, sell, and neutral
// @field buy Represents a "Buy signal" state.
// @field sell Represents a "Sell signal" state.
// @field neutral Represents a "neutral" state.
enum Signal
buy = "Buy signal"
sell = "Sell signal"
neutral
```
```
Pine Script™
enumName.fieldName
```
```
Pine Script™
mySignal = Signal.neutral
```
```
Pine Script™
Signal mySignal = na
```
```
Pine Script™
// @version= 6
indicator("Using enums demo")
// @enum An enumeration of oscillator choices.
enum OscType
rsi = "Relative Strength Index"
mfi = "Money Flow Index"
cci = "Commodity Channel Index"
// @variable An enumerator (member) of the `OscType` enum.
OscType oscInput = input.enum(OscType.rsi, "Oscillator type")
// @function
Calculates one of three oscillators based on a specified `select
// @param source The series of values to process.
// @param length The number of bars in the calculation.
// @param selection Determines which oscillator to calculate.
calcOscillator( float source, simple int length, OscType selection) =>
result = switch selection
OscType.rsi => ta.rsi(source, length)
OscType.mfi => ta.mfi(source, length)
OscType.cci => ta.cci(source, length)
// Plot the value of a `calcOscillator()` call with `oscInput` as the `selection`.
plot(calcOscillator(close, 20 , oscInput))
```
```
Pine Script™
// @version= 6
indicator("Incompatible enums demo")
// @enum An enumeration of oscillator choices.
enum OscType
rsi = "Relative Strength Index"
mfi = "Money Flow Index"
cci = "Commodity Channel Index"
// @enum
An enumeration of oscillator choices. Its fields DO NOT represent the same v
enum OscType
rsi = "Relative Strength Index"
mfi = "Money Flow Index"
cci = "Commodity Channel Index"
// @variable An enumerator (member) of the `OscType2` enum.
OscType2 oscInput = input.enum(OscType2.rsi, "Oscillator type")
// @function
Calculates one of three oscillators based on a specified `select
// @param source The series of values to process.
// @param length The number of bars in the calculation.
// @param selection Determines which oscillator to calculate.
calcOscillator( float source, simple int length, OscType selection) =>
result = switch selection
OscType.rsi => ta.rsi(source, length)
OscType.mfi => ta.mfi(source, length)
OscType.cci => ta.cci(source, length)
// Plot the value of a `calcOscillator()` call with `oscInput` as the `selection`.
// Raises a compilation error because only members of `OscType` are allowed.
plot(calcOscillator(close, 20 , oscInput))
```
```
Pine Script™
// @version= 6
indicator("Utilizing field titles demo")
// @enum
An enumeration of cryptocurrency exchanges. All field titles are the same as
enum Exchange
BINANCE
BITSTAMP
BITFINEX
COINBASE
KRAKEN
// @enum
An enumeration of cryptocurrency pairs. All the field titles are the same as
enum Pair
BTCUSD
ETHUSD
SOLUSD
XRPUSD
// @variable An enumerator (member) of the `Exchange` enum.
Exchange exchangeInput = input.enum(Exchange.BINANCE, "Exchange")
// @variable An enumerator (member) of the `Pair` enum.
Pair pairInput = input.enum(Pair.BTCUSD, "Pair")
// @variable The exchange-symbol pair for the data request.
simple string symbol = str.tostring(exchangeInput) + ":" + str.tostring(pairInput)
// Plot the `close` value requested from the `symbol` context.
plot(request.security(symbol, timeframe.period, close), "Requested close",
color.pur
```
```
Pine Script™
// @variable An enumeration of miscellaneous named members.
enum FooBar
foo
bar
baz
// @variable
An array that can only contain the following values: `FooBar.foo`, `FooB
array < FooBar > fooBarArray = array.new< FooBar >()
```
```
Pine Script™
// @version= 6
indicator("Collecting enum members demo", overlay = true)
// @enum An enumeration of named signal states.
enum Signal
strongBuy = "Strong buy"
buy = "Buy"
neutral = "Neutral"
sell = "Sell"
strongSell = "Strong sell"
// @variable The number of bars in the signal calculation.
int lengthInput = input.int( 50 , "Length", 2 )
// @variable
A map of `Signal.*` keys and "int" values counting the number of bars wi
// Allowed keys: `Signal.strongBuy`, `Signal.buy`, `Signal.neutral`, `Sign
a
var map < Signal , float > signalCounters = map.new< Signal , float >()
// @variable A single-cell table displaying the key-
value pairs of the `signalCounter
var table infoTable = table.new(position.top_right, 1 , 1 , chart.fg_color)
if barstate.isfirst
// Put `Signal.*`-"int" pairs into the `signalCounters` map to establish inserti
signalCounters.put(Signal.strongBuy, 0 )
signalCounters.put(Signal.buy, 0 )
signalCounters.put(Signal.neutral, 0 )
signalCounters.put(Signal.sell, 0 )
signalCounters.put(Signal.strongSell, 0 )
// Initialize the `infoTable` cell.
infoTable.cell( 0 , 0 , text_color = chart.bg_color, text_halign =
text.align_left,
// Calculate the EMA and Percent rank of `source` data over `length` bars.
float ema = ta.ema(close, lengthInput)
float rank = ta.percentrank(close, lengthInput)
// @variable
A `Signal` member representing the current signal state based on `ema` a
Signal signalState = switch
close > ema => rank > 70 ? Signal.strongBuy : rank > 50 ? Signal.buy : Signal.
close < ema => rank < 30 ? Signal.strongSell : rank < 50 ? Signal.sell : Signal.
=> Signal.neutral
// Add 1 to the value in the `signalCounters` map associated with the `signalState`
signalCounters.put(signalState, signalCounters.get(signalState) + 1 )
// Update the `infoTable` cell's text using the keys and values from the `signalCou
n
if barstate.islast
string tableText = ""
for [state, count] in signalCounters
tableText += str.tostring(state) + ": " + str.tostring(count) + "\n"
infoTable.cell_set_text( 0 , 0 , str.trim(tableText))
```
```
Pine Script™
// @version= 6
indicator("Shadowing demo")
enum syminfo
abcd
log.info(str.tostring(syminfo.abcd))
```
```
Pine Script™
// @version= 6
indicator("Name conflict demo")
enum syminfo
abcd
tickerid // This matches the built-in `syminfo.tickerid` variable, causing a com
log.info(str.tostring(syminfo.tickerid))
```
```
Notice! While it is possible for some enum names to shadow language namespaces,
as shown above, we recommend choosing unique names for enums wherever
possible for more readable code thatʼs easier to maintain.
```
```
ADVANCED
```
# Methods
## ## Introduction
Pine Script™ methods are specialized functions associated with values of specific
built-in
types, user-defined types, or enum types. They behave the same as regular functions
in most
regards while offering a shorter, more convenient syntax. Users can access methods
using
_dot notation_ syntax on variables of the associated type, similar to accessing the
fields of a
Pine Script™ object.
## ## Built-in methods
Pine Script™ includes built-in methods for all _special types_ , including array,
matrix, map, line,
linefill, box, polyline, label, and table. These methods provide users with a more
concise way
to call specialized routines for these types within their scripts.
and:
to get the value from an array **id** at the specified **index** , we can simply
use:
to achieve the same effect. This notation eliminates the need for users to
reference the
functionʼs namespace, as get() is a method of **id** in this context.
The following script computes Bollinger Bands over a specified number of prices
sampled
once every **n** bars. It calls array.push() and array.shift() to queue
**sourceInput** values
through the **sourceArray** , then array.avg() and array.stdev() to compute the
**sampleMean** and
**sampleDev**. The script then uses these values to calculate the **highBand** and
**lowBand** ,
which it plots on the chart along with the **sampleMean** :
Letʼs rewrite this code to utilize methods rather than built-in functions. In this
version, we
have replaced all built-in array.* functions in the script with equivalent methods:
Note that:
```
We call the array methods using sourceArray.* rather than referencing the array
namespace.
We do not include sourceArray as a parameter when we call the methods since they
already reference the object.
```
## ## User-defined methods
Pine Script™ allows users to define custom methods for use with objects of any
built-in or
user-defined type. Defining a method is essentially the same as defining a
function, but with
two key differences:
```
The method keyword must be included before the function name.
The type of the first parameter in the signature must be explicitly declared, as it
represents the type of object that the method will be associated with.
```
Letʼs apply user-defined methods to our previous Bollinger Bands example to
encapsulate
operations from the global scope, which will simplify the code and promote
reusability. See
this portion from the example:
Note that:
```
Just as with user-defined functions, we use the @function compiler annotation to
document method descriptions.
```
Now we can replace **sourceArray.push()** and **sourceArray.shift()** with
**sourceArray.maintainQueue()** in our example:
From here, we will further simplify our code by defining a method that handles all
Bollinger
Band calculations within its scope.
This **calcBB()** method invokes the avg() and stdev() methods on a **srcArray** to
update
**mean** and **dev** values when **calculate** is true. The method uses these
values to return a
tuple containing the basis, upper band, and lower band values respectively:
With this method, we can now remove Bollinger Band calculations from the global
scope and
improve code readability:
Note that:
```
Rather than using an if block in the global scope, we have defined a newSample
variable that is only true once every n bars. The maintainQueue() and calcBB()
methods use this value for their respective takeSample and calculate parameters.
Since the maintainQueue() method returns the object that it references, weʼre able
to
call calcBB() from the same line of code, as both methods apply to array<float>
instances.
```
Here is how the full script example looks now that weʼve applied our user-defined
methods:
## ## Method overloading
User-defined methods can override and overload existing built-in and user-defined
methods
with the same identifier. This capability allows users to define multiple routines
associated
with different parameter signatures under the same method name.
Now we can use these overloads to inspect some variables. This script uses
str.format() to
format the results from calling the **getType()** method on five different
variables into a single
**results** string, then displays the string in the **lbl** label using the built-
in set_text() method:
Note that:
```
The underlying type of each variable determines which overload of getType() the
compiler will use.
The method will append “(na)” to the output string when a variable is na to
demarcate
that it is empty.
```
## ## Advanced example
Letʼs apply what weʼve learned to construct a script that estimates the cumulative
distribution
of elements in an array, meaning the fraction of elements in the array that are
less than or
equal to any given value.
There are many ways in which we could choose to tackle this objective. For this
example, we
will start by defining a method to replace elements of an array, which will help us
count the
occurrences of elements within a range of values.
With this method, we can filter an array by value ranges to produce an array of
occurrences.
For example, the expression:
copies the **srcArray** object, replaces all elements between **min** and **val**
with 1.0, then
replaces all elements above **val** with 0.0. From here, itʼs easy to estimate the
output of the
cumulative distribution function at the **val** , as itʼs simply the average of the
resulting array:
Note that:
```
The compiler will only use this fill() overload instead of the built-in when the
user
provides innerValue , outerValue , lowerBound , and upperBound arguments in the
call.
If either lowerBound or upperBound is na , its value is ignored while filtering the
fill
range.
We are able to call copy() , fill() , and avg() successively on the same line of
code
because the first two methods return an array<float> instance.
```
We can now use this to define a method that will calculate our empirical
distribution values.
The following **eCDF()** method estimates a number of evenly spaced ascending
**steps** from
the cumulative distribution function of a **srcArray** and pushes the results into
a **cdfArray** :
Lastly, to ensure that our **eCDF()** method functions properly for arrays
containing small and
large values, we will define a method to normalize our arrays.
This **featureScale()** method uses array min() and range() methods to produce a
rescaled
copy of a **srcArray**. We will use this to normalize our arrays prior to invoking
the **eCDF()**
method:
Note that:
```
This method does not include special handling for divide by zero conditions. If rng
is 0,
the value of the array element will be na.
```
The full example below queues a **sourceArray** of size **length** with
**sourceInput** values
using our previous **maintainQueue()** method, normalizes the arrayʼs elements
using the
**featureScale()** method, then calls the **eCDF()** method to get an array of
estimates for **n**
evenly spaced steps on the distribution. The script then calls a user-defined
**makeLabel()**
function to display the estimates and prices in a label on the right side of the
chart:
```
Previous
Enums
```
```
Next
Arrays
```
```
Notice! This page contains advanced material. If you are a beginning Pine Script™
programmer, we recommend you become familiar with other, more accessible Pine
Script™ features before you venture here.
```
```
<namespace>.<functionName>([paramName =] <objectName>, ...)
```
```
<objectName>.<functionName>(...)
```
```
Pine Script™
array.get(id, index)
```
```
Pine Script™
id.get(index)
```
```
Pine Script™
// @version= 6
indicator("Custom Sample BB", overlay = true)
float sourceInput = input.source(close, "Source")
int samplesInput = input.int( 20 , "Samples")
int n = input.int( 10 , "Bars")
float multiplier = input.float(2.0, "StdDev")
var array < float > sourceArray = array.new< float >(samplesInput)
var float sampleMean = na
var float sampleDev = na
// Identify if `n` bars have passed.
if bar_index % n ==
// Update the queue.
array.push(sourceArray, sourceInput)
array.shift(sourceArray)
// Update the mean and standard deviaiton values.
sampleMean := array.avg(sourceArray)
sampleDev := array.stdev(sourceArray) * multiplier
// Calculate bands.
float highBand = sampleMean + sampleDev
float lowBand = sampleMean - sampleDev
plot(sampleMean, "Basis", color.orange)
plot(highBand, "Upper", color.lime)
plot(lowBand, "Lower", color.red)
```
```
Pine Script™
// @version= 6
indicator("Custom Sample BB", overlay = true)
float sourceInput = input.source(close, "Source")
int samplesInput = input.int( 20 , "Samples")
int n = input.int( 10 , "Bars")
float multiplier = input.float(2.0, "StdDev")
var array < float > sourceArray = array.new< float >(samplesInput)
var float sampleMean = na
var float sampleDev = na
// Identify if `n` bars have passed.
if bar_index % n ==
// Update the queue.
sourceArray.push(sourceInput)
sourceArray.shift()
// Update the mean and standard deviaiton values.
sampleMean := sourceArray.avg()
sampleDev := sourceArray.stdev() * multiplier
// Calculate band values.
float highBand = sampleMean + sampleDev
float lowBand = sampleMean - sampleDev
plot(sampleMean, "Basis", color.orange)
plot(highBand, "Upper", color.lime)
plot(lowBand, "Lower", color.red)
```
```
[export] method <functionName>(<paramType> <paramName> [=
<defaultValue>], ...) =>
<functionBlock>
```
```
Pine Script™
// Identify if `n` bars have passed.
if bar_index % n ==
// Update the queue.
sourceArray.push(sourceInput)
sourceArray.shift()
// Update the mean and standard deviaiton values.
sampleMean := sourceArray.avg()
sampleDev := sourceArray.stdev() * multiplier
// Calculate band values.
float highBand = sampleMean + sampleDev
float lowBand = sampleMean - sampleDev
```
```
Pine Script™
// @function Maintains a queue of the size of `srcArray`.
// It appends a `value` to the array and removes its oldest eleme
n
// @param srcArray (array<float>) The array where the queue is maintained.
// @param value (float) The new value to be added to the queue.
// The queue's oldest value is also removed, so its size is const
a
// @param
takeSample (bool) A new `value` is only pushed into the queue if this is t
// @returns (array<float>) `srcArray` object.
method maintainQueue( array < float > srcArray, float value, bool takeSample =
true) =>
if takeSample
srcArray.push(value)
srcArray.shift()
srcArray
```
```
Pine Script™
// Identify if `n` bars have passed.
if bar_index % n ==
// Update the queue.
sourceArray.maintainQueue(sourceInput)
// Update the mean and standard deviaiton values.
sampleMean := sourceArray.avg()
sampleDev := sourceArray.stdev() * multiplier
// Calculate band values.
float highBand = sampleMean + sampleDev
float lowBand = sampleMean - sampleDev
```
```
Pine Script™
// @function Computes Bollinger Band values from an array of data.
// @param srcArray (array<float>) The array where the queue is maintained.
// @param multiplier (float) Standard deviaiton multiplier.
// @param
calcuate (bool) The method will only calculate new values when this is t
// @returns
A tuple containing the basis, upper band, and lower band respec
method calcBB( array < float > srcArray, float mult, bool calculate = true) =>
var float mean = na
var float dev = na
if calculate
// Compute the mean and standard deviation of the array.
mean := srcArray.avg()
dev := srcArray.stdev() * mult
[mean, mean + dev, mean - dev]
```
```
Pine Script™
// Identify if `n` bars have passed.
bool newSample = bar_index % n ==
// Update the queue and compute new BB values on each new sample.
[sampleMean, highBand, lowBand] = sourceArray.maintainQueue(sourceInput,
newSample).
```
```
Pine Script™
// @version= 6
indicator("Custom Sample BB", overlay = true)
float sourceInput = input.source(close, "Source")
int samplesInput = input.int( 20 , "Samples")
int n = input.int( 10 , "Bars")
float multiplier = input.float(2.0, "StdDev")
var array < float > sourceArray = array.new< float >(samplesInput)
// @function Maintains a queue of the size of `srcArray`.
// It appends a `value` to the array and removes its oldest eleme
n
// @param srcArray (array<float>) The array where the queue is maintained.
// @param value (float) The new value to be added to the queue.
// The queue's oldest value is also removed, so its size is const
a
// @param
takeSample (bool) A new `value` is only pushed into the queue if this is t
// @returns (array<float>) `srcArray` object.
method maintainQueue( array < float > srcArray, float value, bool takeSample =
true) =>
if takeSample
srcArray.push(value)
srcArray.shift()
srcArray
// @function Computes Bollinger Band values from an array of data.
// @param srcArray (array<float>) The array where the queue is maintained.
// @param multiplier (float) Standard deviaiton multiplier.
// @param
calcuate (bool) The method will only calculate new values when this is t
// @returns
A tuple containing the basis, upper band, and lower band respec
method calcBB( array < float > srcArray, float mult, bool calculate = true) =>
var float mean = na
var float dev = na
if calculate
// Compute the mean and standard deviation of the array.
mean := srcArray.avg()
dev := srcArray.stdev() * mult
[mean, mean + dev, mean - dev]
// Identify if `n` bars have passed.
bool newSample = bar_index % n ==
// Update the queue and compute new BB values on each new sample.
[sampleMean, highBand, lowBand] = sourceArray.maintainQueue(sourceInput,
newSample).
plot(sampleMean, "Basis", color.orange)
plot(highBand, "Upper", color.lime)
plot(lowBand, "Lower", color.red)
```
```
Pine Script™
// @function Identifies an object's type.
// @param this Object to inspect.
// @returns (string) A string representation of the type.
method getType( int this) =>
na(this)? "int(na)" : "int"
method getType( float this) =>
na(this)? "float(na)" : "float"
method getType( bool this) =>
// "bool" values only have two states, `true` and `false`, but never `na`.
"bool"
method getType( color this) =>
na(this)? "color(na)" : "color"
method getType( string this) =>
na(this)? "string(na)" : "string"
```
```
Pine Script™
// @version= 6
indicator("Type Inspection")
// @function Identifies an object's type.
// @param this Object to inspect.
// @returns (string) A string representation of the type.
method getType( int this) =>
na(this)? "int(na)" : "int"
method getType( float this) =>
na(this)? "float(na)" : "float"
method getType( bool this) =>
na(this)? "bool(na)" : "bool"
method getType( color this) =>
na(this)? "color(na)" : "color"
method getType( string this) =>
na(this)? "string(na)" : "string"
a =
b = 1.
c = true
d = color.white
e = "1"
// Inspect variables and format results.
results = str.format(
"a: {0}\nb: {1}\nc: {2}\nd: {3}\ne: {4}",
a.getType(), b.getType(), c.getType(), d.getType(), e.getType()
)
var label lbl = label.new( 0 , 0 )
lbl.set_x(bar_index)
lbl.set_text(results)
```
```
Pine Script™
// @function
Replaces elements in a `srcArray` between `lowerBound` and `up
// and replaces elements outside the range with an `outerValue`.
// @param srcArray (array<float>) Array to modify.
// @param innerValue (float) Value to replace elements within the range with.
// @param outerValue (float) Value to replace elements outside the range with.
// @param lowerBound (float) Lowest value to replace with `innerValue`.
// @param upperBound (float) Highest value to replace with `innerValue`.
// @returns (array<float>) `srcArray` object.
method fill( array < float > srcArray, float innerValue, float outerValue, float
lowerBo
for [i, element] in srcArray
if (element >= lowerBound or na(lowerBound)) and (element <= upperBound or n
srcArray.set(i, innerValue)
else
srcArray.set(i, outerValue)
srcArray
```
```
Pine Script™
srcArray.copy().fill(1.0, 0.0, min, val)
```
```
Pine Script™
srcArray.copy().fill(1.0, 0.0, min, val).avg()
```
```
Pine Script™
// @function Estimates the empirical CDF of a `srcArray`.
// @param srcArray (array<float>) Array to calculate on.
// @param steps (int) Number of steps in the estimation.
// @returns (array<float>) Array of estimated CDF ratios.
method eCDF( array < float > srcArray, int steps) =>
float min = srcArray.min()
float rng = srcArray.range() / steps
array < float > cdfArray = array.new< float >()
// Add averages of `srcArray` filtered by value region to the `cdfArray`.
float val = min
for i = 1 to steps
val += rng
cdfArray.push(srcArray.copy().fill(1.0, 0.0, min, val).avg())
cdfArray
```
```
Pine Script™
// @function
Rescales the elements within a `srcArray` to the interval [0, 1]
// @param srcArray (array<float>) Array to normalize.
// @returns (array<float>) Normalized copy of the `srcArray`.
method featureScale( array < float > srcArray) =>
float min = srcArray.min()
float rng = srcArray.range()
array < float > scaledArray = array.new< float >()
// Push normalized `element` values into the `scaledArray`.
for element in srcArray
scaledArray.push((element - min) / rng)
scaledArray
```
```
Pine Script™
// @version= 6
indicator("Empirical Distribution", overlay = true)
float sourceInput = input.source(close, "Source")
int length = input.int( 20 , "Length")
int n = input.int( 20 , "Steps")
// @function Maintains a queue of the size of `srcArray`.
// It appends a `value` to the array and removes its oldest eleme
n
// @param srcArray (array<float>) The array where the queue is maintained.
// @param value (float) The new value to be added to the queue.
// The queue's oldest value is also removed, so its size is const
a
// @param
takeSample (bool) A new `value` is only pushed into the queue if this is t
// @returns (array<float>) `srcArray` object.
method maintainQueue( array < float > srcArray, float value, bool takeSample =
true) =>
if takeSample
srcArray.push(value)
srcArray.shift()
srcArray
// @function
Replaces elements in a `srcArray` between `lowerBound` and `up
// and replaces elements outside the range with an `outerValue`.
// @param srcArray (array<float>) Array to modify.
// @param innerValue (float) Value to replace elements within the range with.
// @param outerValue (float) Value to replace elements outside the range with.
// @param lowerBound (float) Lowest value to replace with `innerValue`.
// @param upperBound (float) Highest value to replace with `innerValue`.
// @returns (array<float>) `srcArray` object.
method fill( array < float > srcArray, float innerValue, float outerValue, float
lowerBo
for [i, element] in srcArray
if (element >= lowerBound or na(lowerBound)) and (element <= upperBound or n
srcArray.set(i, innerValue)
else
srcArray.set(i, outerValue)
srcArray
// @function Estimates the empirical CDF of a `srcArray`.
// @param srcArray (array<float>) Array to calculate on.
// @param steps (int) Number of steps in the estimation.
// @returns (array<float>) Array of estimated CDF ratios.
method eCDF( array < float > srcArray, int steps) =>
float min = srcArray.min()
float rng = srcArray.range() / steps
array < float > cdfArray = array.new< float >()
// Add averages of `srcArray` filtered by value region to the `cdfArray`.
float val = min
for i = 1 to steps
val += rng
cdfArray.push(srcArray.copy().fill(1.0, 0.0, min, val).avg())
cdfArray
// @function
Rescales the elements within a `srcArray` to the interval [0, 1]
// @param srcArray (array<float>) Array to normalize.
// @returns (array<float>) Normalized copy of the `srcArray`.
method featureScale( array < float > srcArray) =>
float min = srcArray.min()
float rng = srcArray.range()
array < float > scaledArray = array.new< float >()
// Push normalized `element` values into the `scaledArray`.
for element in srcArray
scaledArray.push((element - min) / rng)
scaledArray
// @function
Draws a label containing eCDF estimates in the format "{price}:
// @param srcArray (array<float>) Array of source values.
// @param cdfArray (array<float>) Array of CDF estimates.
// @returns (void)
makeLabel( array < float > srcArray, array < float > cdfArray) =>
float max = srcArray.max()
float rng = srcArray.range() / cdfArray.size()
string results = ""
var label lbl = label.new( 0 , 0 , "", style = label.style_label_left, text_font_f
// Add percentage strings to `results` starting from the `max`.
cdfArray.reverse()
for [i, element] in cdfArray
results += str.format("{0}: {1}%\n", max - i * rng, element * 100 )
// Update `lbl` attributes.
lbl.set_xy(bar_index + 1 , srcArray.avg())
lbl.set_text(results)
var array < float > sourceArray = array.new< float >(length)
// Add background color for the last `length` bars.
bgcolor(bar_index > last_bar_index - length? color.new(color.orange, 80 ) : na)
// Queue `sourceArray`, feature scale, then estimate the distribution over `n` step
s
array < float > distArray =
sourceArray.maintainQueue(sourceInput).featureScale().eCDF(
// Draw label.
makeLabel(sourceArray, distArray)
```
```
ADVANCED
```
# Arrays
## ## Introduction
Pine Script™ Arrays are one-dimensional collections that can hold multiple value
references.
Think of them as a better way to handle cases where one would otherwise need to
explicitly
declare a set of similar variables (e.g., **price00** , **price01** , **price02** ,
...).
All elements in an array must be of the same built-in type, user-defined type, or
enum type.
Scripts reference arrays using array IDs similar to the IDs of lines, labels, and
other _special
types_. Pine Script™ does not use an indexing operator to reference individual
array elements.
Instead, functions including array.get() and array.set() read and write the values
of array
elements.
Scripts reference the elements of an array using an _index_ , which starts at 0 and
extends to
the number of elements in the array minus one. Arrays in Pine Script™ can have a
dynamic
size that varies across bars, as one can change the number of elements in an array
on each
iteration of a script. Scripts can contain multiple array instances. The size of
arrays is limited
to 100,000 elements.
## ## Declaring arrays
Where **<type>** is a type template for the array that declares the type of values
it will contain,
and the **<expression>** returns either an array of the specified type or **na**.
When declaring a variable as an array, we can use the array keyword followed by a
type
template. Alternatively, we can use the **type** name followed by the **[]**
modifier (not to be
confused with the [] _history-referencing operator_ ).
This line of code declares an array variable named **prices** that points to
**na**. In this case, we
must specify the type to declare that the variable can reference arrays containing
“float”
values:
When declaring an array and the **<expression>** is not **na** , use one of the
following functions:
array.new<type>(size, initial_value), array.from(), or array.copy(). For
**array.new<type>(size,
initial_value)** functions, the arguments of the **size** and **initial_value**
parameters can
be “series” to allow dynamic sizing and initialization of array elements. The
following example
creates an array containing zero “float” elements, and this time, the array ID
returned by the
array.new<float>() function call is assigned to **prices** :
This line declares an array ID named **prices** pointing to an array containing two
elements,
each assigned to the barʼs **close** value:
To create an array and initialize its elements with different values, use
array.from(). This
function infers the arrayʼs size and the type of elements it will hold from the
arguments in the
function call. As with **array.new*** functions, it accepts “series” arguments. All
values
supplied to the function must be of the same type.
For example, all three of these lines of code will create identical “bool” arrays
with the same
two elements:
## ## Using `var` and `varip` keywords
Users can utilize var and varip keywords to instruct a script to declare an array
variable only
once on the first iteration of the script on the first chart bar. Array variables
declared using
these keywords point to the same array instances until explicitly reassigned,
allowing an array
and its element references to persist across bars.
When declaring an array variable using these keywords and pushing a new value to
the end of
the referenced array on each bar, the array will grow by one on each bar and be of
size
**bar_index + 1** (bar_index starts at zero) by the time the script executes on the
last bar, as
this code demonstrates:
The same code without the var keyword would re-declare the array on each bar. In
this case,
after execution of the array.push() call, the a.size() call would return a value of
1.
Scripts can write values to existing individual array elements using array.set(id,
index, value),
and read using array.get(id, index). When using these functions, it is imperative
that the
**index** in the function call is always less than or equal to the arrayʼs size
(because array
indices start at zero). To get the size of an array, use the array.size(id)
function.
The following example uses the set() method to populate a **fillColors** array with
instances
of one base color using different transparency levels. It then uses array.get() to
retrieve one of
the colors from the array based on the location of the bar with the highest price
within the last
**lookbackInput** bars:
This code is equivalent to the one above, but it uses array.unshift() to insert new
elements at
the _beginning_ of the **fillColors** array:
We can also use array.from() to create the same **fillColors** array with a single
function call:
The array.fill(id, value, index_from, index_to) function points all array elements,
or the
elements within the **index_from** to **index_to** range, to a specified **value**.
Without the last
two optional parameters, the function fills the whole array, so:
and:
only fills the second and third elements (at index 1 and 2) of the array with
**close**. Note how
array.fill()‘s last parameter, **index_to** , must be one greater than the last
index the function
will fill. The remaining elements will hold **na** values, as the array.new()
function call does not
contain an **initial_value** argument.
When looping through an arrayʼs element indices and the arrayʼs size is unknown,
one can use
the array.size() function to get the maximum index value. For example:
Note that:
```
We use the request.security_lower_tf() function which returns an array of close
prices at
the 1 minute timeframe.
This code example will throw an error if you use it on a chart timeframe smaller
than 1
minute.
for loops do not execute if the to expression is na. Note that the to value is only
evaluated once upon entry.
```
An alternative method to loop through an array is to use a for...in loop. This
approach is a
variation of the standard for loop that can iterate over the value references and
indices in an
array. Here is an example of how we can write the code example from above using a
**for...in** loop:
Note that:
```
for...in loops can return a tuple containing each index and corresponding element.
For
example, for [i, price] in a returns the i index and price value for each element
in a.
```
A while loop statement can also be used:
## ## Scope
Users can declare arrays within the global scope of a script, as well as the local
scopes of
functions, methods, and conditional structures. Unlike some of the other built-in
types,
namely _fundamental_ types, scripts can modify globally-assigned arrays from within
local
scopes, allowing users to implement global variables that any function in the
script can
directly interact with. We use the functionality here to calculate progressively
lower or higher
price levels:
## ## History referencing
To illustrate this, letʼs create a simple example to show how one can fetch the
previous barʼs
**close** value in two equivalent ways. This script uses the [ ] operator to get
the array instance
assigned to **a** on the previous bar, then uses the get() method to retrieve the
value of the
first element ( **previousClose1** ). For **previousClose2** , we use the history-
referencing
operator on the **close** variable directly to retrieve the value. As we see from
the plots,
**previousClose1** and **previousClose2** both return the same value:
## Inserting
The following three functions can insert new elements into an array.
array.insert() inserts a new element at the specified **index** and increases the
index of
existing elements at or after the **index** by one.
## ## Removing
These four functions remove elements from an array. The first three also return the
value of
the removed element.
array.remove() removes the element at the specified **index** and returns that
elementʼs value.
array.shift() removes the first element from an array and returns its value.
array.pop() removes the last element of an array and returns its value.
array.clear() removes all elements from an array. Note that clearing an array wonʼt
delete any
objects its elements referenced. See the example below that illustrates how this
works:
## ## Using an array as a stack
Stacks are LIFO (last in, first out) constructions. They behave somewhat like a
vertical pile of
books to which books can only be added or removed one at a time, always from the
top. Pine
Script™ arrays can be used as a stack, in which case we use the array.push() and
array.pop()
functions to add and remove elements at the end of the array.
**array.push(prices, close)** will add a new element to the end of the **prices**
array,
increasing the arrayʼs size by one.
**array.pop(prices)** will remove the end element from the **prices** array, return
its value and
decrease the arrayʼs size by one.
See how the functions are used here to track successive lows in rallies:
Queues are FIFO (first in, first out) constructions. They behave somewhat like cars
arriving at
a red light. New cars are queued at the end of the line, and the first car to leave
will be the
first one that arrived to the red light.
In the following code example, we let users decide through the scriptʼs inputs how
many
labels they want to have on their chart. We use that quantity to determine the size
of the array
of labels we then create, initializing the arrayʼs elements to **na**.
When a new pivot is detected, we create a label for it, saving the labelʼs ID in
the **pLabel**
variable. We then queue the ID of that label by using array.push() to append the
new labelʼs ID
to the end of the array, making our array size one greater than the maximum number
of labels
to keep on the chart.
Lastly, we de-queue the oldest label by removing the arrayʼs first element using
array.shift()
and deleting the label referenced by that array elementʼs value. As we have now de-
queued
an element from our queue, the array contains **pivotCountInput** elements once
again. Note
that on the datasetʼs first bars we will be deleting **na** label IDs until the
maximum number of
labels has been created, but this does not cause runtime errors. Letʼs look at our
code:
## ## Calculations on arrays
While series variables can be viewed as a horizontal set of values stretching back
in time, Pine
Script™‘s one-dimensional arrays can be viewed as vertical structures residing on
each bar.
As an arrayʼs set of elements is not a time series, Pine Script™‘s usual
mathematical functions
are not allowed on them. Special-purpose functions must be used to operate on all
of an
arrayʼs values. The available functions are: array.abs(), array.avg(),
array.covariance(),
array.min(), array.max(), array.median(), array.mode(),
array.percentile_linear_interpolation(),
array.percentile_nearest_rank(), array.percentrank(), array.range(),
array.standardize(),
array.stdev(), array.sum(), array.variance().
Note that contrary to the usual mathematical functions in Pine Script™, those used
on arrays
do not return **na** when some of the values they calculate on have **na** values.
There are a few
exceptions to this rule:
```
When all array elements have na value or the array contains no elements, na is
returned. array.standardize() however, will return an empty array.
array.mode() will return na when no mode is found.
```
## ## Manipulating arrays
## Concatenation
Two arrays can be merged — or concatenated — using array.concat(). When arrays are
concatenated, the second array is appended to the end of the first, so the first
array is
modified while the second one remains intact. The function returns the array ID of
the first
array:
## ## Copying
You can copy an array using array.copy(). Here we copy the array **a** to a new
array named
**_b** :
Note that simply using **_b = a** in the previous example would not have copied the
array, but
only its ID. From thereon, both variables would point to the same array, so using
either one
would affect the same array.
## ## Joining
Use array.join() to concatenate all of the elements in the array into a string and
separate these
elements with the specified separator:
## ## Sorting
## ## Reversing
## ## Slicing
The shallow copy created by the slice acts like a window on the parent arrayʼs
content. The
indices used for the slice define the windowʼs position and size over the parent
array. If, as in
the example below, a slice is created from the first three elements of an array
(indices 0 to 2),
then regardless of changes made to the parent array, and as long as it contains at
least three
elements, the shallow copy will always contain the parent arrayʼs first three
elements.
Additionally, once the shallow copy is created, operations on the copy are mirrored
on the
parent array. Adding an element to the end of the shallow copy, as is done in the
following
example, will widen the window by one element and also insert that element in the
parent
array at index 3. In this example, to slice the subset from index 0 to index 2 of
array **a** , we
must use **_sliceOfA = array.slice(a, 0, 3)** :
## ## Searching arrays
We can also perform a binary search on an array but note that performing a binary
search on
an array means that the array will first need to be sorted in ascending order only.
The
array.binary_search() function will return the valueʼs index if it was found or -1
if it wasnʼt. If
we want to always return an existing index from the array even if our chosen value
wasnʼt
found, then we can use one of the other binary search functions available. The
array.binary_search_leftmost() function, which returns an index if the value was
found or the
first index to the left where the value would be found. The
array.binary_search_rightmost()
function is almost identical and returns an index if the value was found or the
first index to the
right where the value would be found.
## ## Error handling
Malformed **array.*()** call syntax in Pine scripts will cause the usual
**compiler** error
messages to appear in Pine Editorʼs console, at the bottom of the window, when you
save a
script. Refer to the Pine Script™ v 6 Reference Manual when in doubt regarding the
exact
syntax of function calls.
Scripts using arrays can also throw **runtime** errors, which appear as an
exclamation mark next
to the indicatorʼs name on the chart. We discuss those runtime errors in this
section.
This will most probably be the most frequent error you encounter. It will happen
when you
reference an nonexistent array index. The “xx” value will be the value of the
faulty index you
tried to use, and “yy” will be the size of the array. Recall that array indices
start at zero — not
one — and end at the arrayʼs size, minus one. An array of size 3ʼs last valid index
is thus **2**.
To avoid this error, you must make provisions in your code logic to prevent using
an index
lying outside of the arrayʼs index boundaries. This code will generate the error
because the
last index we use in the loop is outside the valid index range for the array:
When you size arrays dynamically using a field in your scriptʼs _Settings/Inputs_
tab, protect the
boundaries of that value using input.int()‘s **minval** and **maxval** parameters:
or:
This error will appear if your code attempts to declare an array with a size
greater than
100,000. It will also occur if, while dynamically appending elements to an array, a
new element
would increase the arrayʼs size past the maximum.
We havenʼt found any use for arrays of negative size yet, but if you ever do, we
may allow
them :)
This error will occur if array.shift() is called to remove the first element of an
empty array.
This error will occur if array.pop() is called to remove the last element of an
empty array.
When two indices are used in functions such as array.slice(), the first index must
always be
smaller than the second one.
This message occurs whenever the parent arrayʼs size is modified in such a way that
it makes
the shallow copy created by a slice point outside the boundaries of the parent
array. This
code will reproduce it because after creating a slice from index 3 to 4 (the last
two elements
of our five-element parent array), we remove the parentʼs first element, making its
size four
and its last index 3. From that moment on, the shallow copy which is still poiting
to the
“window” at the parent arrayʼs indices 3 to 4, is pointing out of the parent
arrayʼs boundaries:
```
Previous
Methods
```
```
Next
Matrices
```
```
Notice! This page contains advanced material. If you are a beginning Pine Script™
programmer, we recommend you become familiar with other, more accessible Pine
Script™ features before you venture here.
```
```
Notice! We will use beginning of an array to designate index 0, and end of an array
to
designate the arrayʼs element with the highest index value. We will also extend the
meaning of array to include array IDs, for the sake of brevity.
```
```
[var/varip ][array<type>/<type[]> ]<identifier> = <expression>
```
```
Pine Script™
array < float > prices = na
```
```
Pine Script™
float [] prices = na
```
```
Pine Script™
prices = array.new< float >( 0 )
```
```
Notice! The array.* namespace also contains type-specific functions for creating
arrays, including array.new_int(), array.new_float(), array.new_bool(),
array.new_color(), array.new_string(), array.new_line(), array.new_linefill(),
array.new_label(), array.new_box() and array.new_table(). The array.new<type>()
function can create an array of any type, including user-defined types.
```
```
Pine Script™
prices = array.new< float >( 2 , close)
```
```
Pine Script™
statesArray = array.from(close > open, high != close)
bool [] statesArray = array.from(close > open, high != close)
array < bool > statesArray = array.from(close > open, high != close)
```
```
Pine Script™
// @version= 6
indicator("Using `var`")
// @variable An array that expands its size by 1 on each bar.
var a = array.new< float >( 0 )
array.push(a, close)
if barstate.islast
// @variable A string containing the size of `a` and the current `bar_index` valu
string labelText = "Array size: " + str.tostring(a.size()) + "\nbar_index: " + s
// Display the `labelText`.
label.new(bar_index, 0 , labelText, size = size.large)
```
```
Notice! Array variables declared using varip behave as ones using var on historical
data, but they update their values for realtime bars (i.e., the bars since the
scriptʼs last
compilation) on each new price tick. Arrays assigned to varip variables can only
hold
int, float, bool, color, or string types or user-defined types that exclusively
contain
within their fields these types or collections (arrays, matrices) of these types.
```
```
Pine Script™
// @version= 6
indicator("Distance from high", "", true)
lookbackInput = input.int( 100 )
FILL_COLOR = color.green
// Declare array and set its values on the first bar only.
var fillColors = array.new< color >( 5 )
if barstate.isfirst
// Initialize the array elements with progressively lighter shades of the fill c
fillColors.set( 0 , color.new(FILL_COLOR, 70 ))
fillColors.set( 1 , color.new(FILL_COLOR, 75 ))
fillColors.set( 2 , color.new(FILL_COLOR, 80 ))
fillColors.set( 3 , color.new(FILL_COLOR, 85 ))
fillColors.set( 4 , color.new(FILL_COLOR, 90 ))
// Find the offset to highest high. Change its sign because the function returns a
n
lastHiBar = - ta.highestbars(high, lookbackInput)
// Convert the offset to an array index, capping it to 4 to avoid a runtime error.
// The index used by `array.get()` will be the equivalent of `floor(fillNo)`.
fillNo = math.min(lastHiBar / (lookbackInput / 5 ), 4 )
// Set background to a progressively lighter fill with increasing distance from loc
a
bgcolor(array.get(fillColors, fillNo))
// Plot key values to the Data Window for debugging.
plotchar(lastHiBar, "lastHiBar", "", location.top, size = size.tiny)
plotchar(fillNo, "fillNo", "", location.top, size = size.tiny)
```
```
Pine Script™
// Declare array and set its values on the first bar only.
var fillColors = array.new< color >( 0 )
if barstate.isfirst
// Initialize the array elements with progressively lighter shades of the fill c
array.push(fillColors, color.new(FILL_COLOR, 70 ))
array.push(fillColors, color.new(FILL_COLOR, 75 ))
array.push(fillColors, color.new(FILL_COLOR, 80 ))
array.push(fillColors, color.new(FILL_COLOR, 85 ))
array.push(fillColors, color.new(FILL_COLOR, 90 ))
```
```
Pine Script™
// Declare array and set its values on the first bar only.
var fillColors = array.new< color >( 0 )
if barstate.isfirst
// Initialize the array elements with progressively lighter shades of the fill c
array.unshift(fillColors, color.new(FILL_COLOR, 90 ))
array.unshift(fillColors, color.new(FILL_COLOR, 85 ))
array.unshift(fillColors, color.new(FILL_COLOR, 80 ))
array.unshift(fillColors, color.new(FILL_COLOR, 75 ))
array.unshift(fillColors, color.new(FILL_COLOR, 70 ))
```
```
Pine Script™
// @version= 6
indicator("Using `var`")
FILL_COLOR = color.green
var array < color > fillColors = array.from(
color.new(FILL_COLOR, 70 ),
color.new(FILL_COLOR, 75 ),
color.new(FILL_COLOR, 80 ),
color.new(FILL_COLOR, 85 ),
color.new(FILL_COLOR, 90 )
)
// Cycle background through the array's colors.
bgcolor(array.get(fillColors, bar_index % (fillColors.size())))
```
```
Pine Script™
a = array.new< float >( 10 , close)
```
```
Pine Script™
a = array.new< float >( 10 )
a.fill(close)
```
```
Pine Script™
a = array.new< float >( 10 )
a.fill(close, 1 , 3 )
```
```
Pine Script™
// @version= 6
indicator("Protected `for` loop", overlay = true)
// @variable An array of `close` prices from the 1-minute timeframe.
array < float > a = request.security_lower_tf(syminfo.tickerid, "1", close)
// @variable A string representation of the elements in `a`.
string labelText = ""
for i = 0 to (array.size(a) == 0 ? na : array.size(a) - 1 )
labelText += str.tostring(array.get(a, i)) + "\n"
label.new(bar_index, high, text = labelText)
```
```
Pine Script™
// @version= 6
indicator("`for...in` loop", overlay = true)
// @variable An array of `close` prices from the 1-minute timeframe.
array < float > a = request.security_lower_tf(syminfo.tickerid, "1", close)
// @variable A string representation of the elements in `a`.
string labelText = ""
for price in a
labelText += str.tostring(price) + "\n"
label.new(bar_index, high, text = labelText)
```
```
Pine Script™
// @version= 6
indicator("`while` loop", overlay = true)
array < float > a = request.security_lower_tf(syminfo.tickerid, "1", close)
string labelText = ""
int i =
while i < array.size(a)
labelText += str.tostring(array.get(a, i)) + "\n"
i +=
label.new(bar_index, high, text = labelText)
```
```
Pine Script™
// @version= 6
indicator("Bands", "", true)
// @variable The distance ratio between plotted price levels.
factorInput = 1 + (input.float(-2., "Step %") / 100 )
// @variable A single-
value array holding the lowest `ohlc4` value within a 50 bar wi
level = array.new< float >( 1 , ta.lowest(ohlc4, 50 )[ 10 ])
nextLevel(val) =>
newLevel = level.get( 0 ) * val
// Write new level to the global `level` array so we can use it as the base in t
level.set( 0 , newLevel)
newLevel
plot(nextLevel( 1 ))
plot(nextLevel(factorInput))
plot(nextLevel(factorInput))
plot(nextLevel(factorInput))
```
```
Pine Script™
// @version= 6
indicator("History referencing")
// @variable A single-value array declared on each bar.
a = array.new< float >( 1 )
// Set the value of the only element in `a` to `close`.
array.set(a, 0 , close)
// @variable The array instance assigned to `a` on the previous bar.
previous = a[ 1 ]
previousClose1 = na(previous)? na : previous.get( 0 )
previousClose2 = close[ 1 ]
plot(previousClose1, "previousClose1", color.gray, 6 )
plot(previousClose2, "previousClose2", color.white, 2 )
```
```
Pine Script™
// @version= 6
indicator("`array.insert()`")
a = array.new< float >( 5 , 0 )
for i = 0 to
array.set(a, i, i + 1 )
if barstate.islast
label.new(bar_index, 0 , "BEFORE\na: " + str.tostring(a), size = size.large)
array.insert(a, 2 , 999 )
label.new(bar_index, 0 , "AFTER\na: " + str.tostring(a), style = label.style_labe
```
```
Pine Script™
// @version= 6
indicator("`array.clear()` example", overlay = true)
// Create a label array and add a label to the array on each new bar.
var a = array.new< label >()
label lbl = label.new(bar_index, high, "Text", color = color.red)
array.push(a, lbl)
var table t = table.new(position.top_right, 1 , 1 )
// Clear the array on the last bar. This doesn't remove the labels from the chart.
if barstate.islast
array.clear(a)
table.cell(t, 0 , 0 , "Array elements count: " + str.tostring(array.size(a)),
bgco
```
```
Pine Script™
// @version= 6
indicator("Lows from new highs", "", true)
var lows = array.new< float >( 0 )
flushLows = false
// Remove last element from the stack when `_cond` is true.
array_pop(id, cond) => cond and array.size(id) > 0 ? array.pop(id) : float(na)
if ta.rising(high, 1 )
// Rising highs; push a new low on the stack.
lows.push(low)
// Force the return type of this `if` block to be the same as that of the next b
bool(na)
else if lows.size() >= 4 or low < array.min(lows)
// We have at least 4 lows or price has breached the lowest low;
// sort lows and set flag indicating we will plot and flush the levels.
array.sort(lows, order.ascending)
flushLows := true
// If needed, plot and flush lows.
lowLevel = array_pop(lows, flushLows)
plot(lowLevel, "Low 1", low > lowLevel? color.silver : color.purple, 2 ,
plot.style_
lowLevel := array_pop(lows, flushLows)
plot(lowLevel, "Low 2", low > lowLevel? color.silver : color.purple, 3 ,
plot.style_
lowLevel := array_pop(lows, flushLows)
plot(lowLevel, "Low 3", low > lowLevel? color.silver : color.purple, 4 ,
plot.style_
lowLevel := array_pop(lows, flushLows)
plot(lowLevel, "Low 4", low > lowLevel? color.silver : color.purple, 5 ,
plot.style_
if flushLows
// Clear remaining levels after the last 4 have been plotted.
lows.clear()
```
```
Pine Script™
// @version= 6
MAX_LABELS =
indicator("Show Last n High Pivots", "", true, max_labels_count = MAX_LABELS)
pivotCountInput = input.int( 5 , "How many pivots to show", minval = 0 , maxval =
MAX_L
pivotLegsInput = input.int( 3 , "Pivot legs", minval = 1 , maxval = 5 )
// Create an array containing the user-selected max count of label IDs.
var labelIds = array.new< label >(pivotCountInput)
pHi = ta.pivothigh(pivotLegsInput, pivotLegsInput)
if not na(pHi)
// New pivot found; plot its label `i_pivotLegs` bars back.
pLabel = label.new(bar_index[pivotLegsInput], pHi, str.tostring(pHi, format.mint
// Queue the new label's ID by appending it to the end of the array.
array.push(labelIds, pLabel)
// De-queue the oldest label ID from the queue and delete the corresponding labe
label.delete(array.shift(labelIds))
```
```
Pine Script™
// @version= 6
indicator("`array.concat()`")
a = array.new< float >( 0 )
b = array.new< float >( 0 )
array.push(a, 0 )
array.push(a, 1 )
array.push(b, 2 )
array.push(b, 3 )
if barstate.islast
label.new(bar_index, 0 , "BEFORE\na: " + str.tostring(a) + "\nb: " + str.tostring
c = array.concat(a, b)
array.push(c, 4 )
label.new(bar_index, 0 , "AFTER\na: " + str.tostring(a) + "\nb: " + str.tostring(
```
```
Pine Script™
// @version= 6
indicator("`array.copy()`")
a = array.new< float >( 0 )
array.push(a, 0 )
array.push(a, 1 )
if barstate.islast
b = array.copy(a)
array.push(b, 2 )
label.new(bar_index, 0 , "a: " + str.tostring(a) + "\nb: " + str.tostring(b), siz
```
```
Pine Script™
// @version= 6
indicator("")
v1 = array.new< string >( 10 , "test")
v2 = array.new< string >( 10 , "test")
array.push(v2, "test1")
v3 = array.new_float( 5 , 5 )
v4 = array.new_int( 5 , 5 )
l1 = label.new(bar_index, close, array.join(v1))
l2 = label.new(bar_index, close, array.join(v2, ","))
l3 = label.new(bar_index, close, array.join(v3, ","))
l4 = label.new(bar_index, close, array.join(v4, ","))
```
```
Pine Script™
// @version= 6
indicator("`array.sort()`")
a = array.new< float >( 0 )
b = array.new< float >( 0 )
array.push(a, 2 )
array.push(a, 0 )
array.push(a, 1 )
array.push(b, 4 )
array.push(b, 3 )
array.push(b, 5 )
if barstate.islast
barUp = close > open
array.sort(barUp? a : b, barUp? order.ascending : order.descending)
label.new(bar_index, 0 ,
"a " + (barUp? "is sorted ▲: " : "is not sorted: ") + str.tostring(a) + "\n
"b " + (barUp? "is not sorted: " : "is sorted ▼: ") + str.tostring(b), size
```
```
Pine Script™
// @version= 6
indicator("`array.reverse()`")
a = array.new< float >( 0 )
array.push(a, 0 )
array.push(a, 1 )
array.push(a, 2 )
if barstate.islast
array.reverse(a)
label.new(bar_index, 0 , "a: " + str.tostring(a))
```
```
Pine Script™
// @version= 6
indicator("`array.slice()`")
a = array.new< float >( 0 )
array.push(a, 0 )
array.push(a, 1 )
array.push(a, 2 )
array.push(a, 3 )
if barstate.islast
// Create a shadow of elements at index 1 and 2 from array `a`.
sliceOfA = array.slice(a, 0 , 3 )
label.new(bar_index, 0 , "BEFORE\na: " + str.tostring(a) + "\nsliceOfA: " + str.t
// Remove first element of parent array `a`.
array.remove(a, 0 )
// Add a new element at the end of the shallow copy, thus also affecting the ori
array.push(sliceOfA, 4 )
label.new(bar_index, 0 , "AFTER\na: " + str.tostring(a) + "\nsliceOfA: " + str.to
```
```
Pine Script™
// @version= 6
indicator("Searching in arrays")
valueInput = input.int( 1 )
a = array.new< float >( 0 )
array.push(a, 0 )
array.push(a, 1 )
array.push(a, 2 )
array.push(a, 1 )
if barstate.islast
valueFound = array.includes(a, valueInput)
firstIndexFound = array.indexof(a, valueInput)
lastIndexFound = array.lastindexof(a, valueInput)
label.new(bar_index, 0 , "a: " + str.tostring(a) +
"\nFirst " + str.tostring(valueInput) + (firstIndexFound != -1? " value was f
"\nLast " + str.tostring(valueInput) + (lastIndexFound != -1? " value was f
```
```
Pine Script™
// @version= 6
indicator("Out of bounds index")
a = array.new< float >( 3 )
for i = 1 to
array.set(a, i, i)
plot(array.pop(a))
```
```
Pine Script™
for i = 0 to
```
```
Pine Script™
// @version= 6
indicator("Protected `for` loop")
sizeInput = input.int( 0 , "Array size", minval = 0 , maxval = 100000 )
a = array.new< float >(sizeInput)
for i = 0 to (array.size(a) == 0 ? na : array.size(a) - 1 )
array.set(a, i, i)
plot(array.pop(a))
```
```
Pine Script™
// @version= 6
indicator("Protected array size")
sizeInput = input.int( 10 , "Array size", minval = 1 , maxval = 100000 )
a = array.new< float >(sizeInput)
for i = 0 to sizeInput -
array.set(a, i, i)
plot(array.size(a))
```
```
Pine Script™
// @version= 6
indicator("Out of bounds index")
array < int > a = na
array.push(a, 111 )
label.new(bar_index, 0 , "a: " + str.tostring(a))
```
```
Pine Script™
array < int > a = array.new_int( 0 )
```
```
Pine Script™
a = array.new_int( 0 )
```
```
Pine Script™
// @version= 6
indicator("Slice out of bounds")
a = array.new< float >( 5 , 0 )
b = array.slice(a, 3 , 5 )
array.remove(a, 0 )
c = array.indexof(b, 2 )
plot(c)
```
```
ADVANCED
```
# Matrices
## ## Introduction
Pine Script™ Matrices are collections that store value references in a rectangular
format. They
are the equivalent of two-dimensional array objects with functions and methods for
inspection, modification, and specialized calculations. As with arrays, all matrix
elements must
be of the same type, user-defined type, or enum type.
Matrices reference their elements using _two_ indices: one index for their _rows_
and the other for
their _columns_. Each index starts at 0 and extends to the number of rows/columns
in the matrix
minus one. Matrices in Pine can have dynamic numbers of rows and columns that vary
across
bars. The total number of elements within a matrix is the _product_ of the number
of rows and
columns (e.g., a 5 x 5 matrix has a total of 25). Like arrays, the total number of
elements in a
matrix cannot exceed 100,000.
## ## Declaring a matrix
Where **<type>** is a type template for the matrix that declares the type of values
it will contain,
and the **<expression>** returns either a matrix instance of the type or **na**.
When declaring a matrix variable as **na** , users must specify that the identifier
will reference
matrices of a specific type by including the matrix keyword followed by a type
template.
When a matrix variable is not assigned to **na** , the matrix keyword and its type
template are
optional, as the compiler will use the type information from the object the
variable references.
As with other variables, users can include the var or varip keywords to instruct a
script to
declare a matrix variable only once rather than on every bar. A matrix variable
declared with
this keyword will point to the same instance throughout the span of the chart
unless the script
explicitly assigns another matrix to it, allowing a matrix and its element
references to persist
between script iterations.
This script declares an **m** variable assigned to a matrix that holds a single row
of two int
elements using the var keyword. On every 20 th bar, the script adds 1 to the first
element on
the first row of the **m** matrix. The plot() call displays this element on the
chart. As we see
from the plot, the value of m.get(0, 0) persists between bars, never returning to
the initial
value of 0:
To retrieve the value from a matrix at a specified **row** and **column** index,
use matrix.get().
This function locates the specified matrix element and returns its value.
Similarly, to overwrite
a specific elementʼs value, use matrix.set() to assign the element at the specified
**row** and
**column** to a new **value**.
The example below defines a square matrix **m** with two rows and columns and an
**initial_value** of 0 for all elements on the first bar. The script adds 1 to each
elementʼs value
on different bars using the m.get() and m.set() methods. It updates the first rowʼs
first value
once every 11 bars, the first rowʼs second value once every seven bars, the second
rowʼs first
value once every five bars, and the second rowʼs second value once every three
bars. The
script plots each elementʼs value on the chart:
## ## `matrix.fill()`
To overwrite all matrix elements with a specific value, use matrix.fill(). This
function points all
items in the entire matrix or within the **from_row/column** and **to_row/column**
index range to
the **value** specified in the call. For example, this snippet declares a 4 x 4
square matrix, then
fills its elements with a random value:
Note when using matrix.fill() with matrices containing special types (line,
linefill, box, polyline,
label, table, or chart.point) or UDTs, all replaced elements will point to the same
object
passed in the function call.
This script declares a matrix with four rows and columns of label references, which
it fills with
a new label object on the first bar. On each bar, the script sets the **x**
attribute of the label
referenced at row 0, column 0 to bar_index, and the **text** attribute of the one
referenced at
row 3, column 3 to the number of labels on the chart. Although the matrix can
reference 16
(4x4) labels, each element points to the _same_ instance, resulting in only one
label on the chart
that updates its **x** and **text** attributes on each bar:
## Retrieving
Matrices facilitate the retrieval of all values from a specific row or column via
the matrix.row()
and matrix.col() functions. These functions return the values as an array object
sized
according to the other dimension of the matrix, i.e., the size of a matrix.row()
array equals the
number of columns and the size of a matrix.col() array equals the number of rows.
The script below populates a 3 x 2 **m** matrix with the values 1 - 6 on the first
chart bar. It calls
the m.row() and m.col() methods to access the first row and column arrays from the
matrix
and displays them on the chart in a label along with the array sizes:
Note that:
```
To get the sizes of the arrays displayed in the label, we used the rows() and
columns()
methods rather than array.size() to demonstrate that the size of the row0 array
equals
the number of columns and the size of the column0 array equals the number of rows.
```
matrix.row() and matrix.col() copy the references in a row/column to a new array.
Modifications to the arrays returned by these functions do not directly affect the
elements or
the shape of a matrix.
Here, weʼve modified the previous script to set the first element of **row0** to 10
via the
array.set() method before displaying the label. This script also plots the value
from row 0,
column 0. As we see, the label shows that the first element of the **row0** array
is 10. However,
the plot shows that the corresponding matrix element still has a value of 1:
This script contains a custom **myUDT** type containing a **value** field with an
initial value of 0. It
declares a 1 x 1 **m** matrix to hold a single **myUDT** instance on the first bar,
then calls **m.row(0)**
to copy the first row of the matrix as an array. On every chart bar, the script
adds 1 to the
**value** field of the first **row** array element. In this case, the **value**
field of the matrix element
increases on every bar as well since both elements reference the same object:
## ## Inserting
Scripts can add new rows and columns to a matrix via matrix.add_row() and
matrix.add_col().
These functions insert the value references from an array into a matrix at the
specified
**row/column** index. If the **id** matrix is empty (has no rows or columns), the
**array_id** in the
call can be of any size. If a row/column exists at the specified index, the matrix
increases the
index value for the existing row/column and all after it by 1.
The script below declares an empty **m** matrix and inserts rows and columns using
the
m.add_row() and m.add_col() methods. It first inserts an array with three elements
at row 0,
turning **m** into a 1 x 3 matrix, then another at row 1, changing the shape to 2
x3. After that, the
script inserts another array at row 0, which changes the shape of **m** to 3 x 3
and shifts the
index of all rows previously at index 0 and higher. It inserts another array at the
last column
index, changing the shape to 3 x4. Finally, it adds an array with four values at
the end row
index.
The resulting matrix has four rows and columns and contains values 1-16 in
ascending order.
The script displays the rows of **m** after each row/column insertion with a user-
defined
**debugLabel()** function to visualize the process:
## ## Removing
For this example, weʼve added these lines of code to our “Rows and columns demo”
script
from the section above:
This code removes the first row and the last column of the **m** matrix using the
m.remove_row() and m.remove_col() methods and displays the rows in a label at
**bar_index +
30**. As we can see, **m** has a 3 x 3 shape after executing this block, and the
index values for all
existing rows are reduced by 1:
## ## Swapping
To swap the rows and columns of a matrix without altering its dimensions, use
matrix.swap_rows() and matrix.swap_columns(). These functions swap the locations of
the
elements at the **row1/column1** and **row2/column2** indices.
Letʼs add the following lines to the previous example, which swap the first and
last rows of **m**
and display the changes in a label at **bar_index + 40** :
In the new label, we see the matrix has the same number of rows as before, and the
first and
last rows have traded places:
## ## Replacing
In the following code, weʼve defined a **replaceRow()** method that uses the
add_row() method
to insert the new **values** at the **row** index and uses the remove_row() method
to remove the
old row that moved to the **row + 1** index. This script uses the **replaceRow()**
method to fill
the rows of a 3 x 3 matrix with the numbers 1-9. It draws a label on the chart
before and after
replacing the rows using the custom **debugLabel()** method:
## `for`
When a script only needs to iterate over the row/column indices in a matrix, the
most common
method is to use for loops. For example, this line creates a loop with a **row**
value that starts
at 0 and increases by one until it reaches one less than the number of rows in the
**m** matrix
(i.e., the last row index):
To iterate over all index values in the **m** matrix, we can create a _nested_ loop
that iterates over
each **column** index on each **row** value:
Letʼs use this nested structure to create a method that visualizes matrix elements.
In the
script below, weʼve defined a **toTable()** method that displays the elements of a
matrix within
a table object. It iterates over each **row** index and over each **column** index
on every **row**.
Within the loop, it converts each element to a string to display in the
corresponding table cell.
On the first bar, the script creates an empty **m** matrix, populates it with rows,
and calls
**m.toTable()** to display its elements:
## ## `for...in`
When a script needs to iterate over and retrieve the rows of a matrix, using the
for...in
structure is often preferred over the standard **for** loop. This structure
directly references the
row arrays in a matrix, making it a more convenient option for such use cases. For
example,
this line creates a loop that returns a **row** array for each row in the **m**
matrix:
The following indicator calculates the moving average of OHLC data with an input
**length**
and displays the values on the chart. The custom **rowWiseAvg()** method loops
through the
rows of a matrix using a **for...in** structure to produce an array containing the
array.avg() of
each **row**.
On the first chart bar, the script creates a new **m** matrix with four rows and
**length** columns,
which it queues a new column of OHLC data into via the m.add_col() and
m.remove_col()
methods on each subsequent bar. It uses **m.rowWiseAvg()** to calculate the array
of row-wise
**averages** , then it plots the element values on the chart:
Note that:
```
for...in loops can also reference the index value of each row. For example, for [i,
row] in m creates a tuple containing the i row index and the corresponding row
array from the m matrix on each loop iteration.
```
## ## Copying a matrix
## Shallow copies
Pine scripts can copy matrices via matrix.copy(). This function returns a _shallow
copy_ of a
matrix that does not affect the shape of the original matrix or its references.
For example, this script assigns a new matrix to the **myMatrix** variable and adds
two
columns. It creates a new **myCopy** matrix from **myMatrix** using the
myMatrix.copy() method,
then adds a new row. It displays the rows of both matrices in labels via the user-
defined
**debugLabel()** function:
Itʼs important to note that the elements within shallow copies of a matrix point to
the same
values as the original matrix. When matrices contain special types (line, linefill,
box, polyline,
label, table, or chart.point) or user-defined types, the elements of a shallow copy
reference
the same objects as the original.
## ## Deep copies
One can produce a _deep copy_ of a matrix (i.e., a matrix whose elements point to
copies of the
original values) by explicitly copying each object the matrix references.
Here, weʼve added a **deepCopy()** user-defined method to our previous script. The
method
creates a new matrix and uses nested for loops to assign all elements to copies of
the
originals. When the script calls this method instead of the built-in copy(), we see
that there
are now two labels on the chart, and any changes to the label from **myCopy** do
not affect the
one from **myMatrix** :
## ## Submatrices
For example, the script below creates an **mSub** matrix from the **m** matrix via
the
m.submatrix() method, then calls our user-defined **debugLabel()** function to
display the rows
of both matrices in labels:
Matrix variables leave historical trails on each bar, allowing scripts to use the
history-
referencing operator [] to interact with past matrix instances previously assigned
to a variable.
Additionally, scripts can modify matrices assigned to global variables from within
the scopes
of functions, methods, and conditional structures.
This script calculates the average ratios of body and wick distances relative to
the bar range
over **length** bars. It displays the data along with values from **length** bars
ago in a table. The
user-defined **addData()** function adds columns of current and historical ratios
to the
**globalMatrix** , and the **calcAvg()** function references **previous** matrices
assigned to
**globalMatrix** using the [] operator to calculate a matrix of averages:
Note that:
```
The addData() and calcAvg() functions have no parameters, as they directly interact
with the globalMatrix and length variables declared in the outer scope.
calcAvg() calculates the average by adding previous matrices using matrix.sum()
and multiplying all elements by 1 / length using matrix.mult(). We discuss these
and
other specialized functions in our Matrix calculations section below.
```
## ## Inspecting a matrix
The ability to inspect the shape of a matrix and patterns within its elements is
crucial, as it
helps reveal important information about a matrix and its compatibility with
various
calculations and transformations. Pine Script™ includes several built-ins for
matrix inspection,
including matrix.is_square(), matrix.is_identity(), matrix.is_diagonal(),
matrix.is_antidiagonal(),
matrix.is_symmetric(), matrix.is_antisymmetric(), matrix.is_triangular(),
matrix.is_stochastic(),
matrix.is_binary(), and matrix.is_zero().
## ## Manipulating a matrix
## Reshaping
The shape of a matrix can determine its compatibility with various matrix
operations. In some
cases, it is necessary to change the dimensions of a matrix without affecting the
number of
elements or the values they reference, otherwise known as _reshaping_. To reshape a
matrix in
Pine, use the matrix.reshape() function.
Note that:
```
The order of elements in m does not change with each m.reshape() call.
When reshaping a matrix, the product of the rows and columns arguments must equal
the matrix.elements_count() value, as matrix.reshape() cannot change the number of
elements in a matrix.
```
## ## Reversing
One can reverse the order of all elements in a matrix using matrix.reverse(). This
function
moves the references of an m-by-n matrix **id** at the i-th row and j-th column to
the m - 1 - i
row and n - 1 - j column.
For example, this script creates a 3 x 3 matrix containing the values 1-9 in
ascending order,
then uses the reverse() method to reverse its contents. It displays the original
and modified
versions of the matrix in labels on the chart via **m.debugLabel()** :
## ## Transposing
Transposing a matrix is a fundamental operation that flips all rows and columns in
a matrix
about its _main diagonal_ (the diagonal vector of all values in which the row index
equals the
column index). This process produces a new matrix with reversed row and column
dimensions, known as the _transpose_. Scripts can calculate the transpose of a
matrix using
matrix.transpose().
For any m-row, n-column matrix, the matrix returned from matrix.transpose() will
have n rows
and m columns. All elements in a matrix at the i-th row and j-th column correspond
to the
elements in its transpose at the j-th row and i-th column.
This example declares a 2 x 4 **m** matrix, calculates its transpose using the
m.transpose()
method, and displays both matrices on the chart using our custom **debugLabel()**
method. As
we can see below, the transposed matrix has a 4 x 2 shape, and the rows of the
transpose
match the columns of the original:
## ## Sorting
Scripts can sort the contents of a matrix via matrix.sort(). Unlike array.sort(),
which sorts
_elements_ , this function organizes all _rows_ in a matrix in a specified
**order** (order.ascending
by default) based on the values in a specified **column**.
This script declares a 3 x 3 **m** matrix, sorts the rows of the **m1** copy in
ascending order based
on the first column, then sorts the rows of the **m2** copy in descending order
based on the
second column. It displays the original matrix and sorted copies in labels using
our
**debugLabel()** method:
Itʼs important to note that matrix.sort() does not sort the columns of a matrix.
However, one
_can_ use this function to sort matrix columns with the help of matrix.transpose().
As an example, this script contains a **sortColumns()** method that uses the sort()
method to
sort the transpose of a matrix using the column corresponding to the **row** of the
original
matrix. The script uses this method to sort the **m** matrix based on the contents
of its first
row:
## ## Concatenating
Scripts can _concatenate_ two matrices using matrix.concat(). This function appends
the rows
of an **id2** matrix to the end of an **id1** matrix with the same number of
columns.
For example, this script appends the rows of the **m2** matrix to the **m1** matrix
and appends
their columns using _transposed copies_ of the matrices. It displays the **m1** and
**m2** matrices
and the results after concatenating their rows and columns in labels using the
custom
**debugLabel()** method:
## ## Matrix calculations
## Element-wise calculations
Pine scripts can calculate the _average_ , _minimum_ , _maximum_ , and _mode_ of
all elements within a
matrix via matrix.avg(), matrix.min(), matrix.max(), and matrix.mode(). These
functions
operate the same as their **array.*** equivalents, allowing users to run element-
wise
calculations on a matrix, its submatrices, and its rows and columns using the same
syntax. For
example, the built-in ***.avg()** functions called on a 3 x 3 matrix with values 1-
9 and an array
with the same nine elements will both return a value of 5.
Note that:
```
In this example, we used array.*() and matrix.*() methods interchangeably to
demonstrate their similarities in syntax and behavior.
Users can calculate the matrix equivalent of array.sum() by multiplying the
matrix.avg()
by the matrix.elements_count().
```
## ## Special calculations
Pine Script™ features several built-in functions for performing essential matrix
arithmetic and
linear algebra operations, including matrix.sum(), matrix.diff(), matrix.mult(),
matrix.pow(),
matrix.det(), matrix.inv(), matrix.pinv(), matrix.rank(), matrix.trace(),
matrix.eigenvalues(),
matrix.eigenvectors(), and matrix.kron(). These functions are advanced features
that facilitate
a variety of matrix calculations and transformations.
Scripts can perform addition and subtraction of two matrices with the same shape or
a matrix
and a scalar value using the matrix.sum() and matrix.diff() functions. These
functions use the
values from the **id2** matrix or scalar to add to or subtract from the elements in
**id**.
Note that:
```
In this example, weʼve labeled the original matrix as “A” and the transpose as
“Aᵀ”.
Adding “A” and “Aᵀ” produces a symmetric matrix, and subtracting them produces an
antisymmetric matrix.
```
## ## `matrix.mult()`
Scripts can multiply two matrices via the matrix.mult() function. This function
also facilitates
the multiplication of a matrix by an array or a scalar value.
In the case of multiplying two matrices, unlike addition and subtraction, matrix
multiplication
does not require two matrices to share the same shape. However, the number of
columns in
the first matrix must equal the number of rows in the second one. The resulting
matrix
returned by matrix.mult() will contain the same number of rows as **id1** and the
same number
of columns as **id2**. For instance, a 2 x 3 matrix multiplied by a 3 x 4 matrix
will produce a
matrix with two rows and four columns, as shown below. Each value within the
resulting matrix
is the dot product of the corresponding row in **id1** and column in **id2** :
Note that:
```
In contrast to the multiplication of scalars, matrix multiplication is non-
commutative , i.e.,
matrix.mult(a, b) does not necessarily produce the same result as matrix.mult(b,
a). In the context of our example, the latter will raise a runtime error because
the
number of columns in b doesnʼt equal the number of rows in a.
```
When multiplying a matrix and an array, this function treats the operation the same
as
multiplying **id1** by a single-column matrix, but it returns an array with the
same number of
elements as the number of rows in **id1**. When matrix.mult() passes a scalar as
its **id2** value,
the function returns a new matrix whose elements are the elements in **id1**
multiplied by the
**id2** value.
## ## `matrix.det()`
In this script, weʼve defined the matrix **m** that holds coefficients and
constants for these
three equations:
Note that:
```
Solving systems of equations is particularly useful for regression analysis , e.g.,
linear
and polynomial regression.
Cramerʼs rule works fine for small systems of equations. However, itʼs
computationally
inefficient on larger systems. Other methods, such as Gaussian elimination, are
often
preferred for such use cases.
```
## ## `matrix.inv()` and `matrix.pinv()`
For any non-singular square matrix, there is an inverse matrix that yields the
identity matrix
when multiplied by the original. Inverses have utility in various matrix
transformations and
solving systems of equations. Scripts can calculate the inverse of a matrix **when
one exists**
via the matrix.inv() function.
The following example forms a 2 x 2 **m** matrix from user inputs, then uses the
m.inv() and
m.pinv() methods to calculate the inverse or pseudoinverse of **m**. The script
displays the
original matrix, its inverse or pseudoinverse, and their product in labels on the
chart:
Note that:
```
This script will only call m.inv() when isInvertible is true , i.e., when m is
square and
has a nonzero determinant. Otherwise, it uses m.pinv() to calculate the generalized
inverse.
```
## ## `matrix.rank()`
The _rank_ of a matrix represents the number of linearly independent vectors (rows
or columns)
it contains. In essence, matrix rank measures the number of vectors one cannot
express as a
linear combination of others, or in other words, the number of vectors that contain
**unique**
information. Scripts can calculate the rank of a matrix via matrix.rank().
Note that:
```
The highest rank value a matrix can have is the minimum of its number of rows and
columns. A matrix with the maximum possible rank is known as a full-rank matrix,
and
any matrix without full rank is known as a rank-deficient matrix.
The determinants of full-rank square matrices are nonzero, and such matrices have
inverses. Conversely, the determinant of a rank-deficient matrix is always 0.
For any matrix that contains nothing but the same value in each of its elements
(e.g., a
matrix filled with 0), the rank is always 0 since none of the vectors hold unique
information. For any other matrix with distinct values, the minimum possible rank
is 1.
```
## ## Error handling
In this section, we discuss runtime errors that users may encounter while utilizing
matrices in
their scripts.
## is (yy).
This runtime error occurs when trying to access indices outside the matrix
dimensions with
functions including matrix.get(), matrix.set(), matrix.fill(), and
matrix.submatrix(), as well as
some of the functions relating to the rows and columns of a matrix.
For example, this code contains two lines that will produce this runtime error. The
m.set()
method references a **row** index that doesnʼt exist (2). The m.submatrix() method
references
all column indices up to **to_column - 1**. A **to_column** value of 4 results in a
runtime error
because the last column index referenced (3) does not exist in **m** :
Users can avoid this error in their scripts by ensuring their function calls do not
reference
indices greater than or equal to the number of rows/columns.
## in the matrix.
Note that:
```
When m is empty, one can insert a row or column array of any size, as shown in the
first
m.add_col() line.
```
## ## Cannot call matrix methods when the ID of matrix is ‘naʼ.
When a matrix variable is assigned to **na** , it means that the variable doesnʼt
reference an
existing object. Consequently, one cannot use built-in **matrix.*()** functions and
methods
with it. For example:
To resolve this error, assign **m** to a valid matrix instance before using
**matrix.*()** functions.
## elements.
For example, this script shows an attempt to declare a submatrix from a 4 x 4 **m**
matrix with a
**from_row** value of 2 and a **to_row** value of 2, which will result in an error:
When using matrix.sum() and matrix.diff() functions, the **id1** and **id2**
matrices must have
the same number of rows and the same number of columns. Attempting to add or
subtract
two matrices with mismatched dimensions will raise an error, as demonstrated by
this code:
For example, this script tries to multiply two 2 x 3 matrices. While _adding_ these
matrices is
possible, _multiplying_ them is not:
```
Previous
Arrays
```
```
Next
Maps
```
```
Notice! This page contains advanced material. If you are a beginning Pine Script™
programmer, we recommend you become familiar with other, more accessible Pine
Script™ features before you venture here.
```
```
[var/varip ][matrix<type> ]<identifier> = <expression>
```
```
Pine Script™
matrix < float > myMatrix = na
```
```
Pine Script™
myMatrix = matrix.new< float >( 2 , 2 , 0.0)
```
```
Pine Script™
// @version= 6
indicator("var matrix demo")
// @variable
A 1x2 rectangular matrix declared only at `bar_index == 0`, i.e., the fi
var m = matrix.new< int >( 1 , 2 , 0 )
// @variable Is `true` on every 20th bar.
bool update = bar_index % 20 ==
if update
int currentValue = m.get( 0 ,
0 ) // Get the current value of the first row and col
m.set( 0 , 0 , currentValue +
1 ) // Set the first row and column element value to
plot(m.get( 0 , 0 ), linewidth =
3 ) // Plot the value from the first row and column.
```
```
Notice! Matrix variables declared using varip behave as ones using var on
historical
data, but they update their values for realtime bars (i.e., the bars since the
scriptʼs last
compilation) on each new price tick. Matrices assigned to varip variables can only
hold
int, float, bool, color, or string types or user-defined types that exclusively
contain
within their fields these types or collections (arrays, matrices, or maps) of these
types.
```
```
Pine Script™
// @version= 6
indicator("Reading and writing elements demo")
// @variable A 2x2 square matrix of `float` values.
var m = matrix.new< float >( 2 , 2 , 0.0)
switch
bar_index % 11 == 0 => m.set( 0 , 0 , m.get( 0 , 0 ) +
1.0) // Adds 1 to the value at
bar_index % 7 == 0 => m.set( 0 , 1 , m.get( 0 , 1 ) +
1.0) // Adds 1 to the value at
bar_index % 5 == 0 => m.set( 1 , 0 , m.get( 1 , 0 ) +
1.0) // Adds 1 to the value at
bar_index % 3 == 0 => m.set( 1 , 1 , m.get( 1 , 1 ) +
1.0) // Adds 1 to the value at
plot(m.get( 0 , 0 ), "Row 0, Column 0 Value", color.red, 2 )
plot(m.get( 0 , 1 ), "Row 0, Column 1 Value", color.orange, 2 )
plot(m.get( 1 , 0 ), "Row 1, Column 0 Value", color.green, 2 )
plot(m.get( 1 , 1 ), "Row 1, Column 1 Value", color.blue, 2 )
```
```
Pine Script™
myMatrix = matrix.new< float >( 4 , 4 )
myMatrix.fill(math.random())
```
```
Pine Script™
// @version= 6
indicator("Object matrix fill demo")
// @variable A 4x4 label matrix.
var matrix < label > m = matrix.new< label >( 4 , 4 )
// Fill `m` with a new label object on the first bar.
if bar_index ==
m.fill(label.new( 0 , 0 , textcolor = color.white, size = size.huge))
// @variable The number of label objects on the chart.
int numLabels = label.all.size()
// Set the `x` of the label from the first row and column to `bar_index`.
m.get( 0 , 0 ).set_x(bar_index)
// Set the `text` of the label at the last row and column to the number of labels.
m.get( 3 , 3 ).set_text(str.format("Total labels on the chart: {0}", numLabels))
```
```
Pine Script™
// @version= 6
indicator("Retrieving rows and columns demo")
// @variable A 3x2 rectangular matrix.
var matrix < float > m = matrix.new< float >( 3 , 2 )
if bar_index ==
m.set( 0 , 0 , 1.0) // Set row 0, column 0 value to 1.
m.set( 0 , 1 , 2.0) // Set row 0, column 1 value to 2.
m.set( 1 , 0 , 3.0) // Set row 1, column 0 value to 3.
m.set( 1 , 1 , 4.0) // Set row 1, column 1 value to 4.
m.set( 2 , 0 , 5.0) // Set row 1, column 0 value to 5.
m.set( 2 , 1 , 6.0) // Set row 1, column 1 value to 6.
// @variable The first row of the matrix.
array < float > row0 = m.row( 0 )
// @variable The first column of the matrix.
array < float > column0 = m.col( 0 )
// @variable
Displays the first row and column of the matrix and their sizes in a lab
var label debugLabel = label.new( 0 , 0 , color = color.blue, textcolor =
color.white,
debugLabel.set_x(bar_index)
debugLabel.set_text(str.format("Row 0: {0}, Size: {1}\nCol 0: {2}, Size: {3}",
row0,
```
```
Pine Script™
// @version= 6
indicator("Retrieving rows and columns demo")
// @variable A 3x2 rectangular matrix.
var matrix < float > m = matrix.new< float >( 3 , 2 )
if bar_index ==
m.set( 0 , 0 , 1.0) // Set row 0, column 0 value to 1.
m.set( 0 , 1 , 2.0) // Set row 0, column 1 value to 2.
m.set( 1 , 0 , 3.0) // Set row 1, column 0 value to 3.
m.set( 1 , 1 , 4.0) // Set row 1, column 1 value to 4.
m.set( 2 , 0 , 5.0) // Set row 1, column 0 value to 5.
m.set( 2 , 1 , 6.0) // Set row 1, column 1 value to 6.
// @variable The first row of the matrix.
array < float > row0 = m.row( 0 )
// @variable The first column of the matrix.
array < float > column0 = m.col( 0 )
// Set the first `row` element to 10.
row0.set( 0 , 10 )
// @variable
Displays the first row and column of the matrix and their sizes in a lab
var label debugLabel = label.new( 0 , m.get( 0 , 0 ), color = color.blue,
textcolor = col
debugLabel.set_x(bar_index)
debugLabel.set_text(str.format("Row 0: {0}, Size: {1}\nCol 0: {2}, Size: {3}",
row0,
// Plot the first element of `m`.
plot(m.get( 0 , 0 ), linewidth = 3 )
```
```
Pine Script™
// @version= 6
indicator("Row with reference types demo")
// @type A custom type that holds a float value.
type myUDT
float value = 0.
// @variable A 1x1 matrix of `myUDT` type.
var matrix < myUDT > m = matrix.new< myUDT >( 1 , 1 , myUDT.new())
// @variable A shallow copy of the first row of `m`.
array < myUDT > row = m.row( 0 )
// @variable The first element of the `row`.
myUDT firstElement = row.get( 0 )
firstElement.value +=
1.0 // Add 1 to the `value` field of `firstElement`. Also affe
plot(m.get( 0 , 0 ).value, linewidth =
3 ) // Plot the `value` of the `myUDT` object fro
```
```
Pine Script™
// @version= 6
indicator("Rows and columns demo")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
//Create an empty matrix.
var m = matrix.new< float >()
if bar_index == last_bar_index -
debugLabel(m, bar_index - 30 , note = "Empty matrix")
// Insert an array at row 0. `m` will now have 1 row and 3 columns.
m.add_row( 0 , array.from( 5 , 6 , 7 ))
debugLabel(m, bar_index - 20 , note = "New row at\nindex 0")
// Insert an array at row 1. `m` will now have 2 rows and 3 columns.
m.add_row( 1 , array.from( 9 , 10 , 11 ))
debugLabel(m, bar_index - 10 , note = "New row at\nindex 1")
// Insert another array at row 0. `m` will now have 3 rows and 3 columns.
// The values previously on row 0 will now be on row 1, and the values from row
m.add_row( 0 , array.from( 1 , 2 , 3 ))
debugLabel(m, bar_index, note = "New row at\nindex 0")
// Insert an array at column 3. `m` will now have 3 rows and 4 columns.
m.add_col( 3 , array.from( 4 , 8 , 12 ))
debugLabel(m, bar_index + 10 , note = "New column at\nindex 3")
// Insert an array at row 3. `m` will now have 4 rows and 4 columns.
m.add_row( 3 , array.from( 13 , 14 , 15 , 16 ))
debugLabel(m, bar_index + 20 , note = "New row at\nindex 3")
```
```
Notice! Just as the row or column arrays retrieved from a matrix of line, linefill,
box,
polyline, label, table, chart.point, or UDT instances behave as shallow copies, the
elements of matrices containing such types reference the same objects as the arrays
inserted into them. Modifications to the element values in either object affect the
other in such cases.
```
```
Pine Script™
// Removing example
// Remove the first row and last column from the matrix. `m` will now have 3 row
m.remove_row( 0 )
m.remove_col( 3 )
debugLabel(m, bar_index + 30 , color.red, note = "Removed row 0\nand column 3")
```
```
Pine Script™
// Swapping example
// Swap the first and last row. `m` retains the same dimensions.
m.swap_rows( 0 , 2 )
debugLabel(m, bar_index + 40 , color.purple, note = "Swapped rows 0\nand 2")
```
```
Pine Script™
// @version= 6
indicator("Replacing rows demo")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @function Replaces the `row` of `this` matrix with a new array of `values`.
// @param row The row index to replace.
// @param values The array of values to insert.
method replaceRow( matrix < float > this, int row, array < float > values) =>
this.add_row(row, values) // Inserts a copy of the `values` array at the `row`.
this.remove_row(row + 1 ) // Removes the old elements previously at the `row`.
// @variable A 3x3 matrix.
var matrix < float > m = matrix.new< float >( 3 , 3 , 0.0)
if bar_index == last_bar_index -
m.debugLabel(note = "Original")
// Replace each row of `m`.
m.replaceRow( 0 , array.from(1.0, 2.0, 3.0))
m.replaceRow( 1 , array.from(4.0, 5.0, 6.0))
m.replaceRow( 2 , array.from(7.0, 8.0, 9.0))
m.debugLabel(bar_index + 10 , note = "Replaced rows")
```
```
Pine Script™
for row = 0 to m.rows() -
```
```
Pine Script™
for row = 0 to m.rows() -
for column = 0 to m.columns() -
```
```
Pine Script™
// @version= 6
indicator("for loop demo", "Matrix to table")
// @function Displays the elements of `this` matrix in a table.
// @param this The matrix to display.
// @param position The position of the table on the chart.
// @param bgColor The background color of the table.
// @param textColor The color of the text in each cell.
// @param note A note string to display on the bottom row of the table.
// @returns
A new `table` object with cells corresponding to each element of `this`
method toTable(
matrix < float > this, string position = position.middle_center,
color bgColor = color.blue, color textColor = color.white,
string note = na
) =>
// @variable The number of rows in `this` matrix.
int rows = this.rows()
// @variable The number of columns in `this` matrix.
int columns = this.columns()
// @variable A table that displays the elements of `this` matrix with an optional
table result = table.new(position, columns, rows + 1 , bgColor)
// Iterate over each row index of `this` matrix.
for row = 0 to rows -
// Iterate over each column index of `this` matrix on each `row`.
for col = 0 to columns -
// @variable The element from `this` matrix at the `row` and `col` index.
float element = this.get(row, col)
// Initialize the corresponding `result` cell with the `element` value.
result.cell(col, row, str.tostring(element), text_color = textColor, tex
// Initialize a merged cell on the bottom row if a `note` is provided.
if not na(note)
result.cell( 0 , rows, note, text_color = textColor, text_size = size.huge)
result.merge_cells( 0 , rows, columns - 1 , rows)
result // Return the `result` table.
// @variable A 3x4 matrix of values.
var m = matrix.new< float >()
if bar_index ==
// Add rows to `m`.
m.add_row( 0 , array.from( 1 , 2 , 3 ))
m.add_row( 1 , array.from( 5 , 6 , 7 ))
m.add_row( 2 , array.from( 9 , 10 , 11 ))
// Add a column to `m`.
m.add_col( 3 , array.from( 4 , 8 , 12 ))
// Display the elements of `m` in a table.
m.toTable()
```
```
Pine Script™
for row in m
```
```
Pine Script™
// @version= 6
indicator("for...in loop demo", "Average OHLC", overlay = true)
// @variable The number of terms in the average.
int length = input.int( 20 , "Length", minval = 1 )
// @function Calculates the average of each matrix row.
method rowWiseAvg( matrix < float > this) =>
// @variable An array with elements corresponding to each row's average.
array < float > result = array.new< float >()
// Iterate over each `row` of `this` matrix.
for row in this
// Push the average of each `row` into the `result`.
result.push(row.avg())
result // Return the resulting array.
// @variable A 4x`length` matrix of values.
var matrix < float > m = matrix.new< float >( 4 , length)
// Add a new column containing OHLC values to the matrix.
m.add_col(m.columns(), array.from(open, high, low, close))
// Remove the first column.
m.remove_col( 0 )
// @variable
An array containing averages of `open`, `high`, `low`, and `close` over
array < float > averages = m.rowWiseAvg()
plot(averages.get( 0 ), "Average Open", color.blue, 2 )
plot(averages.get( 1 ), "Average High", color.green, 2 )
plot(averages.get( 2 ), "Average Low", color.red, 2 )
plot(averages.get( 3 ), "Average Close", color.orange, 2 )
```
```
Pine Script™
// @version= 6
indicator("Shallow copy demo")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @variable A 2x2 `float` matrix.
matrix < float > myMatrix = matrix.new< float >()
myMatrix.add_col( 0 , array.from(1.0, 3.0))
myMatrix.add_col( 1 , array.from(2.0, 4.0))
// @variable A shallow copy of `myMatrix`.
matrix < float > myCopy = myMatrix.copy()
// Add a row to the last index of `myCopy`.
myCopy.add_row(myCopy.rows(), array.from(5.0, 6.0))
if bar_index == last_bar_index -
// Display the rows of both matrices in separate labels.
myMatrix.debugLabel(note = "Original")
myCopy.debugLabel(bar_index + 10 , color.green, note = "Shallow Copy")
```
```
Pine Script™
// @version= 6
indicator("Shallow copy demo")
// @variable Initial value of the original matrix elements.
var label newLabel = label.new(
bar_index, 1 , "Original", color = color.blue, textcolor = color.white, size = s
)
// @variable A 1x1 matrix containing a new `label` instance.
var matrix < label > myMatrix = matrix.new< label >( 1 , 1 , newLabel)
// @variable A shallow copy of `myMatrix`.
var matrix < label > myCopy = myMatrix.copy()
// @variable The first label from the `myCopy` matrix.
label testLabel = myCopy.get( 0 , 0 )
// Change the `text`, `style`, and `x` values of `testLabel`. Also affects the `new
L
testLabel.set_text("Copy")
testLabel.set_style(label.style_label_up)
testLabel.set_x(bar_index)
// Plot the total number of labels.
plot(label.all.size(), linewidth = 3 )
```
```
Pine Script™
// @version= 6
indicator("Deep copy demo")
// @function Returns a deep copy of a label matrix.
method deepCopy( matrix < label > this) =>
// @variable A deep copy of `this` matrix.
matrix < label > that = this.copy()
for row = 0 to that.rows() -
for column = 0 to that.columns() -
// Assign the element at each `row` and `column` of `that` matrix to a c
that.set(row, column, that.get(row, column).copy())
that
// @variable Initial value of the original matrix.
var label newLabel = label.new(
bar_index, 2 , "Original", color = color.blue, textcolor = color.white, size = s
)
// @variable A 1x1 matrix containing a new `label` instance.
var matrix < label > myMatrix = matrix.new< label >( 1 , 1 , newLabel)
// @variable A deep copy of `myMatrix`.
var matrix < label > myCopy = myMatrix.deepCopy()
// @variable The first label from the `myCopy` matrix.
label testLabel = myCopy.get( 0 , 0 )
// Change the `text`, `style`, and `x` values of `testLabel`. Does not affect the `
n
testLabel.set_text("Copy")
testLabel.set_style(label.style_label_up)
testLabel.set_x(bar_index)
// Change the `x` value of `newLabel`.
newLabel.set_x(bar_index)
// Plot the total number of labels.
plot(label.all.size(), linewidth = 3 )
```
```
Pine Script™
// @version= 6
indicator("Submatrix demo")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @variable A 3x3 matrix of values.
var m = matrix.new< float >()
if bar_index == last_bar_index -
// Add columns to `m`.
m.add_col( 0 , array.from( 9 , 6 , 3 ))
m.add_col( 1 , array.from( 8 , 5 , 2 ))
m.add_col( 2 , array.from( 7 , 4 , 1 ))
// Display the rows of `m`.
m.debugLabel(note = "Original Matrix")
// @variable A 2x2 submatrix of `m` containing the first two rows and columns.
matrix < float > mSub = m.submatrix(from_row = 0 , to_row = 2 , from_column =
0 , to_c
// Display the rows of `mSub`
debugLabel(mSub, bar_index + 10 , bgColor = color.green, note = "Submatrix")
```
```
Pine Script™
// @version= 6
indicator("Scope and history demo", "Bar ratio comparison")
int length = input.int( 10 , "Length", 1 )
// @variable A global matrix.
matrix < float > globalMatrix = matrix.new< float >()
// @function Calculates the ratio of body range to candle range.
bodyRatio() =>
math.abs(close - open) / (high - low)
// @function Calculates the ratio of upper wick range to candle range.
upperWickRatio() =>
(high - math.max(open, close)) / (high - low)
// @function Calculates the ratio of lower wick range to candle range.
lowerWickRatio() =>
(math.min(open, close) - low) / (high - low)
// @function Adds data to the `globalMatrix`.
addData() =>
// Add a new column of data at `column` 0.
globalMatrix.add_col( 0 , array.from(bodyRatio(), upperWickRatio(), lowerWickRatio
// @variable The column of `globalMatrix` from index 0 `length` bars ago.
array < float > pastValues = globalMatrix.col( 0 )[length]
// Add `pastValues` to the `globalMatrix`, or an array of `na` if `pastValues` i
if na(pastValues)
globalMatrix.add_col( 1 , array.new< float >( 3 ))
else
globalMatrix.add_col( 1 , pastValues)
// @function Returns the `length`-
bar average of matrices assigned to `globalMatrix`
calcAvg() =>
// @variable The sum historical `globalMatrix` matrices.
matrix < float > sums = matrix.new< float >(globalMatrix.rows(),
globalMatrix.columns
for i = 0 to length -
// @variable The `globalMatrix` matrix `i` bars before the current bar.
matrix < float > previous = globalMatrix[i]
// Break the loop if `previous` is `na`.
if na(previous)
sums.fill(na)
break
// Assign the sum of `sums` and `previous` to `sums`.
sums := matrix.sum(sums, previous)
// Divide the `sums` matrix by the `length`.
result = sums.mult(1.0 / length)
// Add data to the `globalMatrix`.
addData()
// @variable The historical average of the `globalMatrix` matrices.
globalAvg = calcAvg()
// @variable A `table` displaying information from the `globalMatrix`.
var table infoTable = table.new(
position.middle_center, globalMatrix.columns() + 1 , globalMatrix.rows() + 1 , bg
)
// Define value cells.
for [i, row] in globalAvg
for [j, value] in row
color textColor = value > 0.333? color.orange : color.gray
infoTable.cell(j + 1 , i + 1 , str.tostring(value), text_color = textColor, te
// Define header cells.
infoTable.cell( 0 , 1 , "Body ratio", text_color = color.white, text_size =
size.huge)
infoTable.cell( 0 , 2 , "Upper wick ratio", text_color = color.white, text_size =
size.
infoTable.cell( 0 , 3 , "Lower wick ratio", text_color = color.white, text_size =
size.
infoTable.cell( 1 , 0 , "Current average", text_color = color.white, text_size =
size.h
infoTable.cell( 2 , 0 , str.format("{0} bars ago", length), text_color =
color.white, t
```
```
Pine Script™
// @version= 6
indicator("Matrix inspection demo")
// @function
Inspects a matrix using `matrix.is_*()` functions and returns a `string`
method inspect( matrix < int > this)=>
// @variable A string describing `this` matrix.
string result = "This matrix:\n"
if this.is_square()
result += "- Has an equal number of rows and columns.\n"
if this.is_binary()
result += "- Contains only 1s and 0s.\n"
if this.is_zero()
result += "- Is filled with 0s.\n"
if this.is_triangular()
result += "- Contains only 0s above and/or below its main diagonal.\n"
if this.is_diagonal()
result += "- Only has nonzero values in its main diagonal.\n"
if this.is_antidiagonal()
result += "- Only has nonzero values in its main antidiagonal.\n"
if this.is_symmetric()
result += "- Equals its transpose.\n"
if this.is_antisymmetric()
result += "- Equals the negative of its transpose.\n"
if this.is_identity()
result += "- Is the identity matrix.\n"
result
// @variable A 4x4 identity matrix.
matrix < int > m = matrix.new< int >()
// Add rows to the matrix.
m.add_row( 0 , array.from( 1 , 0 , 0 , 0 ))
m.add_row( 1 , array.from( 0 , 1 , 0 , 0 ))
m.add_row( 2 , array.from( 0 , 0 , 1 , 0 ))
m.add_row( 3 , array.from( 0 , 0 , 0 , 1 ))
if bar_index == last_bar_index -
// Display the `m` matrix in a blue label.
label.new(
bar_index, 0 , str.tostring(m), color = color.blue, style = label.style_labe
textcolor = color.white, size = size.huge
)
// Display the result of `m.inspect()` in a purple label.
label.new(
bar_index, 0 , m.inspect(), color = color.purple, style = label.style_label_
textcolor = color.white, size = size.huge
)
```
```
Pine Script™
// @version= 6
indicator("Reshaping example")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @variable A matrix containing the values 1-8.
matrix < int > m = matrix.new< int >()
if bar_index == last_bar_index -
// Add the initial vector of values.
m.add_row( 0 , array.from( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ))
m.debugLabel(note = "Initial 1x8 matrix")
// Reshape. `m` now has 2 rows and 4 columns.
m.reshape( 2 , 4 )
m.debugLabel(bar_index + 10 , note = "Reshaped to 2x4")
// Reshape. `m` now has 4 rows and 2 columns.
m.reshape( 4 , 2 )
m.debugLabel(bar_index + 20 , note = "Reshaped to 4x2")
// Reshape. `m` now has 8 rows and 1 column.
m.reshape( 8 , 1 )
m.debugLabel(bar_index + 30 , note = "Reshaped to 8x1")
```
```
Pine Script™
// @version= 6
indicator("Reversing demo")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @variable A 3x3 matrix.
matrix < float > m = matrix.new< float >()
// Add rows to `m`.
m.add_row( 0 , array.from( 1 , 2 , 3 ))
m.add_row( 1 , array.from( 4 , 5 , 6 ))
m.add_row( 2 , array.from( 7 , 8 , 9 ))
if bar_index == last_bar_index -
// Display the contents of `m`.
m.debugLabel(note = "Original")
// Reverse `m`, then display its contents.
m.reverse()
m.debugLabel(bar_index + 10 , color.red, note = "Reversed")
```
```
Pine Script™
// @version= 6
indicator("Transpose example")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @variable A 2x4 matrix.
matrix < int > m = matrix.new< int >()
// Add columns to `m`.
m.add_col( 0 , array.from( 1 , 5 ))
m.add_col( 1 , array.from( 2 , 6 ))
m.add_col( 2 , array.from( 3 , 7 ))
m.add_col( 3 , array.from( 4 , 8 ))
// @variable The transpose of `m`. Has a 4x2 shape.
matrix < int > mt = m.transpose()
if bar_index == last_bar_index -
m.debugLabel(note = "Original")
mt.debugLabel(bar_index + 10 , note = "Transpose")
```
```
Pine Script™
// @version= 6
indicator("Sorting rows example")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @variable A 3x3 matrix.
matrix < int > m = matrix.new< int >()
if bar_index == last_bar_index -
// Add rows to `m`.
m.add_row( 0 , array.from( 3 , 2 , 4 ))
m.add_row( 1 , array.from( 1 , 9 , 6 ))
m.add_row( 2 , array.from( 7 , 8 , 9 ))
m.debugLabel(note = "Original")
// Copy `m` and sort rows in ascending order based on the first column (default)
matrix < int > m1 = m.copy()
m1.sort()
m1.debugLabel(bar_index + 10 , color.green, note = "Sorted using col 0\n(Ascendin
// Copy `m` and sort rows in descending order based on the second column.
matrix < int > m2 = m.copy()
m2.sort( 1 , order.descending)
m2.debugLabel(bar_index + 20 , color.red, note = "Sorted using col 1\n(Descending
```
```
Pine Script™
// @version= 6
indicator("Sorting columns example")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @function
Sorts the columns of `this` matrix based on the values in the specified
method sortColumns( matrix < int > this, int row = 0 , bool ascending = true) =>
// @variable The transpose of `this` matrix.
matrix < int > thisT = this.transpose()
// @variable Is `order.ascending` when `ascending` is `true`, `order.descending`
order = ascending? order.ascending : order.descending
// Sort the rows of `thisT` using the `row` column.
thisT.sort(row, order)
// @variable A copy of `this` matrix with sorted columns.
result = thisT.transpose()
// @variable A 3x3 matrix.
matrix < int > m = matrix.new< int >()
if bar_index == last_bar_index -
// Add rows to `m`.
m.add_row( 0 , array.from( 3 , 2 , 4 ))
m.add_row( 1 , array.from( 1 , 9 , 6 ))
m.add_row( 2 , array.from( 7 , 8 , 9 ))
m.debugLabel(note = "Original")
// Sort the columns of `m` based on the first row and display the result.
m.sortColumns( 0 ).debugLabel(bar_index + 10 , note = "Sorted using row 0\
n(Ascendi
```
```
Pine Script™
// @version= 6
indicator("Concatenation demo")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @variable A 2x3 matrix filled with 1s.
matrix < int > m1 = matrix.new< int >( 2 , 3 , 1 )
// @variable A 2x3 matrix filled with 2s.
matrix < int > m2 = matrix.new< int >( 2 , 3 , 2 )
// @variable The transpose of `m1`.
t1 = m1.transpose()
// @variable The transpose of `m2`.
t2 = m2.transpose()
if bar_index == last_bar_index -
// Display the original matrices.
m1.debugLabel(note = "Matrix 1")
m2.debugLabel(bar_index + 10 , note = "Matrix 2")
// Append the rows of `m2` to the end of `m1` and display `m1`.
m1.concat(m2)
m1.debugLabel(bar_index + 20 , color.blue, note = "Appended rows")
// Append the rows of `t2` to the end of `t1`, then display the transpose of `t
t1.concat(t2)
t1.transpose().debugLabel(bar_index + 30 , color.purple, note = "Appended columns
```
```
Pine Script™
// @version= 6
indicator("Element-wise calculations example", "Developing values", overlay = true)
// @variable The number of data points in the averages.
int length = input.int( 3 , "Length", 1 )
// @variable The timeframe of each reset period.
string timeframe = input.timeframe("D", "Reset Timeframe")
// @variable A 4x`length` matrix of OHLC values.
var matrix < float > ohlcData = matrix.new< float >( 4 , length)
// @variable Is `true` at the start of a new bar at the `timeframe`.
bool queueColumn = timeframe.change(timeframe)
if queueColumn
// Add new values to the end column of `ohlcData`.
ohlcData.add_col(length, array.from(open, high, low, close))
// Remove the oldest column from `ohlcData`.
ohlcData.remove_col( 0 )
else
// Adjust the last element of column 1 for new highs.
if high > ohlcData.get( 1 , length - 1 )
ohlcData.set( 1 , length - 1 , high)
// Adjust the last element of column 2 for new lows.
if low < ohlcData.get( 2 , length - 1 )
ohlcData.set( 2 , length - 1 , low)
// Adjust the last element of column 3 for the new closing price.
ohlcData.set( 3 , length - 1 , close)
// @variable The `matrix.avg()` of all elements in `ohlcData`.
avgOHLC4 = ohlcData.avg()
// @variable
The `matrix.avg()` of all elements in rows 1 and 2, i.e., the average of
avgHL2 = ohlcData.submatrix(from_row = 1 , to_row = 3 ).avg()
// @variable
The `matrix.max()` of all values in `ohlcData`. Equivalent to `ohlcData.
maxHigh = ohlcData.max()
// @variable
The `array.min()` of all `low` values in `ohlcData`. Equivalent to `ohlc
minLow = ohlcData.row( 2 ).min()
// @variable
The `array.avg()` of the last column in `ohlcData`, i.e., the current OH
ohlc4Value = ohlcData.col(length - 1 ).avg()
plot(avgOHLC4, "Average OHLC4", color.purple, 2 )
plot(avgHL2, "Average HL2", color.navy, 2 )
plot(maxHigh, "Max High", color.green)
plot(minLow, "Min Low", color.red)
plot(ohlc4Value, "Current OHLC4", color.blue)
```
```
Pine Script™
// @version= 6
indicator("Matrix sum and diff example")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @variable A 3x3 matrix.
m = matrix.new< float >()
// Add rows to `m`.
m.add_row( 0 , array.from(0.5, 1.0, 1.5))
m.add_row( 1 , array.from(2.0, 2.5, 3.0))
m.add_row( 2 , array.from(3.5, 4.0, 4.5))
if bar_index == last_bar_index -
// Display `m`.
m.debugLabel(note = "A")
// Get and display the transpose of `m`.
matrix < float > t = m.transpose()
t.debugLabel(bar_index + 10 , note = "Aᵀ")
// Calculate the sum of the two matrices. The resulting matrix is symmetric.
matrix.sum(m, t).debugLabel(bar_index + 20 , color.green, note = "A + Aᵀ")
// Calculate the difference between the two matrices. The resulting matrix is an
matrix.diff(m, t).debugLabel(bar_index + 30 , color.red, note = "A - Aᵀ")
```
```
Pine Script™
// @version= 6
indicator("Matrix mult example")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @variable A 2x3 matrix.
a = matrix.new< float >()
// @variable A 3x4 matrix.
b = matrix.new< float >()
// Add rows to `a`.
a.add_row( 0 , array.from( 1 , 2 , 3 ))
a.add_row( 1 , array.from( 4 , 5 , 6 ))
// Add rows to `b`.
b.add_row( 0 , array.from(0.5, 1.0, 1.5, 2.0))
b.add_row( 1 , array.from(2.5, 3.0, 3.5, 4.0))
b.add_row( 0 , array.from(4.5, 5.0, 5.5, 6.0))
if bar_index == last_bar_index -
// @variable The result of `a` * `b`.
matrix < float > ab = a.mult(b)
// Display `a`, `b`, and `ab` matrices.
debugLabel(a, note = "A")
debugLabel(b, bar_index + 10 , note = "B")
debugLabel(ab, bar_index + 20 , color.green, note = "A * B")
```
```
3 * x0 + 4 * x1 - 1 * x2 = 8
5 * x0 - 2 * x1 + 1 * x2 = 4
2 * x0 - 2 * x1 + 1 * x2 = 1
```
```
Pine Script™
// @version= 6
indicator("Determinants example", "Cramer's Rule")
// @function
Solves a system of linear equations with a matching number of unknowns u
// @param
this An augmented matrix containing the coefficients for each unknown an
// the equations. For example, a row containing the values 2, -1, and 3 re
p
// `2 * x0 + (-1) * x1 = 3`, where `x0` and `x1` are the unknown values in
// @returns An array containing solutions for each variable in the system.
solve( matrix < float > this) =>
// @variable The coefficient matrix for the system of equations.
matrix < float > coefficients = this.submatrix(from_column = 0 , to_column =
this.co
// @variable The array of resulting constants for each equation.
array < float > constants = this.col(this.columns() - 1 )
// @variable An array containing solutions for each unknown in the system.
array < float > result = array.new< float >()
// @variable The determinant value of the coefficient matrix.
float baseDet = coefficients.det()
matrix < float > modified = na
for col = 0 to coefficients.columns() - 1
modified := coefficients.copy()
modified.add_col(col, constants)
modified.remove_col(col + 1 )
// Calculate the solution for the column's unknown by dividing the determina
result.push(modified.det() / baseDet)
result
// @variable
A 3x4 matrix containing coefficients and results for a system of three e
m = matrix.new< float >()
// Add rows for the following equations:
// Equation 1: 3 * x0 + 4 * x1 - 1 * x2 =
// Equation 2: 5 * x0 - 2 * x1 + 1 * x2 =
// Equation 3: 2 * x0 - 2 * x1 + 1 * x2 =
m.add_row( 0 , array.from(3.0, 4.0, -1.0, 8.0))
m.add_row( 1 , array.from(5.0, -2.0, 1.0, 4.0))
m.add_row( 2 , array.from(2.0, -2.0, 1.0, 1.0))
// @variable
An array of solutions to the unknowns in the system of equations represe
solutions = solve(m)
plot(solutions.get( 0 ), "x0", color.red, 3 ) // Plots 1.
plot(solutions.get( 1 ), "x1", color.green, 3 ) // Plots 2.
plot(solutions.get( 2 ), "x2", color.blue, 3 ) // Plots 3.
```
```
Pine Script™
// @version= 6
indicator("Inverse example")
// Element inputs for the 2x2 matrix.
float r0c0 = input.float(4.0, "Row 0, Col 0")
float r0c1 = input.float(3.0, "Row 0, Col 1")
float r1c0 = input.float(2.0, "Row 1, Col 0")
float r1c1 = input.float(1.0, "Row 1, Col 1")
// @function Displays the rows of a matrix in a label with a note.
// @param this The matrix to display.
// @param barIndex The `bar_index` to display the label at.
// @param bgColor The background color of the label.
// @param textColor The color of the label's text.
// @param note The text to display above the rows.
method debugLabel(
matrix < float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
labelText = note + "\n" + str.tostring(this)
if barstate.ishistory
label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
// @variable A 2x2 matrix of input values.
m = matrix.new< float >()
// Add input values to `m`.
m.add_row( 0 , array.from(r0c0, r0c1))
m.add_row( 1 , array.from(r1c0, r1c1))
// @variable
Is `true` if `m` is square with a nonzero determinant, indicating invert
bool isInvertible = m.is_square() and m.det() !=
// @variable The inverse or pseudoinverse of `m`.
mInverse = isInvertible? m.inv() : m.pinv()
// @variable
The product of `m` and `mInverse`. Returns the identity matrix when `isI
matrix < float > product = m.mult(mInverse)
if bar_index == last_bar_index -
// Display `m`, `mInverse`, and their `product`.
m.debugLabel(note = "Original")
mInverse.debugLabel(bar_index + 10 , color.purple, note = isInvertible? "Inverse
product.debugLabel(bar_index + 20 , color.green, note = "Product")
```
```
Pine Script™
// @version= 6
indicator("Matrix rank example")
// @variable A 3x3 full-rank matrix.
m1 = matrix.new< float >()
// @variable A 3x3 rank-deficient matrix.
m2 = matrix.new< float >()
// Add linearly independent vectors to `m1`.
m1.add_row( 0 , array.from( 3 , 2 , 3 ))
m1.add_row( 1 , array.from( 4 , 6 , 6 ))
m1.add_row( 2 , array.from( 7 , 4 , 9 ))
// Add linearly dependent vectors to `m2`.
m2.add_row( 0 , array.from( 1 , 2 , 3 ))
m2.add_row( 1 , array.from( 2 , 4 , 6 ))
m2.add_row( 2 , array.from( 3 , 6 , 9 ))
// Plot `matrix.rank()` values.
plot(m1.rank(), color = color.green, linewidth = 3 )
plot(m2.rank(), color = color.red, linewidth = 3 )
```
```
Pine Script™
// @version= 6
indicator("Out of bounds demo")
// @variable A 2x3 matrix with a max row index of 1 and max column index of 2.
matrix < float > m = matrix.new< float >( 2 , 3 , 0.0)
m.set(row = 2 , column = 0 , value =
1.0) // The `row` index is out of bounds on t
m.submatrix(from_column = 1 , to_column =
4 ) // The `to_column` index is invalid on t
if bar_index == last_bar_index -
label.new(bar_index, 0 , str.tostring(m), color = color.navy, textcolor = color.w
```
```
Pine Script™
// @version= 6
indicator("Invalid array size demo")
// Declare an empty matrix.
m = matrix.new< float >()
m.add_col( 0 , array.from( 1 ,
2 )) // Add a column. Changes the shape of `m` to 2x1.
m.add_col( 1 , array.from( 1 , 2 ,
3 )) // Raises a runtime error because `m` has 2 rows,
plot(m.col( 0 ).get( 1 ))
```
```
Pine Script™
// @version= 6
indicator("na matrix methods demo")
// @variable A `matrix` variable assigned to `na`.
matrix < float > m = na
mCopy =
m.copy() // Raises a runtime error. You can't copy a matrix that doesn't exi
if bar_index == last_bar_index -
label.new(bar_index, 0 , str.tostring(mCopy), color = color.navy, textcolor = col
```
```
Pine Script™
// @version= 6
indicator("Matrix too large demo")
var matrix < float > m = matrix.new< float >()
if bar_index ==
for i = 1 to
// This raises an error because the script adds 101 elements on each iterati
// 1000 rows * 101 elements per row = 101000 total elements. This is too lar
m.add_row(m.rows(), array.new< float >( 101 , i))
plot(m.get( 0 , 0 ))
```
```
Pine Script™
// @version= 6
indicator("Invalid from_row, to_row demo")
// @variable A 4x4 matrix filled with a random value.
matrix < float > m = matrix.new< float >( 4 , 4 , math.random())
matrix < float > mSub = m.submatrix(from_row = 2 , to_row =
2 ) // Raises an error. `from
plot(mSub.get( 0 , 0 ))
```
```
Pine Script™
// @version= 6
indicator("Invalid sum dimensions demo")
// @variable A 2x3 matrix.
matrix < float > m1 = matrix.new< float >( 2 , 3 , 1 )
// @variable A 3x4 matrix.
matrix < float > m2 = matrix.new< float >( 3 , 4 , 2 )
mSum = matrix.sum(m1,
m2) // Raises an error. `m1` and `m2` don't have matching dime
plot(mSum.get( 0 , 0 ))
```
```
Pine Script™
// @version= 6
indicator("Invalid mult dimensions demo")
// @variable A 2x3 matrix.
matrix < float > m1 = matrix.new< float >( 2 , 3 , 1 )
// @variable A 2x3 matrix.
matrix < float > m2 = matrix.new< float >( 2 , 3 , 2 )
mSum = matrix.mult(m1,
m2) // Raises an error. The number of columns in `m1` and row
plot(mSum.get( 0 , 0 ))
```
```
Pine Script™
// @version= 6
indicator("Non-square demo")
// @variable A 3x5 matrix.
matrix < float > m = matrix.new< float >( 3 , 5 , 1 )
plot(m.det()) // Raises a runtime error. You can't calculate the determinant of a 3
x
```
```
ADVANCED
```
# Maps
## ## Introduction
Pine Script™ Maps are collections that store elements in _key-value pairs_. They
allow scripts to
collect multiple value references associated with unique identifiers (keys).
Unlike arrays and matrices, maps are _unordered_ collections. Scripts quickly
access a mapʼs
values by referencing the keys from the key-value pairs put into them rather than
traversing
an internal index.
A mapʼs keys can be of any fundamental type or enum type, and its values can be of
any
available type. Maps cannot directly use other collections (maps, arrays, or
matrices) as
values, but they can hold UDT instances containing these data structures within
their fields.
See this section for more information.
As with other collections, maps can contain up to 100,000 elements in total. Since
each key-
value pair in a map consists of two elements (a unique _key_ and its associated
_value_ ), the
maximum number of key-value pairs a map can hold is 50,000.
## ## Declaring a map
Where **<keyType, valueType>** is the mapʼs type template that declares the types
of keys and
values it will contain, and the **<expression>** returns either a map instance or
**na**.
When declaring a map variable assigned to **na** , users must include the map
keyword
followed by a type template to tell the compiler that the variable can accept maps
with
**keyType** keys and **valueType** values.
For example, this line of code declares a new **myMap** variable that can accept
map instances
holding pairs of string keys and float values:
When the **<expression>** is not **na** , the compiler does not require explicit
type declaration, as
it will infer the type information from the assigned map object.
This line declares a **myMap** variable assigned to an empty map with string keys
and float
values. Any maps assigned to this variable later must have the same key and value
types:
Users can include the var or varip keywords to instruct their scripts to declare
map variables
only on the first chart bar. Variables that use these keywords point to the same
map instances
on each script iteration until explicitly reassigned.
For example, this script declares a **colorMap** variable assigned to a map that
holds pairs of
string keys and color values on the first chart bar. The script displays an
**oscillator** on the
chart and uses the values it put into the **colorMap** on the _first_ bar to color
the plots on _all_
bars:
The map.put() function is one that map users will utilize quite often, as itʼs the
primary
method to put a new key-value pair into a map. It associates the **key** argument
with the
**value** argument in the call and adds the pair to the map **id**.
If the **key** argument in the map.put() call already exists in the mapʼs keys, the
new pair
passed into the function will **replace** the existing one.
To retrieve the value from a map **id** associated with a given **key** , use
map.get(). This
function returns the value if the **id** map contains the **key**. Otherwise, it
returns na.
The following example calculates the difference between the bar_index values from
when
close was last rising and falling over a given **length** with the help of
map.put() and map.get()
methods. The script puts a **("Rising", bar_index)** pair into the **data** map
when the price is
rising and puts a **("Falling", bar_index)** pair into the map when the price is
falling. It then
puts a pair containing the “Difference” between the “Rising” and “Falling” values
into the map
and plots its value on the chart:
Note that:
```
This script replaces the values associated with the “Rising”, “Falling”, and
“Difference”
keys on successive data.put() calls, as each of these keys is unique and can only
appear
once in the data map.
Replacing the pairs in a map does not change the internal insertion order of its
keys. We
discuss this further in the next section.
```
Similar to working with other collections, when putting a value of a _special type_
(line, linefill,
box, polyline, label, table, or chart.point) or a user-defined type into a map,
itʼs important to
note the inserted pairʼs **value** points to that same object without copying it.
Modifying the
value referenced by a key-value pair will also affect the original object.
For example, this script contains a custom **ChartData** type with **o** , **h** ,
**l** , and **c** fields. On
the first chart bar, the script declares a **myMap** variable and adds the pair
**("A", myData)** ,
where **myData** is a **ChartData** instance with initial field values of **na**.
It adds the pair **("B",
myData)** to **myMap** and updates the object from this pair on every bar via the
user-defined
**update()** method.
Each change to the object with the “B” key affects the one referenced by the “A”
key, as
shown by the candle plot of the “A” objectʼs fields:
Note that:
```
This script would behave differently if it passed a copy of myData into each
myMap.put() call. For more information, see this section of our User Manualʼs page
on
objects.
```
## ## Inspecting keys and values
To retrieve all keys and values put into a map, use map.keys() and map.values().
These
functions copy all key/value references within a map **id** to a new array object.
Modifying the
array returned from either of these functions does not affect the **id** map.
Although maps are _unordered_ collections, Pine Script™ internally maintains the
_insertion
order_ of a mapʼs key-value pairs. As a result, the map.keys() and map.values()
functions
always return arrays with their elements ordered based on the **id** mapʼs
insertion order.
The script below demonstrates this by displaying the key and value arrays from an
**m** map in
a label once every 50 bars. As we see on the chart, the order of elements in each
array
returned by **m.keys()** and **m.values()** aligns with the insertion order of the
key-value pairs
in **m** :
Note that:
```
The value with the “First” key is a random whole number between 0 and 100. The
“Second” value is one greater than the “First”, and the “Third” value is one
greater than
the “Second”.
```
Itʼs important to note a mapʼs internal insertion order **does not** change when
replacing its
key-value pairs. The locations of the new elements in the keys() and values()
arrays will be the
same as the old elements in such cases. The only exception is if the script
completely
removes the key beforehand.
Below, weʼve added a line of code to put a new value with the “Second” key into the
**m** map,
overwriting the previous value associated with that key. Although the script puts
this new pair
into the map _after_ the one with the “Third” key, the pairʼs key and value are
still second in the
**keys** and **values** arrays since the key was already present in **m** _before_
the change:
## ## `map.contains()`
For example, this script checks if various keys exist within an **m** map, then
displays the
results in a label:
To remove a specific key-value pair from a map **id** , use map.remove(). This
function
removes the **key** and its associated value from the map while preserving the
insertion order
of other key-value pairs. It returns the removed value if the map contained the
**key**.
Otherwise, it returns na.
To remove all key-value pairs from a map **id** at once, use map.clear().
The following script creates a new **m** map, puts key-value pairs into the map,
uses
m.remove() within a loop to remove each valid **key** listed in the **removeKeys**
array, then calls
m.clear() to remove all remaining key-value pairs. It uses a custom
**debugLabel()** method to
display the size, keys, and values of **m** after each change:
Note that:
```
Not all strings in the removeKeys array were present in the keys of m. Attempting
to
remove non-existent keys (“F”, “a”, and the second “B” in this example) has no
effect on
a mapʼs contents.
```
## ## Combining maps
Scripts can combine two maps via map.put_all(). This function puts _all_ key-value
pairs from
the **id2** map, in their insertion order, into the **id1** map. As with map.put(),
if any keys in
**id2** are also present in **id1** , this function **replaces** the key-value
pairs that contain those
keys without affecting their initial insertion order.
This example contains a user-defined **hexMap()** function that maps decimal int
keys to string
representations of their hexadecimal forms. The script uses this function to create
two maps,
**mapA** and **mapB** , then uses mapA.put_all(mapB) to put all key-value pairs
from **mapB** into
**mapA**.
The script uses a custom **debugLabel()** function to display labels showing the
keys and
values of **mapA** and **mapB** , then another label displaying the contents of
**mapA** after putting
all key-value pairs from **mapB** into it:
There are several ways scripts can iteratively access the keys and values in a map.
For
example, one could loop through a mapʼs keys() array and get() the value for each
**key** , like
so:
Letʼs use this structure to write a script that displays a mapʼs key-value pairs in
a table. In the
example below, weʼve defined a custom **toTable()** method that creates a table,
then uses a
**for...in** loop to iterate over the mapʼs key-value pairs and populate the
tableʼs cells. The
script uses this method to visualize a map containing **length** - bar **averages**
of price and
volume data:
## ## Copying a map
## Shallow copies
Scripts can make a _shallow copy_ of a map **id** using the map.copy() function.
Modifications to
a shallow copy do not affect the original **id** map or its internal insertion
order.
For example, this script constructs an **m** map with the keys “A”, “B”, “C”, and
“D” assigned to
four random values between 0 and 10. It then creates an **mCopy** map as a shallow
copy of **m**
and updates the values associated with its keys. The script displays the key-value
pairs in **m**
and **mCopy** on the chart using our custom **debugLabel()** method:
## ## Deep copies
While a shallow copy will suffice when copying maps that have values of a
fundamental type
or enum type, itʼs crucial to understand that shallow copies of a map holding
values of a
_special type_ (line, linefill, box, polyline, label, table, chart.point or a UDT)
point to the same
objects as the original. Modifying the objects referenced by a shallow copy will
affect the
instances referenced by the original map and vice versa.
This example creates an **original** map of string keys and label values and puts a
key-value
pair into it. The script copies the map to a **shallow** variable via the built-in
copy() method,
then to a **deep** variable using a custom **deepCopy()** method.
As we see from the chart, changes to the label retrieved from the **shallow** copy
also affect
the instance referenced by the **original** map, but changes to the one from the
**deep** copy
do not:
Note that:
```
The deepCopy() method loops through the original map, copying each value and
putting key-value pairs containing the copies into a new map instance.
```
## ## Scope and history
As with other collections in Pine, map variables leave historical trails on each
bar, allowing a
script to access past map instances assigned to a variable using the history-
referencing
operator []. Scripts can also assign maps to global variables and interact with
them from the
scopes of functions, methods, and conditional structures.
As an example, this script uses a global map and its history to calculate an
aggregate set of
EMAs. It declares a **globalData** map of int keys and float values, where each key
in the map
corresponds to the length of each EMA calculation. The user-defined **update()**
function
calculates each **key** - length EMA by mixing the values from the **previous** map
assigned to
**globalData** with the current **source** value.
The script plots the maximum and minimum values in the global mapʼs values() array
and the
value from **globalData.get(50)** (i.e., the 50-bar EMA):
Maps cannot directly use other maps, arrays, or matrices as values, but they can
hold values
of a user-defined type that contains collections within its fields.
For example, suppose we want to create a “2D” map that uses string keys to access
_nested
maps_ that hold pairs of string keys and float values. Since maps cannot use other
collections
as values, we will first create a _wrapper type_ with a field to hold a
**map<string, float>**
instance, like so:
With our **Wrapper** type defined, we can create maps of string keys and
**Wrapper** values,
where the **data** field of each value in the map points to a **map<string,
float>** instance:
The script below uses this concept to construct a map containing maps that hold
OHLCV data
requested from multiple tickers. The user-defined **requestData()** function
requests price and
volume data from a ticker, creates a **<string, float>** map, puts the data into
it, then returns
a **Wrapper** instance containing the new map.
The script puts the results from each call to **requestData()** into the
**mapOfMaps** , then creates
a string representation of the nested maps with a user-defined **toString()**
method, which it
displays on the chart in a label:
```
Previous
Matrices
```
```
Notice! This page contains advanced material. If you are a beginning Pine Script™
programmer, we recommend you become familiar with other, more accessible Pine
Script™ features before you venture here.
```
```
[var/varip ][map<keyType, valueType> ]<identifier> = <expression>
```
```
Pine Script™
map < string , float > myMap = na
```
```
Pine Script™
myMap = map.new< string , float >()
```
```
Pine Script™
// @version= 6
indicator("var map demo")
// @variable A map associating color values with string keys.
var colorMap = map.new< string , color >()
// Put `<string, color>` pairs into `colorMap` on the first bar.
if bar_index == 0
colorMap.put("Bull", color.green)
colorMap.put("Bear", color.red)
colorMap.put("Neutral", color.gray)
// @variable The 14-bar RSI of `close`.
float oscillator = ta.rsi(close, 14 )
// @variable The color of the `oscillator`.
color oscColor = switch
oscillator > 50 => colorMap.get("Bull")
oscillator < 50 => colorMap.get("Bear")
=> colorMap.get("Neutral")
// Plot the `oscillator` using the `oscColor` from our `colorMap`.
plot(oscillator, "Histogram", oscColor, 2 , plot.style_histogram, histbase = 50 )
plot(oscillator, "Line", oscColor, 3 )
```
```
Notice! Map variables declared using varip behave as ones using var on historical
data, but they update their key-value pairs for realtime bars (i.e., the bars since
the
scriptʼs last compilation) on each new price tick. Maps assigned to varip variables
can
only hold values of int, float, bool, color, or string types or user-defined types
that
exclusively contain within their fields these types or collections (arrays,
matrices, or
maps) of these types.
```
```
Pine Script™
// @version= 6
indicator("Putting and getting demo")
// @variable The length of the `ta.rising()` and `ta.falling()` calculation.
int length = input.int( 2 , "Length")
// @variable A map associating `string` keys with `int` values.
var data = map.new< string , int >()
// Put a new ("Rising", `bar_index`) pair into the `data` map when `close` is risin
g
if ta.rising(close, length)
data.put("Rising", bar_index)
// Put a new ("Falling", `bar_index`) pair into the `data` map when `close` is fall
i
if ta.falling(close, length)
data.put("Falling", bar_index)
// Put the "Difference" between current "Rising" and "Falling" values into the `dat
a
data.put("Difference", data.get("Rising") - data.get("Falling"))
// @variable The difference between the last "Rising" and "Falling" `bar_index`.
int index = data.get("Difference")
// @variable
Returns `color.green` when `index` is positive, `color.red` when negativ
color indexColor = index > 0 ? color.green : index < 0 ? color.red : color.gray
plot(index, color = indexColor, style = plot.style_columns)
```
```
Pine Script™
// @version= 6
indicator("Putting and getting objects demo")
// @type A custom type to hold OHLC data.
type ChartData
float o
float h
float l
float c
// @function Updates the fields of a `ChartData` object.
method update( ChartData this) =>
this.o := open
this.h := high
this.l := low
this.c := close
// @variable A new `ChartData` instance declared on the first bar.
var myData = ChartData.new()
// @variable A map associating `string` keys with `ChartData` instances.
var myMap = map.new< string , ChartData >()
// Put a new pair with the "A" key into `myMap` only on the first bar.
if bar_index == 0
myMap.put("A", myData)
// Put a pair with the "B" key into `myMap` on every bar.
myMap.put("B", myData)
// @variable The `ChartData` value associated with the "A" key in `myMap`.
ChartData oldest = myMap.get("A")
// @variable The `ChartData` value associated with the "B" key in `myMap`.
ChartData newest = myMap.get("B")
// Update `newest`. Also affects `oldest` and `myData` since they all reference the
newest.update()
// Plot the fields of `oldest` as candles.
plotcandle(oldest.o, oldest.h, oldest.l, oldest.c)
```
```
Pine Script™
// @version= 6
indicator("Keys and values demo")
if bar_index % 50 == 0
// @variable A map containing pairs of `string` keys and `float` values.
m = map.new< string , float >()
// Put pairs into `m`. The map will maintain this insertion order.
m.put("First", math.round(math.random( 0 , 100 )))
m.put("Second", m.get("First") + 1 )
m.put("Third", m.get("Second") + 1 )
// @variable An array containing the keys of `m` in their insertion order.
array < string > keys = m.keys()
// @variable An array containing the values of `m` in their insertion order.
array < float > values = m.values()
// @variable A label displaying the `size` of `m` and the `keys` and `values` arr
label debugLabel = label.new(
bar_index, 0 ,
str.format("Pairs: {0}\nKeys: {1}\nValues: {2}", m.size(), keys, values),
color = color.navy, style = label.style_label_center,
textcolor = color.white, size = size.huge
)
```
```
Pine Script™
// @version= 6
indicator("Keys and values demo")
if bar_index % 50 == 0
// @variable A map containing pairs of `string` keys and `float` values.
m = map.new< string , float >()
// Put pairs into `m`. The map will maintain this insertion order.
m.put("First", math.round(math.random( 0 , 100 )))
m.put("Second", m.get("First") + 1 )
m.put("Third", m.get("Second") + 1 )
// Overwrite the "Second" pair in `m`. This will NOT affect the insertion order.
// The key and value will still appear second in the `keys` and `values` arrays.
m.put("Second", -2)
// @variable An array containing the keys of `m` in their insertion order.
array < string > keys = m.keys()
// @variable An array containing the values of `m` in their insertion order.
array < float > values = m.values()
// @variable A label displaying the `size` of `m` and the `keys` and `values` arr
label debugLabel = label.new(
bar_index, 0 ,
str.format("Pairs: {0}\nKeys: {1}\nValues: {2}", m.size(), keys, values),
color = color.navy, style = label.style_label_center,
textcolor = color.white, size = size.huge
)
```
```
Notice! The elements in a map.values() array point to the same values as the map
id. Consequently, when the mapʼs values are of reference types , including line,
linefill, box, polyline, label, table, chart.point, or UDTs, modifying the
instances
referenced by the map.values() array will also affect those referenced by the map
id
since the contents of both collections point to identical objects.
```
```
Pine Script™
// @version= 6
indicator("Inspecting keys demo")
// @variable A map containing `string` keys and `string` values.
m = map.new< string , string >()
// Put key-value pairs into the map.
m.put("A", "B")
m.put("C", "D")
m.put("E", "F")
// @variable An array of keys to check for in `m`.
array < string > testKeys = array.from("A", "B", "C", "D", "E", "F")
// @variable
An array containing all elements from `testKeys` found in the keys of `m
array < string > mappedKeys = array.new< string >()
for key in testKeys
// Add the `key` to `mappedKeys` if `m` contains it.
if m.contains(key)
mappedKeys.push(key)
// @variable
A string representing the `testKeys` array and the elements found within
string testText = str.format("Tested keys: {0}\nKeys found: {1}", testKeys,
mappedKe
if bar_index == last_bar_index - 1
// @variable Displays the `testText` in a label at the `bar_index` before the las
label debugLabel = label.new(
bar_index, 0 , testText, style = label.style_label_center,
textcolor = color.white, size = size.huge
)
```
```
Pine Script™
// @version= 6
indicator("Removing key-value pairs demo")
// @function Returns a label to display the keys and values from a map.
method debugLabel(
map < string , int > this, int barIndex = bar_index,
color bgColor = color.blue, string note = ""
) =>
// @variable A string representing the size, keys, and values in `this` map.
string repr = str.format(
"{0}\nSize: {1}\nKeys: {2}\nValues: {3}",
note, this.size(), str.tostring(this.keys()), str.tostring(this.values())
)
label.new(
barIndex, 0 , repr, color = bgColor, style = label.style_label_center,
textcolor = color.white, size = size.huge
)
if bar_index == last_bar_index - 1
// @variable A map containing `string` keys and `int` values.
m = map.new< string , int >()
// Put key-value pairs into `m`.
for [i, key] in array.from("A", "B", "C", "D", "E")
m.put(key, i)
m.debugLabel(bar_index, color.green, "Added pairs")
// @variable An array of keys to remove from `m`.
array < string > removeKeys = array.from("B", "B", "D", "F", "a")
// Remove each `key` in `removeKeys` from `m`.
for key in removeKeys
m.remove(key)
m.debugLabel(bar_index + 10 , color.red, "Removed pairs")
// Remove all remaining keys from `m`.
m.clear()
m.debugLabel(bar_index + 20 , color.purple, "Cleared the map")
```
```
Pine Script™
// @version= 6
indicator("Combining maps demo", "Hex map")
// @variable An array of string hex digits.
var array < string > hexDigits = str.split("0123456789ABCDEF", "")
// @function Returns a hexadecimal string for the specified `value`.
hex( int value) =>
// @variable A string representing the hex form of the `value`.
string result = ""
// @variable A temporary value for digit calculation.
int tempValue = value
while tempValue > 0
// @variable The next integer digit.
int digit = tempValue % 16
// Add the hex form of the `digit` to the `result`.
result := hexDigits.get(digit) + result
// Divide the `tempValue` by the base.
tempValue := int(tempValue / 16 )
result
// @function
Returns a map holding the `numbers` as keys and their `hex` strings as v
hexMap( array < int > numbers) =>
// @variable A map associating `int` keys with `string` values.
result = map.new< int , string >()
for number in numbers
// Put a pair containing the `number` and its `hex()` representation into th
result.put(number, hex(number))
result
// @function Returns a label to display the keys and values of a hex map.
debugLabel(
map < int , string > this, int barIndex = bar_index, color bgColor = color.blue,
string style = label.style_label_center, string note = ""
) =>
string repr = str.format(
"{0}\nDecimal: {1}\nHex: {2}",
note, str.tostring(this.keys()), str.tostring(this.values())
)
label.new(
barIndex, 0 , repr, color = bgColor, style = style,
textcolor = color.white, size = size.huge
)
if bar_index == last_bar_index - 1
// @variable A map with decimal `int` keys and hexadecimal `string` values.
map < int , string > mapA = hexMap(array.from( 101 , 202 , 303 , 404 ))
debugLabel(mapA, bar_index, color.navy, label.style_label_down, "A")
// @variable A map containing key-value pairs to add to `mapA`.
map < int , string > mapB = hexMap(array.from( 303 , 404 , 505 , 606 , 707 ,
808 ))
debugLabel(mapB, bar_index, color.maroon, label.style_label_up, "B")
// Put all pairs from `mapB` into `mapA`.
mapA.put_all(mapB)
debugLabel(mapA, bar_index + 10 , color.purple, note = "Merge B into A")
```
```
Pine Script™
for key in thisMap.keys()
value = thisMap.get(key)
```
```
Pine Script™
for [key, value] in thisMap
```
```
Pine Script™
// @version= 6
indicator("Looping through a map demo", "Table of averages")
// @variable The length of the moving average.
int length = input.int( 20 , "Length")
// @variable The size of the table text.
string txtSize = input.string(
size.huge, "Text size",
options = [size.auto, size.tiny, size.small, size.normal, size.large, size.huge
)
// @function Displays the pairs of `this` map within a table.
// @param this A map with `string` keys and `float` values.
// @param position The position of the table on the chart.
// @param header The string to display on the top row of the table.
// @param textSize The size of the text in the table.
// @returns A new `table` object with cells displaying each pair in `this`.
method toTable(
map < string , float > this, string position = position.middle_center, string heade
string textSize = size.huge
) =>
// Color variables
borderColor = #000000
headerColor = color.rgb( 1 , 88 , 80 )
pairColor = color.maroon
textColor = color.white
// @variable A table that displays the key-value pairs of `this` map.
table result = table.new(
position, this.size() + 1 , 3 , border_width = 2 , border_color = borderColor
)
// Initialize top and side header cells.
result.cell( 1 , 0 , header, bgcolor = headerColor, text_color = textColor,
text_si
result.merge_cells( 1 , 0 , this.size(), 0 )
result.cell( 0 , 1 , "Key", bgcolor = headerColor, text_color = textColor,
text_siz
result.cell( 0 , 2 , "Value", bgcolor = headerColor, text_color = textColor,
text_s
// @variable The column index of the table. Updates on each loop iteration.
int col = 1
// Loop over each `key` and `value` from `this` map in the insertion order.
for [key, value] in this
// Initialize a `key` cell in the `result` table on row 1.
result.cell(
col, 1 , str.tostring(key), bgcolor = color.maroon,
text_color = color.white, text_size = textSize
)
// Initialize a `value` cell in the `result` table on row 2.
result.cell(
col, 2 , str.tostring(value), bgcolor = color.maroon,
text_color = color.white, text_size = textSize
)
// Move to the next column index.
col += 1
result // Return the `result` table.
// @variable A map with `string` keys and `float` values to hold `length`-
bar average
averages = map.new< string , float >()
// Put key-value pairs into the `averages` map.
averages.put("Open", ta.sma(open, length))
averages.put("High", ta.sma(high, length))
averages.put("Low", ta.sma(low, length))
averages.put("Close", ta.sma(close, length))
averages.put("Volume", ta.sma(volume, length))
// @variable The text to display at the top of the table.
string headerText = str.format("{0} {1}-bar averages", "'" + syminfo.tickerid +
"'",
// Display the `averages` map in a `table` with the `headerText`.
averages.toTable(header = headerText, textSize = txtSize)
```
```
Pine Script™
// @version= 6
indicator("Shallow copy demo")
// @function Displays the key-value pairs of `this` map in a label.
method debugLabel(
map < string , float > this, int barIndex = bar_index, color bgColor = color.blue,
color textColor = color.white, string note = ""
) =>
// @variable The text to display in the label.
labelText = note + "\n{"
for [key, value] in this
labelText += str.format("{0}: {1}, ", key, value)
labelText := str.replace(labelText, ", ", "}", this.size() - 1 )
if barstate.ishistory
label result = label.new(
barIndex, 0 , labelText, color = bgColor, style = label.style_label_cent
textcolor = textColor, size = size.huge
)
if bar_index == last_bar_index - 1
// @variable A map of `string` keys and random `float` values.
m = map.new< string , float >()
// Assign random values to an array of keys in `m`.
for key in array.from("A", "B", "C", "D")
m.put(key, math.random( 0 , 10 ))
// @variable A shallow copy of `m`.
mCopy = m.copy()
// Assign the insertion order value `i` to each `key` in `mCopy`.
for [i, key] in mCopy.keys()
mCopy.put(key, i)
// Display the labels.
m.debugLabel(bar_index, note = "Original")
mCopy.debugLabel(bar_index + 10 , color.purple, note = "Copied and changed")
```
```
Pine Script™
// @version= 6
indicator("Deep copy demo")
// @function Returns a deep copy of `this` map.
method deepCopy( map < string , label > this) =>
// @variable A deep copy of `this` map.
result = map.new< string , label >()
// Add key-value pairs with copies of each `value` to the `result`.
for [key, value] in this
result.put(key, value.copy())
result //Return the `result`.
// @variable A map containing `string` keys and `label` values.
var original = map.new< string , label >()
if bar_index == last_bar_index - 1
// Put a new key-value pair into the `original` map.
map.put(
original, "Test",
label.new(bar_index, 0 , "Original", textcolor = color.white, size = size.hu
)
// @variable A shallow copy of the `original` map.
map < string , label > shallow = original.copy()
// @variable A deep copy of the `original` map.
map < string , label > deep = original.deepCopy()
// @variable The "Test" label from the `shallow` copy.
label shallowLabel = shallow.get("Test")
// @variable The "Test" label from the `deep` copy.
label deepLabel = deep.get("Test")
// Modify the "Test" label's `y` attribute in the `original` map.
// This also affects the `shallowLabel`.
original.get("Test").set_y(label.all.size())
// Modify the `shallowLabel`. Also modifies the "Test" label in the `original` m
shallowLabel.set_text("Shallow copy")
shallowLabel.set_color(color.red)
shallowLabel.set_style(label.style_label_up)
// Modify the `deepLabel`. Does not modify any other label instance.
deepLabel.set_text("Deep copy")
deepLabel.set_color(color.navy)
deepLabel.set_style(label.style_label_left)
deepLabel.set_x(bar_index + 5 )
```
```
Pine Script™
// @version= 6
indicator("Scope and history demo", overlay = true)
// @variable The source value for EMA calculation.
float source = input.source(close, "Source")
// @variable A map containing global key-value pairs.
globalData = map.new< int , float >()
// @function Calculates a set of EMAs and updates the key-
value pairs in `globalData`
update() =>
// @variable The previous map instance assigned to `globalData`.
map < int , float > previous = globalData[ 1 ]
// Put key-value pairs with keys 10-200 into `globalData` if `previous` is `na`.
if na(previous)
for i = 10 to 200
globalData.put(i, source)
else
// Iterate each `key` and `value` in the `previous` map.
for [key, value] in previous
// @variable The smoothing parameter for the `key`-length EMA.
float alpha = 2.0 / (key + 1.0)
// @variable The `key`-length EMA value.
float ema = (1.0 - alpha) * value + alpha * source
// Put the `key`-length `ema` into the `globalData` map.
globalData.put(key, ema)
// Update the `globalData` map.
update()
// @variable The array of values from `globalData` in their insertion order.
array < float > values = globalData.values()
// Plot the max EMA, min EMA, and 50-bar EMA values.
plot(values.max(), "Max EMA", color.green, 2 )
plot(values.min(), "Min EMA", color.red, 2 )
plot(globalData.get( 50 ), "50-bar EMA", color.orange, 3 )
```
```
Pine Script™
// @type A wrapper type for maps with `string` keys and `float` values.
type Wrapper
map < string , float > data
```
```
Pine Script™
mapOfMaps = map.new< string , Wrapper >()
```
```
Pine Script™
// @version= 6
indicator("Nested map demo")
// @variable The timeframe of the requested data.
string tf = input.timeframe("D", "Timeframe")
// Symbol inputs.
string symbol1 = input.symbol("EURUSD", "Symbol 1")
string symbol2 = input.symbol("GBPUSD", "Symbol 2")
string symbol3 = input.symbol("EURGBP", "Symbol 3")
// @type A wrapper type for maps with `string` keys and `float` values.
type Wrapper
map < string , float > data
// @function
Returns a wrapped map containing OHLCV data from the `tickerID` at the `
requestData( string tickerID, string timeframe) =>
// Request a tuple of OHLCV values from the specified ticker and timeframe.
[o, h, l, c, v] = request.security(
tickerID, timeframe,
[open, high, low, close, volume]
)
// @variable A map containing requested OHLCV data.
result = map.new< string , float >()
// Put key-value pairs into the `result`.
result.put("Open", o)
result.put("High", h)
result.put("Low", l)
result.put("Close", c)
result.put("Volume", v)
//Return the wrapped `result`.
Wrapper.new(result)
// @function
Returns a string representing `this` map of `string` keys and `Wrapper`
method toString( map < string , Wrapper > this) =>
// @variable A string representation of `this` map.
string result = "{"
// Iterate over each `key1` and associated `wrapper` in `this`.
for [key1, wrapper] in this
// Add `key1` to the `result`.
result += key1
// @variable A string representation of the `wrapper.data` map.
string innerStr = ": {"
// Iterate over each `key2` and associated `value` in the wrapped map.
for [key2, value] in wrapper.data
// Add the key-value pair's representation to `innerStr`.
innerStr += str.format("{0}: {1}, ", key2, str.tostring(value))
// Replace the end of `innerStr` with "}" and add to `result`.
result += str.replace(innerStr, ", ", "},\n", wrapper.data.size() - 1 )
// Replace the blank line at the end of `result` with "}".
result := str.replace(result, ",\n", "}", this.size() - 1 )
result
// @variable A map of wrapped maps containing OHLCV data from multiple tickers.
var mapOfMaps = map.new< string , Wrapper >()
// @variable A label showing the contents of the `mapOfMaps`.
var debugLabel = label.new(
bar_index, 0 , color = color.navy, textcolor = color.white, size = size.huge,
style = label.style_label_center, text_font_family = font.family_monospace
)
// Put wrapped maps into `mapOfMaps`.
mapOfMaps.put(symbol1, requestData(symbol1, tf))
mapOfMaps.put(symbol2, requestData(symbol2, tf))
mapOfMaps.put(symbol3, requestData(symbol3, tf))
// Update the label.
debugLabel.set_text(mapOfMaps.toString())
debugLabel.set_x(bar_index)
```
# Alerts
## ## Introduction
TradingView alerts run 24 x 7 on our servers and do not require users to be logged
in to
execute. Alerts are created from the charts user interface ( _UI_ ). You will find
all the information
necessary to understand how alerts work and how to create them from the charts UI
in the
Help Centerʼs About TradingView alerts page.
This page covers the different ways Pine Script™ programmers can code their scripts
to
create alert events from which script users will in turn be able to create alerts
from the charts
UI. We will cover:
```
How to use the alert() function to alert() function calls in indicators or
strategies, which
can then be included in script alerts created from the charts UI.
How to add custom alert messages to be included in script alerts triggering on the
order
fill events of strategies.
How to use the alertcondition() function to generate, in indicators only,
alertcondition()
events which can then be used to create alertcondition() alerts from the charts UI.
```
Keep in mind that:
```
No alert-related Pine Script™ code can create a running alert in the charts UI; it
merely
creates alert events which can then be used by script users to create running
alerts from
the charts UI.
Alerts only trigger in the realtime bar. The operational scope of Pine Script™ code
dealing with any type of alert is therefore restricted to realtime bars only.
When an alert is created in the charts UI, TradingView saves a mirror image of the
script
and its inputs, along with the chartʼs main symbol and timeframe to run the alert
on its
servers. Subsequent changes to your scriptʼs inputs or the chart will thus not
affect
running alerts previously created from them. If you want any changes to your
context to
be reflected in a running alertʼs behavior, you will need to delete the alert and
create a
new one in the new context.
```
## ## Background
The different methods Pine programmers can use today to create alert events in
their script
are the result of successive enhancements deployed throughout Pine Script™‘s
evolution. The
alertcondition() function, which works in indicators only, was the first feature
allowing Pine
Script™ programmers to create alert events. Then came order fill alerts for
strategies, which
trigger when the broker emulator creates _order fill events_. _Order fill events_
require no special
code for script users to create alerts on them, but by way of the **alert_message**
parameter
for order-generating **strategy.*()** functions, programmers can customize the
message of
alerts triggering on _order fill events_ by defining a distinct alert message for
any number of
order fulfillment events.
The alert() function is the most recent addition to Pine Script™. It more or less
supersedes
alertcondition(), and when used in strategies, provides a useful complement to
alerts on _order
fill events_.
For Pine Script™ programmers, the alert() function will generally be easier and
more flexible
to work with. Contrary to alertcondition(), it allows for dynamic alert messages,
works in both
indicators and strategies and the programmer decides on the frequency of alert()
events.
While alert() calls can be generated on any logic programmable in Pine, including
when orders
are **sent** to the broker emulator in strategies, they cannot be coded to trigger
when orders are
**executed** (or _filled_ ) because after orders are sent to the broker emulator,
the emulator
controls their execution and does not report fill events back to the script
directly.
When a script user wants to generate an alert on a strategyʼs order fill events, he
must include
those events when creating a _script alert_ on the strategy in the “Create Alert”
dialog box. No
special code is required in scripts for users to be able to do this. The message
sent with order
fill events can, however, be customized by programmers through use of the
**alert_message**
parameter in order-generating **strategy.*()** function calls. A combination of
alert() calls and
the use of custom **alert_message** arguments in order-generating **strategy.*()**
calls should
allow programmers to generate alert events on most conditions occurring in their
scriptʼs
execution.
## ## Script alerts
When a script user creates a _script alert_ using the “Create Alert” dialog box,
the events able
to trigger the alert will vary depending on whether the alert is created from an
indicator or a
strategy.
```
The indicator contains alert() calls.
The codeʼs logic allows a specific alert() call to execute.
The frequency specified in the alert() call allows the alert to trigger.
```
A _script alert_ created from a **strategy** can trigger on _alert() function
calls_ , on _order fill events_ ,
or both. The script user creating an alert on a strategy decides which type of
events he
wishes to include in his _script alert_. While users can create a _script alert_ on
_order fill events_
without the need for a strategy to include special code, it must contain alert()
calls for users
to include _alert() function calls_ in their _script alert_.
```
message
```
A “series string” representing the message text sent when the alert triggers.
Because this
argument allows “series” values, it can be generated at runtime and differ bar to
bar, making it
dynamic.
```
freq
```
An “input string” specifying the triggering frequency of the alert. Valid arguments
are:
```
alert.freq_once_per_bar : Only the first call per realtime bar triggers the alert
(default
value).
alert.freq_once_per_bar_close : An alert is only triggered when the realtime bar
closes
and an alert() call is executed during that script iteration.
alert.freq_all : All calls during the realtime bar trigger the alert.
```
The alert() function can be used in both indicators and strategies. For an alert()
call to trigger
a _script alert_ configured on _alert() function calls_ , the scriptʼs logic must
allow the alert() call to
execute, **and** the frequency determined by the **freq** parameter must allow the
alert to
trigger.
Note that by default, strategies are recalculated at the barʼs close, so if the
alert() function
with the frequency **alert.freq_all** or **alert.freq_once_per_bar** is used in a
strategy, then it
will be called no more often than once at the barʼs close. In order to enable the
alert() function
to be called during the bar construction process, you need to enable the
**calc_on_every_tick**
option.
```
When RSI crosses the centerline up, the script alert will trigger with the “Go
long...”
message. When RSI crosses the centerline down, the script alert will trigger with
the “Go
short...” message.
Because no argument is specified for the freq parameter in the alert() call, the
default
value of alert.freq_once_per_bar will be used, so the alert will only trigger the
first
time each of the alert() calls is executed during the realtime bar.
The message sent with the alert is composed of two parts: a constant string and
then
the result of the str.tostring() call which will include the value of RSI at the
moment
where the alert() call is executed by the script. An alert message for a cross up
would
look like: “Go long (RSI is 53.41)“.
Because a script alert always triggers on any occurrence of a call to alert(), as
long as
the frequency used in the call allows for it, this particular script does not allow
a script
user to restrict his script alert to longs only, for example.
```
Note that:
```
Contrary to an alertcondition() call which is always placed at column 0 (in the
scriptʼs
global scope), the alert() call is placed in the local scope of an if branch so it
only
executes when our triggering condition is met. If an alert() call was placed in the
scriptʼs
global scope at column 0, it would execute on all bars, which would likely not be
the
desired behavior.
An alertcondition() could not accept the same string we use for our alertʼs message
because of its use of the str.tostring() call. alertcondition() messages must be
constant
strings.
```
Lastly, because alert() messages can be constructed dynamically at runtime, we
could have
used the following code to generate our alert events:
Suppose, for our next example, that we want to provide the option of triggering
alerts on only
longs, only shorts, or both. You could code your script like this:
Note how:
```
We create a compound condition that is met only when the userʼs selection allows
for an
entry in that direction. A long entry on a crossover of the centerline only
triggers the
alert when long entries have been enabled in the scriptʼs Inputs.
We offer the user to indicate his repainting preference. When he does not allow the
calculations to repaint, we wait until the barʼs confirmation to trigger the
compound
condition. This way, the alert and the marker only appear at the end of the
realtime bar.
If a user of this script wanted to create two distinct script alerts from this
script, i.e., one
triggering only on longs, and one only on shorts, then he would need to:
Select only “Detect Longs” in the inputs and create a first script alert on the
script.
Select only “Detect Shorts” in the Inputs and create another script alert on the
script.
```
## ## In strategies
alert() function calls can be used in strategies also, with the provision that
strategies, by
default, only execute on the close of realtime bars. Unless **calc_on_every_tick =
true** is
used in the strategy() declaration statement, all alert() calls will use the
**alert.freq_once_per_bar_close** frequency, regardless of the argument used for
**freq**.
While _script alerts_ on strategies will use _order fill events_ to trigger alerts
when the broker
emulator fills orders, alert() can be used advantageously to generate other alert
events in
strategies.
This strategy creates _alert() function calls_ when RSI moves against the trade for
three
consecutive bars:
If a user created a _script alert_ from this strategy and included both _order fill
events_ and _alert()
function calls_ in his alert, the alert would trigger whenever an order is
executed, or when one
of the alert() calls was executed by the script on the realtime barʼs closing
iteration, i.e., when
barstate.isrealtime and barstate.isconfirmed are both true. The _alert() function
events_ in the
script would only trigger the alert when the realtime bar closes because
**alert.freq_once_per_bar_close** is the argument used for the **freq** parameter
in the alert()
calls.
When a _script alert_ is created from an indicator, it can only trigger on _alert()
function calls_.
However, when a _script alert_ is created from a strategy, the user can specify
that _order fill
events_ also trigger the _script alert_. An _order fill event_ is any event
generated by the broker
emulator which causes a simulated order to be executed. It is the equivalent of a
trade order
being filled by a broker/exchange. Orders are not necessarily executed when they
are placed.
In a strategy, the execution of orders can only be detected indirectly and after
the fact, by
analyzing changes in built-in variables such as strategy.opentrades or
strategy.position_size.
_Script alerts_ configured on _order fill events_ are thus useful in that they
allow the triggering of
alerts at the precise moment of an orderʼs execution, before a scriptʼs logic can
detect it.
Pine Script™ programmers can customize the alert message sent when specific orders
are
executed. While this is not a pre-requisite for _order fill events_ to trigger,
custom alert
messages can be useful because they allow custom syntax to be included with alerts
in order
to route actual orders to a third-party execution engine, for example. Specifying
custom alert
messages for specific _order fill events_ is done by means of the **alert_message**
parameter in
functions which can generate orders: strategy.close(), strategy.entry(),
strategy.exit() and
strategy.order().
Letʼs look at a strategy where we use the **alert_message** parameter in both our
strategy.entry() calls:
Note that:
```
We use the stop parameter in our strategy.entry() calls, which creates stop-buy and
stop-sell orders. This entails that buy orders will only execute once price is
higher than
the high on the bar where the order is placed, and sell orders will only execute
once
price is lower than the [low] on the bar where the order is placed.
The up/down arrows which we plot with plotchar() are plotted when orders are
placed.
Any number of bars may elapse before the order is actually executed, and in some
cases
the order will never be executed because price does not meet the required
condition.
Because we use the same id argument for all buy orders, any new buy order placed
before a previous orderʼs condition is met will replace that order. The same
applies to
sell orders.
Variables included in the alert_message argument are evaluated when the order is
executed, so when the alert triggers.
```
When the **alert_message** parameter is used in a strategyʼs order-generating
**strategy.*()**
function calls, script users must include the **{{strategy.order.alert_message}}**
placeholder
in the “Create Alert” dialog boxʼs “Message” field when creating _script alerts_ on
_order fill
events_. This is required so the **alert_message** argument used in the order-
generating
**strategy.*()** function calls is used in the message of alerts triggering on each
_order fill
event_. When only using the **{{strategy.order.alert_message}}** placeholder in the
“Message” field and the **alert_message** parameter is present in only some of the
order-
generating **strategy.*()** function calls in your strategy, an empty string will
replace the
placeholder in the message of alerts triggered by any order-generating
**strategy.*()**
function call not using the **alert_message** parameter.
While other placeholders can be used in the “Create Alert” dialog boxʼs “Message”
field by
users creating alerts on _order fill events_ , they cannot be used in the argument
of
**alert_message**.
## ## `alertcondition()` events
```
condition
```
A “series bool” value ( **true** or **false** ) which determines when the alert
will trigger. It is a
required argument. When the value is **true** the alert will trigger. When the
value is **false**
the alert will not trigger. Contrary to alert() function calls, alertcondition()
calls must start at
column zero of a line, so cannot be placed in conditional blocks.
```
title
```
A “const string” optional argument that sets the name of the alert condition as it
will appear in
the “Create Alert” dialog boxʼs “Condition” field in the charts UI. If no argument
is supplied,
“Alert” will be used.
```
message
```
A “const string” optional argument that specifies the text message to display when
the alert
triggers. The text will appear in the “Message” field of the “Create Alert” dialog
box, from
where script users can then modify it when creating an alert. **As this argument
must be a
“const string”, it must be known at compilation time and thus cannot vary bar to
bar.** It
can, however, contain placeholders which will be replaced at runtime by dynamic
values that
may change bar to bar. See this pageʼs Placeholders section for a list.
The alertcondition() function does not include a **freq** parameter. The frequency
of
_alertcondition() alerts_ is determined by users in the “Create Alert” dialog box.
Because we have two alertcondition() calls in our script, two different alerts will
be available in
the “Create Alert” dialog boxʼs “Condition” field: “Long Alert” and “Short Alert”.
If we wanted to include the value of RSI when the cross occurs, we could not simply
add its
value to the **message** string using **str.tostring(r)** , as we could in an
alert() call or in an
**alert_message** argument in a strategy. We can, however, include it using a
placeholder. This
shows two alternatives:
Note that:
```
The first line uses the {{plot_0}} placeholder, where the plot number corresponds
to
the order of the plot in the script.
The second line uses the {{plot("[plot_title]")}} type of placeholder, which must
include the title of the plot() call used in our script to plot RSI. Double quotes
are
used to wrap the plotʼs title inside the {{plot("RSI")}} placeholder. This requires
that
we use single quotes to wrap the message string.
Using one of these methods, we can include any numeric value that is plotted by our
indicator, but as strings cannot be plotted, no string variable can be used.
```
## ## Using compound conditions
If we want to offer script users the possiblity of creating a single alert from an
indicator using
multiple alertcondition() calls, we will need to provide options in the scriptʼs
inputs through
which users will indicate the conditions they want to trigger their alert before
creating it.
Note how the alertcondition() call is allowed to trigger on one of two conditions.
Each
condition can only trigger the alert if the user enables it in the scriptʼs inputs
before creating
the alert.
## ## Placeholders
Note that users creating _alertcondition() alerts_ from the “Create Alert” dialog
box in the charts
UI are also able to use these placeholders in the dialog boxʼs “Message” field.
```
{{exchange}}
```
Exchange of the symbol used in the alert (NASDAQ, NYSE, MOEX, etc.). Note that for
delayed
symbols, the exchange will end with “_DL” or “_DLY.” For example, “NYMEX_DL.”
```
{{interval}}
```
Returns the timeframe of the chart the alert is created on. Note that Range charts
are
calculated based on 1 m data, so the placeholder will always return “1” on any
alert created on
a Range chart.
```
{{open}} , {{high}} , {{low}} , {{close}} , {{volume}}
```
Corresponding values of the bar on which the alert has been triggered.
```
{{plot_0}} , {{plot_1}} , [...], {{plot_19}}
```
Value of the corresponding plot number. Plots are numbered from zero to 19 in order
of
appearance in the script, so only one of the first 20 plots can be used. For
example, the built-
in “Volume” indicator has two output series: Volume and Volume MA, so you could use
the
following:
```
{{plot("[plot_title]")}}
```
This placeholder can be used when one needs to refer to a plot using the **title**
argument
used in a plot() call. Note that double quotation marks ( **"** ) **must** be used
inside the
placeholder to wrap the **title** argument. This requires that a single quotation
mark ( **'** ) be
used to wrap the **message** string:
```
{{ticker}}
```
Ticker of the symbol used in the alert (AAPL, BTCUSD, etc.).
```
{{time}}
```
Returns the time at the beginning of the bar. Time is UTC, formatted as **yyyy-MM-
ddTHH:mm:ssZ** , so for example: **2019-08-27T09:56:00Z**.
```
{{timenow}}
```
Current time when the alert triggers, formatted in the same way as **{{time}}**.
The precision
is to the nearest second, regardless of the chartʼs timeframe.
The most common instances of repainting traders want to avoid with alerts are ones
where
they must prevent an alert from triggering at some point during the realtime bar
when it would
**not** have triggered at its close. This can happen when these conditions are met:
```
The calculations used in the condition triggering the alert can vary during the
realtime
bar. This will be the case with any calculation using high , low or close , for
example,
which includes almost all built-in indicators. It will also be the case with the
result of any
request.security() call using a higher timeframe than the chartʼs, when the higher
timeframeʼs current bar has not closed yet.
The alert can trigger before the close of the realtime bar, so with any frequency
other
than “Once Per Bar Close”.
```
The simplest way to avoid this type of repainting is to configure the triggering
frequency of
alerts so they only trigger on the close of the realtime bar. There is no panacea;
avoiding this
type of repainting **always** entails waiting for confirmed information, which
means the trader
must sacrifice immediacy to achieve reliability.
Note that other types of repainting such as those documented in our Repainting
section may
not be preventable by simply triggering alerts on the close of realtime bars.
```
Next
Backgrounds
```
```
alert(message, freq)
```
```
Pine Script™
// @version= 6
indicator("All `alert()` calls")
r = ta.rsi(close, 20 )
// Detect crosses.
xUp = ta.crossover( r, 50 )
xDn = ta.crossunder(r, 50 )
// Trigger an alert on crosses.
if xUp
alert("Go long (RSI is " + str.tostring(r, "#.00)"))
else if xDn
alert("Go short (RSI is " + str.tostring(r, "#.00)"))
plotchar(xUp, "Go Long", "▲", location.bottom, color.lime, size = size.tiny)
plotchar(xDn, "Go Short", "▼", location.top, color.red, size = size.tiny)
hline( 50 )
plot(r)
```
```
Pine Script™
// Trigger an alert on crosses.
if xUp or xDn
firstPart = (xUp? "Go long" : "Go short") + " (RSI is "
alert(firstPart + str.tostring(r, "#.00)"))
```
```
Pine Script™
// @version= 6
indicator("Selective `alert()` calls")
detectLongsInput = input.bool(true, "Detect Longs")
detectShortsInput = input.bool(true, "Detect Shorts")
repaintInput = input.bool(false, "Allow Repainting")
r = ta.rsi(close, 20 )
// Detect crosses.
xUp = ta.crossover( r, 50 )
xDn = ta.crossunder(r, 50 )
// Only generate entries when the trade's direction is allowed in inputs.
enterLong = detectLongsInput and xUp and (repaintInput or barstate.isconfirmed)
enterShort = detectShortsInput and xDn and (repaintInput or barstate.isconfirmed)
// Trigger the alerts only when the compound condition is met.
if enterLong
alert("Go long (RSI is " + str.tostring(r, "#.00)"))
else if enterShort
alert("Go short (RSI is " + str.tostring(r, "#.00)"))
plotchar(enterLong, "Go Long", "▲", location.bottom, color.lime, size = size.tiny)
plotchar(enterShort, "Go Short", "▼", location.top, color.red, size = size.tiny)
hline( 50 )
plot(r)
```
```
Pine Script™
// @version= 6
strategy("Strategy with selective `alert()` calls")
r = ta.rsi(close, 20 )
// Detect crosses.
xUp = ta.crossover( r, 50 )
xDn = ta.crossunder(r, 50 )
// Place orders on crosses.
if xUp
strategy.entry("Long", strategy.long)
else if xDn
strategy.entry("Short", strategy.short)
// Trigger an alert when RSI diverges from our trade's direction.
divInLongTrade = strategy.position_size > 0 and ta.falling(r, 3 )
divInShortTrade = strategy.position_size < 0 and ta.rising( r, 3 )
if divInLongTrade
alert("WARNING: Falling RSI", alert.freq_once_per_bar_close)
if divInShortTrade
alert("WARNING: Rising RSI", alert.freq_once_per_bar_close)
plotchar(xUp, "Go Long", "▲", location.bottom, color.lime, size = size.tiny)
plotchar(xDn, "Go Short", "▼", location.top, color.red, size = size.tiny)
plotchar(divInLongTrade, "WARNING: Falling RSI", "•", location.top, color.red,
plotchar(divInShortTrade, "WARNING: Rising RSI", "•", location.bottom, color.lime,
hline( 50 )
plot(r)
```
```
Pine Script™
// @version= 6
strategy("Strategy using `alert_message`")
r = ta.rsi(close, 20 )
// Detect crosses.
xUp = ta.crossover( r, 50 )
xDn = ta.crossunder(r, 50 )
// Place order on crosses using a custom alert message for each.
if xUp
strategy.entry("Long", strategy.long, stop = high, alert_message = "Stop-buy exe
else if xDn
strategy.entry("Short", strategy.short, stop = low, alert_message = "Stop-sell e
plotchar(xUp, "Go Long", "▲", location.bottom, color.lime, size = size.tiny)
plotchar(xDn, "Go Short", "▼", location.top, color.red, size = size.tiny)
hline( 50 )
plot(r)
```
```
alertcondition(condition, title, message)
```
```
Pine Script™
// @version= 6
indicator("`alertcondition()` on single condition")
r = ta.rsi(close, 20 )
xUp = ta.crossover( r, 50 )
xDn = ta.crossunder(r, 50 )
plot(r, "RSI")
hline( 50 )
plotchar(xUp, "Long", "▲", location.bottom, color.lime, size = size.tiny)
plotchar(xDn, "Short", "▼", location.top, color.red, size = size.tiny)
alertcondition(xUp, "Long Alert", "Go long")
alertcondition(xDn, "Short Alert", "Go short ")
```
```
Pine Script™
alertcondition(xUp, "Long Alert", "Go long. RSI is {{plot_0}}")
alertcondition(xDn, "Short Alert", 'Go short. RSI is {{plot("RSI")}}')
```
```
Pine Script™
// @version= 6
indicator("`alertcondition()` on multiple conditions")
detectLongsInput = input.bool(true, "Detect Longs")
detectShortsInput = input.bool(true, "Detect Shorts")
r = ta.rsi(close, 20 )
// Detect crosses.
xUp = ta.crossover( r, 50 )
xDn = ta.crossunder(r, 50 )
// Only generate entries when the trade's direction is allowed in inputs.
enterLong = detectLongsInput and xUp
enterShort = detectShortsInput and xDn
plot(r)
plotchar(enterLong, "Go Long", "▲", location.bottom, color.lime, size = size.tiny)
plotchar(enterShort, "Go Short", "▼", location.top, color.red, size = size.tiny)
hline( 50 )
// Trigger the alert when one of the conditions is met.
alertcondition(enterLong or enterShort, "Compound alert", "Entry")
```
```
Pine Script™
alertcondition(volume > ta.sma(volume, 20 ), "Volume alert",
"Volume ({{plot_0}}) > av
```
```
Pine Script™
// @version= 6
indicator("")
r = ta.rsi(close, 14 )
xUp = ta.crossover(r, 50 )
plot(r, "RSI", display = display.none)
alertcondition(xUp, "xUp alert", message = 'RSI is bullish at: {{plot("RSI")}}')
```
The bgcolor() function changes the color of the scriptʼs background. If the script
is running in
**overlay = true** mode, then it will color the chartʼs background.
Its **color** parameter allows a “series color” to be used for its argument, so it
can be
dynamically calculated in an expression.
Here is a script that colors the background of trading sessions (try it on 30 min
EURUSD, for
example):
Note that:
```
The script only works on chart timeframes of 30 min or less. It prints an error
message
when the chartʼs timeframe is higher than 30 min.
When the if structureʼs else branch is used because the chartʼs timeframe is
incorrect,
the local block returns the NO_COLOR color so that no background is displayed in
that
case.
We first initialize constants using our base colors, which include the 40
transparency in
hex notation at the end. 40 in the hexadecimal notation on the reversed 00-FF scale
for
transparency corresponds to 75 in Pine Script™‘s 0-100 decimal scale for
transparency.
We provide color inputs allowing script users to change the default colors we
propose.
```
In our next example, we generate a gradient for the background of a CCI line:
Note that:
```
We use the ta.cci() built-in function to calculate the indicator value.
We use the ta.percentrank() built-in function to calculate myCCIPosition , i.e.,
the
percentage of past myCCI values in the last 100 bars that are below the current
value of
myCCI.
To calculate the gradient, we use two different calls of the color.from_gradient()
built-in:
one for the bull gradient when myCCIPosition is in the 50-100% range, which means
that more past values are below its current value, and another for the bear
gradient
when myCCIPosition is in the 0-49.99% range, which means that more past values are
above it.
We provide inputs so the user can change the bull/bear colors, and we place both
color
input widgets on the same line using inline = "1" in both input.color() calls.
We plot the CCI signal using two plot() calls to achieve the best contrast over the
busy
background: the first plot is a 3-pixel wide white background, the second plot()
call plots
the thin, 1-pixel wide black line.
```
See the Colors page for more examples of backgrounds.
```
Previous
Alerts
```
```
Next
Bar coloring
```
```
bgcolor(color, offset, editable, show_last, title, force_overlay) → void
```
```
Pine Script™
// @version= 6
indicator("Session backgrounds", overlay = true)
// Default color constants using tranparency of 25.
BLUE_COLOR = #0050FF40
PURPLE_COLOR = #0000FF40
PINK_COLOR = #5000FF40
NO_COLOR = color(na)
// Allow user to change the colors.
preMarketColor = input.color(BLUE_COLOR, "Pre-market")
regSessionColor = input.color(PURPLE_COLOR, "Pre-market")
postMarketColor = input.color(PINK_COLOR, "Pre-market")
// Function returns `true` when the bar's time is
timeInRange(tf, session) =>
time(tf, session) != 0
// Function prints a message at the bottom-right of the chart.
f_print(_text) =>
var table _t = table.new(position.bottom_right, 1 , 1 )
table.cell(_t, 0 , 0 , _text, bgcolor = color.yellow)
var chartIs30MinOrLess = timeframe.isseconds or (timeframe.isintraday and
timeframe.
sessionColor = if chartIs30MinOrLess
switch
timeInRange(timeframe.period, "0400-0930") => preMarketColor
timeInRange(timeframe.period, "0930-1600") => regSessionColor
timeInRange(timeframe.period, "1600-2000") => postMarketColor
=> NO_COLOR
else
f_print("No background is displayed.\nChart timeframe must be <= 30min.")
NO_COLOR
bgcolor(sessionColor)
```
```
Pine Script™
// @version= 6
indicator("CCI Background")
bullColor = input.color(color.lime, "", inline = "1")
bearColor = input.color(color.fuchsia, "", inline = "1")
// Calculate CCI.
myCCI = ta.cci(hlc3, 20 )
// Get relative position of CCI in last 100 bars, on a 0-100% scale.
myCCIPosition = ta.percentrank(myCCI, 100 )
// Generate a bull gradient when position is 50-100%, bear gradient when position i
s
backgroundColor = if myCCIPosition >= 50
color.from_gradient(myCCIPosition, 50 , 100 , color.new(bullColor, 75 ),
bullColor)
else
color.from_gradient(myCCIPosition, 0 , 50 , bearColor, color.new(bearColor,
75 ))
// Wider white line background.
plot(myCCI, "CCI", color.white, 3 )
// Think black line.
plot(myCCI, "CCI", color.black, 1 )
// Zero level.
hline( 0 )
// Gradient background.
bgcolor(backgroundColor)
```
# Bar coloring
The barcolor() function colors bars on the main chart, regardless of whether the
script is
running in the main chart pane or a separate pane.
The coloring can be conditional because the **color** parameter accepts “series
color”
arguments.
The following script renders _inside_ and _outside_ bars in different colors:
Note that:
```
The na value leaves bars as is.
In the barcolor() call, we use embedded ?: ternary operator expressions to select
the
color.
```
```
Previous
Backgrounds
```
```
Next
Bar plotting
```
```
barcolor(color, offset, editable, show_last, title, display) → void
```
```
Pine Script™
// @version= 6
indicator("barcolor example", overlay = true)
isUp = close > open
isDown = close <= open
isOutsideUp = high > high[ 1 ] and low < low[ 1 ] and isUp
isOutsideDown = high > high[ 1 ] and low < low[ 1 ] and isDown
isInside = high < high[ 1 ] and low > low[ 1 ]
barcolor(isInside? color.yellow : isOutsideUp? color.aqua : isOutsideDown? color.
```
# Bar plotting
## ## Introduction
Both functions require four arguments that will be used for the OHLC prices (open,
high, low,
close) of the bars they will be plotting. If one of those is na, no bar is plotted.
This plots simple candles, all in blue, using the habitual OHLC values, in a
separate pane:
Note that the **color** parameter accepts “series color” arguments, so constant
values such as
**color.red** , **color.lime** , **"#FF9090"** , as well as expressions that
calculate colors at runtime,
as is done with the **paletteColor** variable here, will all work.
You can build bars or candles using values other than the actual OHLC values. For
example
you could calculate and plot smoothed candles using the following code, which also
colors
wicks depending on the position of close relative to the smoothed close ( **c** )
of our indicator:
You may find it useful to plot OHLC values taken from a higher timeframe. You can,
for
example, plot daily bars on an intraday chart:
Note that:
```
We show the scriptʼs plot after having used “Visual Order/Bring to Front” from the
scriptʼs “More” menu. This causes our scriptʼs candles to appear on top of the
chartʼs
candles.
```
```
The script will only display candles when two conditions are met:
```
```
The chart is using an intraday timeframe (see the check on
```
```
timeframe.isintraday in the plotcandle() call). We do this because itʼs not useful
to
show a daily value on timeframes higher or equal to 1 D.
```
```
The
```
```
request.security() function returns non na values (see gaps = barmerge.gaps_on in
the function call).
```
```
We use a tuple ( [open, high, low, close] ) with request.security() to fetch four
values
in one call.
```
```
We use var to declare our UP_COLOR and DN_COLOR color constants on bar zero only.
We use constants because those colors are used in more than one place in our code.
This way, if we need to change them, we need only do so in one place.
```
```
We create a lighter transparency for the body of our candles in the bodyColor
variable
initialization, so they donʼt obstruct the chartʼs candles.
```
## ## Plotting bars with `plotbar()`
This plots conventional bars using the same coloring logic as in the second example
of the
previous section:
```
Previous
Bar coloring
```
```
Next
Bar states
```
```
plotcandle(open, high, low, close, title, color, wickcolor, editable,
show_last, bordercolor, display) → void
```
```
Pine Script™
// @version= 6
indicator("Single-color candles")
plotcandle(open, high, low, close)
```
```
Pine Script™
// @version= 6
indicator("Example 2")
paletteColor = close >= open? color.lime : color.red
plotbar(open, high, low, close, color = paletteColor)
```
```
Pine Script™
// @version= 6
indicator("Smoothed candles", overlay = true)
lenInput = input.int( 9 )
smooth(source, length) =>
ta.sma(source, length)
o = smooth(open, lenInput)
h = smooth(high, lenInput)
l = smooth(low, lenInput)
c = smooth(close, lenInput)
ourWickColor = close > c? color.green : color.red
plotcandle(o, h, l, c, wickcolor = ourWickColor)
```
```
Pine Script™
// NOTE: Use this script on an intraday chart.
// @version= 6
indicator("Daily bars")
// Use gaps to only return data when the 1D timeframe completes, `na` otherwise.
[o, h, l, c] = request.security(syminfo.tickerid, "D", [open, high, low, close],
gap
var color UP_COLOR = color.silver
var color DN_COLOR = color.blue
color wickColor = c >= o? UP_COLOR : DN_COLOR
color bodyColor = c >= o? color.new(UP_COLOR, 70 ) : color.new(DN_COLOR, 70 )
// Only plot candles on intraday timeframes,
// and when non `na` values are returned by `request.security()` because a HTF has
c
plotcandle(timeframe.isintraday? o : na, h, l, c, color = bodyColor, wickcolor = wi
```
```
plotbar(open, high, low, close, title, color, editable, show_last,
display, force_overlay) → void
```
```
Pine Script™
// @version= 6
indicator("Dual-color bars")
paletteColor = close >= open? color.lime : color.red
plotbar(open, high, low, close, color = paletteColor)
```
# Bar states
## ## Introduction
These states can be used to restrict the execution or the logic of your code to
specific bars.
Some built-ins return information on the trading session the current bar belongs
to. They are
explained in the Session states section.
Note that while indicators and libraries run on all price or volume updates in real
time,
strategies not using **calc_on_every_tick** will not; they will only execute when
the realtime bar
closes. This will affect the detection of bar states in that type of script. On
open markets, for
example, this code will not display a background until the realtime closes because
that is
when the strategy runs:
## ## `barstate.isfirst`
barstate.isfirst is only **true** on the datasetʼs first bar, i.e., when bar_index
is zero.
## ## `barstate.islast`
barstate.islast is **true** if the current bar is the last one on the chart,
whether that bar is a
realtime bar or not.
It can be used to restrict the execution of code to the chartʼs last bar, which is
often useful
when drawing lines, labels or tables. Here, we use it to determine when to update a
label
which we want to appear only on the last bar. We create the label only once and
then update
its properties using **label.set_*()** functions because it is more efficient:
## ## `barstate.ishistory`
## ## `barstate.isrealtime`
## ## `barstate.isnew`
barstate.isnew is **true** on all historical bars and on the realtime barʼs first
(opening) update.
All historical bars are considered _new_ bars because the Pine Script™ runtime
executes your
script on each bar sequentially, from the chartʼs first bar in time, to the last.
Each historical bar
is thus _discovered_ by your script as it executes, bar to bar.
barstate.isnew can be useful to reset varip variables when a new realtime bar comes
in. The
following code will reset **updateNo** to 1 on all historical bars and at the
beginning of each
realtime bar. It calculates the number of realtime updates during each realtime
bar:
## ## `barstate.isconfirmed`
## ## `barstate.islastconfirmedhistory`
## ## Example
Note that:
```
Each stateʼs name will appear in the labelʼs text when it is true.
There are five possible colors for the labelʼs background:
fuchsia on the first bar
silver on historical bars
gray on the last confirmed historical bar
orange when a realtime bar is confirmed (when it closes and becomes an elapsed
realtime bar)
red on the realtime barʼs first execution
yellow for other executions of the realtime bar
```
We begin by adding the indicator to the chart of an open market, but before any
realtime
update is received. Note how the last confirmed history bar is identified in #1,
and how the
last bar is identified as the last one, but is still considered a historical bar
because no realtime
updates have been received.
Letʼs look at what happens when realtime updates start coming in:
Note that:
```
The realtime bar is red because it is its first execution, because barstate.isnew
is
true and barstate.ishistory is no longer true , so our switch structure determing
our color uses the barstate.isnew => color.red branch. This will usually not last
long
because on the next update barstate.isnew will no longer be true so the labelʼs
color
will turn yellow.
The label of elapsed realtime bars is orange because those bars were not historical
bars
when they closed. Accordingly, the barstate.ishistory => color.silver branch in the
switch structure was not executed, but the next one, barstate.isconfirmed =>
color.orange was.
```
This last example shows how the realtime barʼs label will turn yellow after the
first execution
on the bar. This is the way the label will usually appear on realtime bars:
```
Previous
Bar plotting
```
```
Next
Chart information
```
```
Pine Script™
// @version= 6
strategy("S")
bgcolor(barstate.islast? color.silver : na)
```
```
Pine Script™
// Declare array and set its values on the first bar only.
FILL_COLOR = color.green
var fillColors = array.new_color( 0 )
if barstate.isfirst
// Initialize the array elements with progressively lighter shades of the fill c
array.push(fillColors, color.new(FILL_COLOR, 70 ))
array.push(fillColors, color.new(FILL_COLOR, 75 ))
array.push(fillColors, color.new(FILL_COLOR, 80 ))
array.push(fillColors, color.new(FILL_COLOR, 85 ))
array.push(fillColors, color.new(FILL_COLOR, 90 ))
```
```
Pine Script™
// @version= 6
indicator("", "", true)
// Create label on the first bar only.
var label hiLabel = label.new(na, na, "")
// Update the label's position and text on the last bar,
// including on all realtime bar updates.
if barstate.islast
label.set_xy(hiLabel, bar_index, high)
label.set_text(hiLabel, str.tostring(high, format.mintick))
```
```
Pine Script™
// @version= 6
indicator("")
updateNo() =>
varip int updateNo = na
if barstate.isnew
updateNo := 1
else
updateNo += 1
plot(updateNo())
```
```
Pine Script™
// @version= 6
indicator("")
myRSI = ta.rsi(close, 20 )
plot(barstate.isconfirmed? myRSI : na)
```
```
Pine Script™
// @version= 6
indicator("Bar States", overlay = true, max_labels_count = 500 )
stateText() =>
string txt = ""
txt += barstate.isfirst? "isfirst\n" : ""
txt += barstate.islast? "islast\n" : ""
txt += barstate.ishistory? "ishistory\n" : ""
txt += barstate.isrealtime? "isrealtime\n" : ""
txt += barstate.isnew? "isnew\n" : ""
txt += barstate.isconfirmed? "isconfirmed\n" : ""
txt += barstate.islastconfirmedhistory? "islastconfirmedhistory\n" : ""
labelColor = switch
barstate.isfirst => color.fuchsia
barstate.islastconfirmedhistory => color.gray
barstate.ishistory => color.silver
barstate.isconfirmed => color.orange
barstate.isnew => color.red
=> color.yellow
label.new(bar_index, na, stateText(), yloc = yloc.abovebar, color = labelColor)
```
# Chart information
## ## Introduction
The way scripts can obtain information about the chart and symbol they are
currently running
on is through a subset of Pine Script™‘s built-in variables. The ones we cover here
allow
scripts to access information relating to:
```
The chartʼs prices and volume
The chartʼs symbol
The chartʼs timeframe
The session (or time period) the symbol trades on
```
## ## Prices and volume
```
open: the barʼs opening price.
high: the barʼs highest price, or the highest price reached during the realtime
barʼs
elapsed time.
low: the barʼs lowest price, or the lowest price reached during the realtime barʼs
elapsed
time.
close: the barʼs closing price, or the current price in the realtime bar.
volume: the volume traded during the bar, or the volume traded during the realtime
barʼs
elapsed time. The unit of volume information varies with the instrument. It is in
shares
for stocks, in lots for forex, in contracts for futures, in the base currency for
crypto, etc.
```
Other values are available through:
```
hl 2 : the average of the barʼs high and low values.
hlc 3 : the average of the barʼs high, low and close values.
ohlc 4 : the average of the barʼs open, high, low and close values.
```
On historical bars, the values of the above variables do not vary during the bar
because only
OHLCV information is available on them. When running on historical bars, scripts
execute on
the barʼs close, when all the barʼs information is known and cannot change during
the scriptʼs
execution on the bar.
Realtime bars are another story altogether. When indicators (or strategies using
**calc_on_every_tick = true** ) run in realtime, the values of the above variables
(except open)
will vary between successive iterations of the script on the realtime bar, because
they
represent their **current** value at one point in time during the progress of the
realtime bar. This
may lead to one form of repainting. See the page on Pine Script™‘s execution model
for more
details.
## ## Symbol information
```
syminfo.basecurrency: the base currency, e.g., “BTC” in “BTCUSD”, or “EUR” in
“EURUSD”.
syminfo.currency: the quote currency, e.g., “USD” in “BTCUSD”, or “CAD” in
“USDCAD”.
syminfo.description: The long description of the symbol.
syminfo.mintick: The symbolʼs tick value, or the minimum increment price can move
in.
Not to be confused with pips or points. On “ES1!” (“S&P 500 E-Mini”) the tick size
is
0.25 because that is the minimal increment the price moves in.
syminfo.pointvalue: The point value is the multiple of the underlying asset
determining a
contractʼs value. On “ES1!” (“S&P 500 E-Mini”) the point value is 50, so a contract
is
worth 50 times the price of the instrument.
syminfo.prefix: The prefix is the exchange or brokerʼs identifier: “NASDAQ” or
“BATS” for
“AAPL”, “CME_MINI_DL” for “ES1!”.
syminfo.root: It is the tickerʼs prefix for structured tickers like those of
futures. It is “ES”
for “ES1!”, “ZW” for “ZW1!”.
syminfo.session: It reflects the session setting on the chart for that symbol. If
the “Chart
settings/Symbol/Session” field is set to “Extended”, it will only return “extended”
if the
symbol and the userʼs feed allow for extended sessions. It is rarely displayed and
used
mostly as an argument to the session parameter in ticker.new().
syminfo.ticker: It is the symbolʼs name, without the exchange part
(syminfo.prefix):
“BTCUSD”, “AAPL”, “ES1!”, “USDCAD”.
syminfo.tickerid: This string is rarely displayed. It is mostly used as an argument
for
request.security()‘s symbol parameter. It includes session, prefix and ticker
information.
syminfo.timezone: The timezone the symbol is traded in. The string is an IANA time
zone
database name (e.g., “America/New_York”).
syminfo.type: The type of market the symbol belongs to. The values are “stock”,
“futures”, “index”, “forex”, “crypto”, “fund”, “dr”, “cfd”, “bond”, “warrant”,
“structured” and
“right”.
```
This script will display the values of those built-in variables on the chart:
## ## Chart timeframe
A script can obtain information on the type of timeframe used on the chart using
these built-
ins, which all return a “simple bool” result:
```
timeframe.isseconds
timeframe.isminutes
timeframe.isintraday
timeframe.isdaily
timeframe.isweekly
timeframe.ismonthly
timeframe.isdwm
```
Two additional built-ins return more specific timeframe information:
```
timeframe.multiplier returns a “simple int” containing the multiplier of the
timeframe
unit. A chart timeframe of one hour will return 60 because intraday timeframes are
expressed in minutes. A 30 sec timeframe will return 30 (seconds), a daily chart
will
return 1 (day), a quarterly chart will return 3 (months), and a yearly chart will
return
12 (months). The value of this variable cannot be used as an argument to timeframe
parameters in built-in functions, as they expect a string in timeframe
specifications
format.
timeframe.period returns a string in Pine Script™‘s timeframe specification format.
```
See the page on Timeframes for more information.
## ## Session information
```
The syminfo.session built-in variable returns a value that is either
session.regular or
session.extended. It reflects the session setting on the chart for that symbol. If
the
“Chart settings/Symbol/Session” field is set to “Extended”, it will only return
“extended”
if the symbol and the userʼs feed allow for extended sessions. It is used when a
session
type is expected, for example as the argument for the session parameter in
ticker.new().
Session state built-ins provide information on the trading session a bar belongs
to.
```
```
Previous
Bar states
```
```
Next
Colors
```
```
Pine Script™
// @version= 6
indicator("`syminfo.*` built-ins", "", true)
printTable(txtLeft, txtRight) =>
var table t = table.new(position.middle_right, 2 , 1 )
table.cell(t, 0 , 0 , txtLeft, bgcolor = color.yellow, text_halign =
text.align_ri
table.cell(t, 1 , 0 , txtRight, bgcolor = color.yellow, text_halign =
text.align_l
nl = "\n"
left =
"syminfo.basecurrency: " + nl +
"syminfo.currency: " + nl +
"syminfo.description: " + nl +
"syminfo.mintick: " + nl +
"syminfo.pointvalue: " + nl +
"syminfo.prefix: " + nl +
"syminfo.root: " + nl +
"syminfo.session: " + nl +
"syminfo.ticker: " + nl +
"syminfo.tickerid: " + nl +
"syminfo.timezone: " + nl +
"syminfo.type: "
right =
syminfo.basecurrency + nl +
syminfo.currency + nl +
syminfo.description + nl +
str.tostring(syminfo.mintick) + nl +
str.tostring(syminfo.pointvalue) + nl +
syminfo.prefix + nl +
syminfo.root + nl +
syminfo.session + nl +
syminfo.ticker + nl +
syminfo.tickerid + nl +
syminfo.timezone + nl +
syminfo.type
printTable(left, right)
```
# Colors
## ## Introduction
Script visuals can play a critical role in the usability of the indicators we write
in Pine Script™.
Well-designed plots and drawings make indicators easier to use and understand. Good
visual
designs establish a visual hierarchy that allows the more important information to
stand out,
and the less important one to not get in the way.
Using colors in Pine can be as simple as you want, or as involved as your concept
requires.
The 4,294,967,296 possible assemblies of color and transparency available in Pine
Script™
can be applied to:
```
Any element you can plot or draw in an indicatorʼs visual space, be it lines,
fills, text or
candles.
The background of a scriptʼs visual space, whether the script is running in its own
pane,
or in overlay mode on the chart.
The color of bars or the body of candles appearing on a chart.
```
A script can only color the elements it places in its own visual space. The only
exception to
this rule is that a pane indicator can color chart bars or candles.
Pine Script™ has built-in colors such as color.green, as well as functions like
color.rgb() which
allow you to dynamically generate any color in the RGBA color space.
## ## Transparency
```
Its red, green and blue components (0-255), following the RGB color model.
Its transparency (0-100), often referred to as the Alpha channel outside Pine, as
defined
in the RGBA color model. Even though transparency is expressed in the 0-100 range,
its
value can be a “float” when used in functions, which gives you access to the 256
underlying values of the alpha channel.
```
The transparency of a color defines how opaque it is: zero is fully opaque, 100
makes the
color — whichever it is — invisible. Modulating transparency can be crucial in more
involved
color visuals or when using backgrounds, to control which colors dominate the
others, and
how they mix together when superimposed.
## ## Z-index
When you place elements in a scriptʼs visual space, they have relative depth on the
_z_ axis;
some will appear on top of others. The _z-index_ is a value that represents the
position of
elements on the _z_ axis. Elements with the highest z-index appear on top.
Elements drawn in Pine Script™ are divided in groups. Each group has its own
position in the _z_
space, and **within the same group** , elements created last in the scriptʼs logic
will appear on
top of other elements from the same group. An element of one group cannot be placed
outside the region of the _z_ space attributed to its group, so a plot can never
appear on top of
a table, for example, because tables have the highest z-index.
This list contains the groups of visual elements, ordered by increasing z-index, so
background
colors are always at the bottom of _z_ space, and tables will always appear on top
of all other
elements:
```
Background colors
Fills
Plots
Hlines
LineFills
Lines
Boxes
Labels
Tables
```
Note that by using **explicit_plot_zorder = true** in indicator() or strategy(),
you can control
the relative z-index of **plot*()** , hline() and fill() visuals using their
sequential order in the
script.
## ## Constant colors
There are 17 built-in colors in Pine Script™. This table lists their names,
hexadecimal
equivalent, and RGB values as arguments to color.rgb():
```
Name Hex RGB values
color.aqua # 00 BCD 4 color.rgb(0, 188, 212)
```
```
color.black # 363 A 45 color.rgb(54, 58, 69)
```
```
color.blue # 2196 F 3 color.rgb(33, 150, 243)
color.fuchsia #E 040 FB color.rgb(224, 64, 251)
```
```
color.gray # 787 B 86 color.rgb(120, 123, 134)
```
```
color.green # 4 CAF 50 color.rgb(76, 175, 80)
color.lime # 00 E 676 color.rgb(0, 230, 118)
```
```
color.maroon # 880 E 4 F color.rgb(136, 14, 79)
color.navy # 311 B 92 color.rgb(49, 27, 146)
```
```
color.olive # 808000 color.rgb(128, 128, 0)
```
```
color.orange #FF 9800 color.rgb(255, 152, 0)
color.purple # 9 C 27 B 0 color.rgb(156, 39, 176)
```
```
color.red #F 23645 color.rgb(242, 54, 69)
```
```
color.silver #B 2 B 5 BE color.rgb(178, 181, 190)
color.teal # 089981 color.rgb(8, 153, 129)
```
```
color.white #FFFFFF color.rgb(255, 255, 255)
```
```
color.yellow #FDD 835 color.rgb(253, 216, 53)
```
In the following script, all plots use the same color.olive color with a
transparency of 40, but
expressed in different ways. All five methods are functionally equivalent:
The colors in the previous script do not vary as the script executes bar to bar.
Sometimes,
however, colors need to be created as the script executes on each bar because they
depend
on conditions that are unknown at compile time, or when the script begins execution
on bar
zero. For those cases, programmers have two options:
```
. Use conditional statements to select colors from a few pre-determined base
colors.
. Build new colors dynamically, by calculating them as the script executes bar to
bar, to
implement color gradients, for example.
```
## ## Conditional coloring
Letʼs say you want to color a moving average in different colors, depending on some
conditions you define. To do so, you can use a conditional statement that will
select a
different color for each of your states. Letʼs start by coloring a moving average
in a bull color
when itʼs rising, and in a bear color when itʼs not:
Note that:
```
We provide users of our script a selection of colors for our bull/bear colors.
We define an maRising boolean variable which will hold true when the moving
average is higher on the current bar than it was on the last.
We define a c_ma color variable that is assigned one of our two colors, depending
on
the value of the maRising boolean. We use the? : ternary operator to write our
conditional statement.
```
You can also use conditional colors to avoid plotting under certain conditions.
Here, we plot
high and low pivots using a line, but we do not want to plot anything when a new
pivot comes
in, to avoid the joints that would otherwise appear in pivot transitions. To do so,
we test for
pivot changes and use na as the color value when a change is detected, so that no
line is
plotted on that bar:
To undertand how this code works, one must first know that ta.pivothigh() and
ta.pivotlow(),
used as they are here without an argument to the **source** parameter, will return
a value when
they find a high/low pivot, otherwise they return na.
When we test the value returned by the pivot function for na using the nz()
function, we allow
the value returned to be assigned to the **pHi** or **pLo** variables only when it
is not na,
otherwise the previous value of the variable is simply reassigned to it, which has
no impact on
its value. Keep in mind that previous values of **pHi** and **pLo** are preserved
bar to bar
because we use the var keyword when initializing them, which causes the
initialization to only
occur on the first bar.
All thatʼs left to do next is, when we plot our lines, to insert a ternary
conditional statement
that will yield na for the color when the pivot value changes, or the color
selected in the
scriptʼs inputs when the pivot level does not change.
## ## Calculated colors
color.new() is most useful when you need to generate different transparency levels
from a
base color.
color.rgb() is useful when you need to build colors dynamically from red, green,
blue, or
tranparency components. While color.rgb() creates a color, its sister functions
color.r(),
color.g(), color.b() and color.t() can be used to extract the red, green, blue or
transparency
values from a color, which can in turn be used to generate a variant.
## ## color.new()
Letʼs put color.new(color, transp) to use to create different transparencies for
volume columns
using one of two bull/bear base colors:
Note that:
```
In the next to last line of our script, we dynamically calculate the column color
by varying
both the base color used, depending on whether the bar is up or down, and the
transparency level, which is calculated from the cumulative rises or falls of
volume.
We offer the script user control over not only the base bull/bear colors used, but
also on
the number of brightness levels we use. We use this value to determine the maximum
number of rises or falls we will track. Giving users the possiblity to manage this
value
allows them to adapt the indicatorʼs visuals to the timeframe or market they use.
We take care to control the maximum level of transparency we use so that it never
goes
higher than 80. This ensures our colors always retain some visibility.
We also set the minimum value for the number of levels to 1 in the inputs. When the
user
selects 1, the volume columns will be either in bull or bear color of maximum
brightness
— or transparency zero.
```
## ## color.rgb()
In our next example we use color.rgb(red, green, blue, transp) to build colors from
RGBA
values. We use the result in a holiday season gift for our friends, so they can
bring their
TradingView charts to parties:
Note that:
```
We generate values in the zero to 255 range for the red, green and blue channels,
and in
the zero to 100 range for transparency. Also note that because math.random()
returns
float values, the float 0.0-100.0 range provides access to the full 0-255
transparency
values of the underlying alpha channel.
We use the math.random(min, max, seed) function to generate pseudo-random values.
We do not use an argument for the third parameter of the function: seed. Using it
is
handy when you want to ensure the repeatability of the functionʼs results. Called
with
the same seed, it will produce the same sequence of values.
```
## ## color.from_gradient()
Note that:
```
To calculate the gradient, color.from_gradient() requires minimum and maximum
values
against which the argument used for the value parameter will be compared. The fact
that we want a gradient for an unbounded signal like CCI (i.e., without fixed
boundaries
such as RSI, which always oscillates between 0-100), does not entail we cannot use
color.from_gradient(). Here, we solve our conundrum by providing values of -200 and
200 as arguments. They do not represent the real minimum and maximum values for
CCI, but they are at levels from which we do not mind the colors no longer
changing, as
whenever the series is outside the bottom_value and top_value limits, the colors
used
for bottom_color and top_color will apply.
The color progression calculated by color.from_gradient() is linear. If the value
of the
series is halfway between the bottom_value and top_value arguments, the generated
colorʼs RGBA components will also be halfway between those of bottom_color and
top_color.
Many common indicator calculations are available in Pine Script™ as built-in
functions.
Here we use ta.cci() instead of calculating it the long way.
```
The argument used for **value** in color.from_gradient() does not necessarily have
to be the
value of the line we are calculating. Anything we want can be used, as long as
arguments for
**bottom_value** and **top_value** can be supplied. Here, we enhance our CCI
indicator by
coloring the band using the number of bars since the signal has been above/below
the
centerline:
Note that:
```
The signal plot uses the same base colors and gradient as in our previous example.
We
have however increased the width of the line from the default 1 to 2. It is the
most
important component of our visuals; increasing its width is a way to give it more
prominence, and ensure users are not distracted by the band, which has become
busier
than it was in its original, flat beige color.
The fill must remain unobtrusive for two reasons. First, it is of secondary
importance to
the visuals, as it provides complementary information, i.e., the duration for which
the
signal has been in bull/bear territory. Second, since fills have a greater z-index
than
plots, the fill will cover the signal plot. For these reasons, we make the fillʼs
base colors
fairly transparent, at 70, so they do not mask the plots. The gradient used for the
band
starts with no color at all (see the na used as the argument to bottom_color in the
color.from_gradient() call), and goes to the base bull/bear colors from the inputs,
which
the conditional, c_endColor color variable contains