# https://www.tradingview.com/pine-script-docs/welcome User Manual / Welcome to Pine Script® v6 Welcome to Pine Script® v6 Pine Script® is TradingView’s programming language. It allows traders to create their own trading tools and run them on our servers. We designed Pine Script as a lightweight, yet powerful, language for developing indicators and strategies that you can then backtest. Most of TradingView’s built-in indicators are written in Pine Script, and our thriving community of Pine Script programmers has published more than 150,000 Community Scripts, half of which are open-source. 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 --- # https://www.tradingview.com/pine-script-docs/primer/first-steps User Manual / Pine Script® primer / First steps First steps Introduction Welcome to the Pine Script® v6 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. On 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 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. If you are new to our platform, then please read on! Using scripts If you are interested in using technical indicators or strategies on TradingView, you can first start exploring the thousands of indicators already available on our platform. You can access existing indicators on the platform in two different ways: By using the chart’s “Indicators, metrics, and strategies” button. By browsing TradingView’s Community scripts, the largest repository of trading scripts in the world, with more than 150,000 scripts, half 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. Loading scripts from the chart To explore and load scripts from your chart, click the “Indicators, metrics, and strategies” button, or use the forward slash / keyboard shortcut: The dialog box that appears 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 the script name when you hover over it. “Personal” displays the scripts you have written and saved in the Pine Editor. They are saved on TradingView’s servers. “Technicals” groups most TradingView built-in scripts, 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 more than 150,000 published scripts written by TradingView users. The scripts can be sorted by one of the three different filters — “Editors’ picks” only shows open-source scripts hand-picked by our script moderators, “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, we selected the “Technicals” tab to see the TradingView built-in indicators: Clicking on one of the listed indicators or strategies loads the script on your chart. Strategy scripts are distinguished from indicators by a special symbol that appears to the right of the script name. Browsing community scripts To access the Community scripts feed from TradingView’s homepage, select “Indicators and strategies” from the “Community” menu: You can also search for scripts using the homepage’s “Search” field, and filter scripts using different criteria. See this Help Center page explaining the different types of scripts that are available. The scripts feed generates script widgets, which show the title and author of each publication with a preview of the published chart and description. Clicking on a widget opens the script page, which shows the publication’s complete description, an enlarged chart, and any additional release notes. Users can boost, favorite, share, and comment on publications. If it is an open-source script, the source code is also available on the script page. When you find an interesting script in the Community scripts, follow the instructions in the Help Center to load it on your chart. Changing script settings 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 reputations and open-source publications Reading code from Community scripts is easy; if there is no grey or red “lock” icon in the upper-right corner of the script widget, then the script is open-source. By opening the script page, you can read its full source code. To see the code of a TradingView built-in indicator, 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 opens below the chart and displays the script’s code. If you want to edit the script, you must first select the “Create a working copy” button. You will then be able to modify and save the code. Because the working copy is a different version of the script, you need to use the Editor’s “Add to chart” button to add that new copy to the chart. For example, this image shows the Pine Editor, where we selected to view the source code from the “Bollinger Bands” indicator on our chart. Initially, the script is read-only, as indicated by the orange warning text: You can also open editable versions of the TradingView built-in scripts from the Pine Editor by using the “Create new” > “Built-in…” menu selection: Writing scripts We have built Pine Script to empower both new and experienced traders to create their own trading tools. Although learning a first programming language, like trading, is rarely very easy for anyone, we have designed Pine Script so it is relatively easy to learn for first-time programmers, yet powerful enough for knowledgeable programmers to build tools of moderate complexity. Pine Script allows you to write three types of scripts: 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 often-used functions that can be reused by other scripts. The next step we recommend is to write your first indicator. Next First indicator On this page Introduction Using scripts Loading scripts from the chart Browsing community scripts Changing script settings Reading scripts Writing scripts --- # https://www.tradingview.com/pine-script-docs/primer/first-indicator User Manual / Pine Script® primer / First indicator First indicator The Pine Editor 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 the Pine 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 select Ctrl or Cmd and a built-in Pine Script construct, and opens the library publication page when doing the same with code imported from libraries. It provides an auto-complete feature that you can activate by selecting Ctrl+Space or Cmd+I, depending on your operating system. It makes the write/compile/run cycle more efficient because saving a new version of a script already loaded on the chart automatically compiles and executes it. To open the Pine Editor, select the “Pine Editor” tab at the bottom of the TradingView chart. First version Let’s create our first working Pine script, an implementation of the MACD indicator: Open the Pine Editor’s dropdown menu (the arrow at the top-left corner of the Pine Editor pane, beside the script name) and select “Create new/Indicator”. Copy the example script code below by clicking the button on the top-right of the code widget. Select all the code already in the editor and replace it with the example code. Save the script by selecting the script name or using the keyboard shortcut Ctrl+S. Choose a name for the script (e.g., “MACD #1”). The script is saved in TradingView’s cloud servers, and is local to your account, meaning only you can see and use this version. Select “Add to chart” in the Pine Editor’s menu bar. The MACD indicator appears in a separate pane under the chart. Pine Script® Copied //@version=6 indicator("MACD #1") fast = 12 slow = 26 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) Our first Pine script is now running on the chart, which should look like this: Let’s look at our script’s code, line by line: Line 1: //@version=6 This is a compiler annotation telling the compiler the script uses version 6 of Pine Script. Line 2: indicator("MACD #1") Declares this script as an indicator, and defines the title of the script that appears on the chart as “MACD #1”. Line 3: fast = 12 Defines an integer variable fast as the length of the fast moving average. Line 4: slow = 26 Defines an integer variable slow as the length of the slow moving average. Line 5: fastMA = ta.ema(close, fast) Defines the variable fastMA, which holds the result of the EMA (Exponential Moving Average) calculated on the close series, i.e., the closing price of bars, with a length equal to fast (12). Line 6: slowMA = ta.ema(close, slow) Defines the variable slowMA, which holds the result of the EMA calculated on the close series with a length equal to slow (26). 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 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 the MACD using multiple steps, but because Pine Script is specially designed to write indicators and strategies, built-in functions exist for many common indicators, including one for MACD: ta.macd(). Therefore, we can write a second version of our script that takes advantage of Pine’s available built-in functions: Pine Script® Copied //@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) Note that: We add inputs so we can change the lengths of the moving averages from the script’s settings. We now use the ta.macd() built-in function to calculate our MACD directly, which replaces three lines of calculations and makes our code easier to read. Let’s repeat the same process as before to create our new indicator: Open the Pine Editor’s dropdown menu (the arrow at the top-left corner of the Pine Editor pane, beside the script name) and select “Create new/Indicator”. Copy the example script code above. The button on the top-right of the code widget allows you to copy it with a single click. Select all the code already in the editor and replace it with the example code. Save the script by selecting the script name or using the keyboard shortcut Ctrl+S. Choose a name for your script that is different from the previous one (e.g., “MACD #2”). Select “Add to chart” in the Pine Editor’s menu bar. The “MACD #2” indicator appears in a separate pane under the “MACD #1” indicator. Our second Pine script is now running on the chart. If we double-click on the indicator’s name on the chart, it displays the script’s “Settings/Inputs” tab, where we can now change the fast and slow lengths used in the MACD calculation: Let’s look at the lines that have changed in the second version of our script: Line 2: indicator("MACD #2") 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 the variable, we used the input() function so we can change the length value from the script’s “Settings/Inputs” tab. The default value is 12 and the input field’s label is "Fast length". When we change the value in the “Inputs” tab, the fastInput variable updates to contain the new length and the script re-executes 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") As with fastInput in the previous line, we do the same for the slow length, taking care to use a different variable name, default value, and title string for the input field’s label. Line 5: [macdLine, signalLine, histLine] = ta.macd(close, fastInput, slowInput, 9) This is where we call the ta.macd() built-in function to perform all the first version’s calculations in only one line. The function requires four parameters (the values enclosed in parentheses after the function name). It returns three values, unlike the other functions we’ve used so far that only returned one. For this reason, we need to enclose the list of three variables receiving the function’s result in square brackets (to form a tuple) 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). Lines 6 and 7: plot(macdLine, color = color.blue) and plot(signalLine, color = color.orange) The variable names we are plotting here have changed, but the lines still behave the same as in our first version. Our second version of the script performs the same calculations as our first, but we’ve made the indicator more efficient as it now leverages Pine’s built-in capabilities and easily supports variable lengths for the MACD calculation. Therefore, we have successfully improved our Pine script. Next We now recommend you go to the Next Steps page. Previous First steps Next Next steps On this page The Pine Editor First version Second version Next --- # https://www.tradingview.com/pine-script-docs/primer/next-steps User Manual / Pine Script® primer / Next steps 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. Pine Script indicators also contain calculations, but cannot be used in backtesting. Because they do not require the broker emulator, they use less resources and will run faster. It is thus advantageous to use indicators whenever you can. 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. How scripts are executed 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. Contrary to indicators, strategies normally execute only once on realtime bars, when they close. They can also be configured to execute on each price/volume update if that is what you need. See the page on Strategies for more information, and to understand how strategies calculate differently than indicators. 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 TradingView is home to a large community of Pine Script programmers and millions of traders from all around the world. Once you become proficient enough in Pine Script, you can choose to share your scripts with other traders. Before doing so, please take the time to learn Pine Script well-enough to supply traders with an original and reliable tool. All publicly published scripts are analyzed by our team of moderators and must comply with our Script Publishing Rules, which require them to be original and well-documented. If you 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. Getting around the Pine Script documentation 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 v6 User Manual Our Pine Script v6 Reference Manual The Pine Script v6 User Manual, which is located on its separate page and in English only. The Pine Script v6 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. Access the popup version from the Pine Editor by either selecting Ctrl or Cmd and selecting a keyword, or by using the Editor’s “More/Reference Manual…” menu. The Reference Manual is translated into multiple languages. There are six different versions of Pine Script released. Ensure the documentation you use corresponds to the Pine Script version you are coding with. Where to go from here? This Pine Script v6 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 v6 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 On this page Overview ”indicators” vs “strategies” How scripts are executed Time series Publishing scripts Getting around the Pine Script documentation Where to go from here? --- # https://www.tradingview.com/pine-script-docs/language/execution-model User Manual / Language / Execution model Execution model Introduction Pine Script® relies on an event-driven, sequential execution model to control how a script’s compiled source code runs in charts, alerts, Deep Backtesting mode, and the Pine Screener. In contrast to the traditional execution model of most programming languages, Pine’s runtime system executes a script repeatedly on the sequence of historical bars and realtime ticks in the dataset on which it runs, performing separate calculations for each bar as it progresses. After each execution on a closed bar, the necessary data from that execution becomes part of an internal time series, and the script can use that data in its calculations on subsequent bars. This combination of sequential executions and storage enables programmers to use minimal code to write scripts with dynamic calculations that advance across a dataset bar by bar. The execution model and time series structure closely connect to the type system — together, they define how a script behaves as it runs on a dataset. Although it’s possible to write simple scripts without understanding these foundational topics, learning about them and their nuances is key to becoming proficient in Pine Script. This page explains the execution model in two parts: The basics and The details. The first part provides quick, actionable information about the model for beginners. The second part offers an advanced, in-depth breakdown of the model’s workings and unique behaviors. To make the most of the information on this page, we recommend that newcomers to Pine Script start with The basics, learn about other topics in this manual, and then come back to this page for the advanced details. The basics The following sections outline core principles of the execution model for beginners. If you are new to Pine Script, start here. Bar-by-bar execution The dataset for a symbol on a given timeframe, as shown on a chart, consists of a sequence of bars representing a time series. Each bar in the sequence represents the price and volume for a specific time period. The first (leftmost) bar on a chart corresponds to the earliest period, and the last (rightmost) bar corresponds to the most recent period. Much of the power of Pine Script stems from its ability to process this time series data efficiently. When a user runs a script, its code does not execute just once; it executes from start to end on each bar in the symbol’s dataset individually, progressing from the first available bar to the most recent bar. Each separate script execution performs calculations or generates outputs (e.g., plots) for a specific bar using the data available on that bar. A script can retrieve price, volume, and other essential data for each bar on which it executes by using the built-in variables that hold bar information, such as open, high, low, close, and volume. These variables automatically update before each new execution to store the values for the current bar. For example, the simple script below uses the plot() function to display the series of close values (i.e., the closing price of each bar) on the chart: Pine Script® Copied //@version=6 indicator("Bar-by-bar execution demo", overlay = true, behind_chart = false) // Plot the `close` series on the chart. // This call defines the plotted point for the current bar on each execution. plot(close, "Close price", chart.fg_color, 5) When a user first adds this script to their chart, its code executes once for every bar in the available dataset. As the script runs on the data, two primary steps occur on each bar: The close variable automatically updates to hold the current bar’s latest price. The plot() function call plots the updated close value at the current bar’s position. When the script finishes its run from the first bar to the most recent bar, the result is a simple line plot showing the progression of closing prices across the chart’s history: Note that the above script evaluates the plot() function call once for every bar on the chart, not just once in total. On each separate execution, the call defines the plotted point for the current bar: the chart’s first bar during the first execution, the second bar during the next, and so on. This pattern illustrates a key principle of Pine’s execution model: on each successive execution, a script re-evaluates function calls and other expressions within its required scopes to perform separate calculations for the current bar. Note The scope of an expression is the part of the code where the script can access it. A script evaluates its global scope once per execution, i.e., on every bar. In contrast, it evaluates local scopes — such as the code inside conditional structures, user-defined functions, and loops — zero, one, or several times per execution, depending on the logic. Repeated code evaluation also applies to variable declarations. By default, a script does not declare a variable only once throughout its runtime; the script re-declares that variable and assigns an initial value based on the current bar’s data during each new evaluation of its scope. Let’s look at a simple example. The following script declares an x variable of the “int” type with an initial value of 0. Then, it increases the variable’s value by 10 with the addition assignment operator (+=). The script calls plot() to display the value of x on each bar in a separate pane: Pine Script® Copied //@version=6 indicator("Repeated declarations demo") //@variable A user-defined variable. The script declares this variable and initializes it to 0 on *every* execution. int x = 0 // Increase the value of `x` by 10 on every bar. x += 10 // Plot the value of `x`. // Because `x` begins at 0 on every execution, and the script adds 10 to that value, the plotted value is always 10. plot(x, "`x` value", color.blue, 3) As shown above, the script plots a value of 10 on every bar, because the x variable does not carry over from bar to bar; the script declares the variable repeatedly. On each bar, the script re-declares x with an initial value of 0, then adds 10 to that value, resulting in a final value of 10 for every plotted point. Programmers can change the behavior of a variable, enabling it to persist and preserve updates to its value across bars, by including the var keyword in its declaration, as described in the Declaration modes section of the Variable declarations page. Below, we modify the previous script by adding var to the x declaration. Now, the script declares and initializes x only once — on the first bar — and that variable persists across all bars that follow. The script now plots a line that increases by 10 on each bar, because x preserves the result from each addition across the chart’s history. The value changes from 0 to 10 on the first bar, then to 20 on the second, and so on: Pine Script® Copied //@version=6 indicator("Persistent declarations demo") //@variable A *persistent* variable. The script initializes this variable only on the *first execution*.  //          The variable preserves all changes to its value on each closed bar.  var int x = 0 // Increase the value of `x` by 10 on every bar. x += 10 // Plot the `x` series on the chart.  // Because the script declares `x` using `var` and then increments its value, the value never resets to 0. // The plotted value is 10 on the first bar, 20 on the next, and so on.  plot(x, "`x` series", color.blue, 3) Storing and using data from previous bars As a script runs on a dataset, the states of its variables, function calls, and other expressions are automatically committed (saved) to an internal time series on each bar, creating historical trails of previous bar values that the script can access during its calculations on the current bar. The script can use these previous values by doing either of the following: Using the [] history-referencing operator. The number in the square brackets represents how many bars back from the current bar the script looks to retrieve a past value. For instance, close[1] retrieves the close value from one bar before the current bar, and close[100] retrieves the value from 100 bars back. Calling the built-in functions that calculate on past values internally, such as ta.*() functions. For instance, ta.change(close, 10) calculates the difference between the current value of close and its value from 10 bars back. The example below uses both of the above techniques to perform calculations based on data from previous bars. The script calculates a series of bar-by-bar price returns and plots the result as color-coded columns. It declares two global variables on each bar: priceReturn for the calculated returns, and returnColor for the plot’s color. The priceReturn value is the result of dividing the current one-bar change in closing prices (ta.change(close, 1)) by the previous bar’s closing price (close[1]). The returnColor value is color.teal if the current value of priceReturn is higher than the value from the previous bar (priceReturn[1]), and color.maroon otherwise: Pine Script® Copied //@version=6 indicator("Storing and using data from previous bars demo") //@variable The one-bar price return, based on the current and *previous* bars' `close` values. //          This variable's final value on each bar automatically becomes part of the internal time series.  float priceReturn = ta.change(close, 1) / close[1] //@variable Is `color.teal` if the `priceReturn` value is above the value on the previous bar; `color.maroon` otherwise.  color returnColor = priceReturn > priceReturn[1] ? color.teal : color.maroon // Plot the current `priceReturn` value as a column, colored using the value of `returnColor`.  plot(priceReturn, "Price return", returnColor, 1, plot.style_columns) Note that: This script does not plot a column on bar 0 (the first bar). The priceReturn value is na on that bar, because there is no previous bar available for the script to reference at that point. Notice For consistency, use historical references only on variables or expressions that the script evaluates on every bar, in the global scope. A script that references the history of variables or expressions defined in local scopes — such as the code inside an if statement — can cause unintended results. The compiler warns users about this behavior directly inside the Pine Editor. For advanced details on this behavior, see the Time series in scopes section. Realtime bars When a script first runs on a chart, all closed bars in the accessed dataset are historical bars. These bars represent data for elapsed time periods where the final price and volume are confirmed. All indicators execute once per historical bar. When the rightmost bar on the chart is open, it is a realtime bar. Unlike a historical bar, whose values are final, a realtime bar updates its values as new price or volume data becomes available. After the bar closes, it becomes an elapsed realtime bar, which is then no longer subject to change as the script runs. Because the final values for a realtime bar are unknown until the bar closes, an indicator executes differently on that bar than it does on historical bars. The script executes not once, but repeatedly on the realtime bar — once for each new update (tick) — to recalculate its results using the latest data. Note On the open realtime bar, variables such as high, low, close, and volume hold the latest available values for the bar. These values do not represent confirmed data for the bar until the script executes on that bar’s closing tick. Scripts can identify historical and realtime bars, and whether a realtime bar’s data is confirmed, by using the barstate.ishistory, barstate.isrealtime, and barstate.isconfirmed variables. See the Bar states page to learn more. Before each recalculation on the realtime bar, the data for a script’s variables, expressions, and outputs on that bar is cleared, or reset. We refer to this process as rollback. The purpose of rollback is to revert the script to the same confirmed state it had when the realtime bar opened. This process ensures that the script’s calculations for the bar operate only on the latest available data, without relying on temporary data from the bar’s previous ticks. Notice A variable can escape rollback and persist across all ticks within a bar if its declaration includes the varip keyword. This behavior is helpful for calculations that require data from before a bar’s closing tick. However, it can also cause repainting, because the ticks that occur before a realtime bar’s close become unavailable after the script reloads. Refer to the Declaration modes section of the Variable declarations page to learn more about this keyword. Let’s look at rollback and recalculation in action. The following script uses ta.stoch() to calculate the Stochastic oscillator based on the close, high, and low values over a specified number of bars, then plots the result in a separate pane. It also calls bgcolor() to highlight the background on each realtime bar — where barstate.isrealtime is true — for visual reference: Pine Script® Copied //@version=6 indicator("Recalculation on realtime bars demo") //@variable The number of bars in the Stochastic calculation. Users can change this value in the "Settings/Inputs" tab. int lengthInput = input.int(10, "Length", 1) //@variable The Stochastic oscillator, based on the `close`, `high`, and `low` values over `lengthInput` bars. float stochastic = ta.stoch(close, high, low, lengthInput) // Plot the `stochastic` value for each bar. plot(stochastic, "Stochastic %K", color.teal, 3) // Highlight the background of each realtime bar.   bgcolor(barstate.isrealtime ? color.new(color.purple, 80) : na, title = "Realtime background highlight") When we add the script to our chart, it executes once per bar in the chart’s history, from the leftmost bar to the rightmost bar. However, the rightmost bar on our chart is still open. Therefore, it is a realtime bar, not a historical bar. After the script reaches that bar, it begins executing once for every new update to the bar’s data. Each new script execution calculates on the latest available prices and replaces the bar’s previous result. For instance, in the initial image below, the oscillator’s value 10 seconds into the open realtime bar (the one with the purple background) is 32.08: Every time the bar updates, rollback resets the script’s data for that bar, and the script recalculates its result using the latest high, low, and close values. Here, halfway through the realtime bar’s period, the oscillator’s plot now shows a value of 16.71: Recalculation continues for each successive update to the bar. Then, the script reaches the bar’s closing tick, where the prices become confirmed. On that tick, the script calculates the oscillator’s final value of 19.35. Afterward, another realtime bar opens, and the pattern of rollback and recalculation continues on that bar: Note that: Only the values for a realtime bar’s final tick become part of the internal time series. The values from ticks before the bar’s close are not saved. The input.int() function returns a value of the “input int” qualified type. Values qualified as “input” are established before the first script execution, and they remain consistent throughout the script’s runtime. If the user changes the “Length” input to a new value, the script restarts to perform new calculations across the dataset using that value. See the Inputs page and the Qualifiers section of the Type system page to learn more about script inputs and the “input” qualifier. If the script restarts, all the realtime bars from the previous script run become historical bars in the new run. Therefore, after restarting, the script executes only once on each of those bars and does not highlight their background. Note Strategy scripts do not execute in the same way as indicators by default; they execute only once on every bar, including all realtime bars. Calculations occur on each realtime bar only after the bar closes. Users can change this behavior with the calc_on_every_tick and calc_on_order_fills parameters of the strategy() function. See the Strategies page to learn more about strategy scripts and how they differ from indicators. The details The following sections provide in-depth details about Pine’s execution model, including the mechanics of executions on historical bars and realtime bars, which events trigger script executions, and how the runtime system maintains data across executions in a time series format. Tip New to Pine Script? To make the most of the advanced information below, start by learning The basics and understanding other core concepts — such as the type system, variable declarations, operators, conditional structures, and user-defined functions — before venturing further. Executions on historical bars When a script loads on the chart or in another location after an execution-triggering event, its compiled source code executes on every accessible bar in the current dataset in order, starting with the first bar. Note The first bar that a script accesses depends on the calc_bars_count parameter of its declaration statement and the limits of the user’s plan. If the calc_bars_count argument is a non-zero value that is less than or equal to the plan’s bar limit, script executions begin at the specified number of bars before the latest bar. Otherwise, executions start at the earliest available bar. While the script loads, the runtime system performs the following steps for each bar that it accesses: It updates the built-in variables that hold bar information. For instance, the system sets the open, high, low, and close variables to hold the OHLC price values of the bar before each execution. It executes the script’s compiled code from start to end using the data available as of the current bar. After the execution ends, the system commits (saves) all necessary data for the current bar to the time series. The script can then access that data from historical buffers during its executions on subsequent bars by using the history-referencing operator or the built-in functions that reference past bars internally. These steps repeat for every successive bar up to the most recent bar. After the runtime system completes this process across the dataset, the script’s committed outputs — such as plots, drawings, Pine Logs, and Strategy Tester results — become available to the user. All the closed bars on which the script executes while loading are historical, because they represent data points that were confirmed before the event that triggered the loading process. By default, all scripts execute once for each historical bar. Tip Scripts can identify which bars have a historical state with the barstate.ishistory variable. Its value is true for every closed bar accessed during the script’s loading time and false for all bars that close afterward. See the Bar states page to learn more about barstate.* variables. Let’s examine a simple indicator to understand how script executions work on historical bars. The script below calculates the 20-bar moving average of close values and plots the result on the chart. The color of the plot depends on whether the average is above or below the value on the previous bar. The script also increments an executionNum variable to count code executions, then plots the result alongside bar_index for comparison. Additionally, it highlights the background of historical bars in orange for visual reference: Pine Script® Copied //@version=6 indicator("Executions on historical bars demo") //@variable The average of the latest 20 `close` values. float sma = ta.sma(close, 20) //@variable Is `color.green` if the `sma` value is above the value on the previous bar; `color.red` otherwise. color plotColor = sma > sma[1] ? color.green : color.red //@variable Tracks the current execution number, where 0 represents the first execution. varip int executionNum = -1 // Add 1 to the `executionNum` value. executionNum += 1 // Display the `sma` as a line plot on the main chart pane, colored by the `plotColor`. plot(sma, "SMA", plotColor, 3, force_overlay = true) // Display the `executionNum` and `bar_index` series in a separate pane. plot(executionNum, "Execution number", color.purple, 5) plot(bar_index,    "Bar index",        color.aqua,   2) // Highlight the chart's background in translucent orange when `barstate.ishistory` is `true`. bgcolor(barstate.ishistory ? color.new(color.orange, 70) : na, title = "Historical highlight", force_overlay = true) The statements and expressions in this source code might appear static at first glance. However, they have dynamic behavior across bars because the system executes the script repeatedly — once for each successive data point. Below, we inspect the code step by step to explain how the script works during its historical executions. The indicator() call at the top of the code is a declaration statement that defines the script’s type and properties once, at compile time. This statement does not execute as the script runs on the dataset: Pine Script® Copied indicator("Executions on historical bars demo") Before each script execution on a bar, the runtime system updates the built-in bar_index and close variables required in the calculations. The bar_index value is the bar’s global time series index, where 0 represents the first bar, 1 represents the second, and so on. The close variable holds the bar’s latest price. For historical bars, its value is the final price at the bar’s closing time. Each time that the script executes, it declares and initializes a global sma variable of the “float” type. This variable declaration happens on every execution because the code line does not specify a declaration mode. The variable’s assigned value is the result of a ta.sma() function call. The call returns the average of the latest 20 close values as of the current bar, or na if fewer than 20 bars are available. After the execution ends, the system commits the new value of sma to the time series: Pine Script® Copied //@variable The average of the latest 20 `close` values. float sma = ta.sma(close, 20) Note that: The //@variable comment above the sma declaration is an annotation that documents the variable in the code. The Pine Editor displays the comment in a pop-up window when the user hovers the mouse pointer over the variable. During each execution, the script also initializes a plotColor variable of the “color” type. The script uses a ternary operation that compares the current sma value to sma[1] — the last committed value for sma as of the previous bar — to determine the plotColor variable’s assigned value. If the current sma value is higher than the last committed value, the plotColor value is color.green. Otherwise, it is color.red: Pine Script® Copied //@variable Is `color.green` if the `sma` value is above the value on the previous bar; `color.red` otherwise. color plotColor = sma > sma[1] ? color.green : color.red In contrast to the variables above, the script does not initialize the executionNum variable on every execution. Instead, initialization happens only once — on the first bar — because the variable declaration is in the global scope and uses the varip keyword. Once initialized, the variable persists across all subsequent bars and the ticks within those bars. Only the reassignment or compound assignment operators can change its value: Pine Script® Copied //@variable Tracks the current execution number, where 0 represents the first execution. varip int executionNum = -1 The code following the executionNum declaration uses the addition assignment operator (+=) to increase the variable’s value by one on each new execution. Starting from -1, the value increases to 0 on the first execution after initialization, then 1 on the second, and so on: Pine Script® Copied // Add 1 to the `executionNum` value. executionNum += 1 The script evaluates the plot() and bgcolor() calls on every execution. Each plot() call creates a new point on a line plot at the bar’s location on the time axis. The bgcolor() call creates a background color for the bar based on a ternary expression. The background is translucent orange if barstate.ishistory is true. Otherwise, it is na (no color): Pine Script® Copied // Display the `sma` as a line plot on the main chart pane, colored by the `plotColor`. plot(sma, "SMA", plotColor, 3, force_overlay = true) // Display the `executionNum` and `bar_index` series in a separate pane. plot(executionNum, "Execution number", color.purple, 5) plot(bar_index,    "Bar index",        color.aqua,   2) // Highlight the chart's background in translucent orange when `barstate.ishistory` is `true`. bgcolor(barstate.ishistory ? color.new(color.orange, 70) : na, title = "Historical highlight", force_overlay = true) Note that: The plot() and bgcolor() calls that include force_overlay = true display their visuals on the main chart pane. The other plot() calls output visuals in a separate pane, because the indicator() call does not include overlay = true. After the system executes the script on all available data points and finishes loading, the script’s outputs then become visible on the chart: Note that: When the script first loads, all bars, including the latest one, have an orange background because they initially represent historical data. However, the latest bar on our chart is still open, meaning it is a realtime bar. After a new tick arrives from the realtime data feed, the bar’s values update, and the script executes again on that bar. The orange background for the bar then disappears because the system sets the value of barstate.ishistory to false. The executionNum and bar_index values are identical on historical bars because the script executes once per bar on that part of the dataset. However, they begin to differ on the realtime bar. On that bar, the script executes after every new update to recalculate its results, and the executionNum value increases each time. See the Executions on realtime bars section to learn more. An alternative, more robust method to track code executions is to use the Pine Profiler. The profiler analyzes the total runtime and execution count of every significant part of the source code. To learn more about this feature, see the Profiling and optimization page. It’s important to note that, unlike indicators, strategies can execute more than once per historical bar, depending on the specified calculation behavior. If the strategy() declaration statement includes calc_on_order_fills = true, or if the user selects the “After order is filled” checkbox in the “Settings/Properties” tab, the runtime system executes the script on each available tick where the broker emulator fills an order, or once per bar when there is no order to fill. Let’s look at a simple example. The following strategy changes the direction of its simulated position on each execution. If there is an open short position or no position, the strategy places a market order to close all short trades and enter a long trade. If a long position is open, the strategy places a market order to close it and open a short trade. As with the previous example, this script increments an executionNum variable declared with varip to count new executions, plots the result alongside bar_index for comparison, and highlights the background of historical bars in orange with bgcolor(): Pine Script® Copied //@version=6 strategy("Default strategy behavior on historical bars demo") // Place a market order to close short trades and enter a long trade when there is a short position or no position. // Otherwise, if a long position is open, place a market order to close the long trades and enter a short trade. if strategy.position_size <= 0     strategy.entry("Long", strategy.long) else     strategy.entry("Short", strategy.short) //@variable Tracks the current execution number, where 0 represents the first execution. varip int executionNum = -1 // Add 1 to the `executionNum` value. executionNum += 1 // Display the `executionNum` and `bar_index` series in a separate pane. plot(executionNum, "Execution number", color.purple, 5) plot(bar_index,    "Bar index",        color.aqua,   2) // Highlight the chart's background in translucent orange when `barstate.ishistory` is `true`. bgcolor(barstate.ishistory ? color.new(color.orange, 70) : na, title = "Historical highlight", force_overlay = true) Note that: The strategy.entry() command creates entry orders. By default, a long entry using this command reverses an open short position, and a short entry reverses an open long position. See the Reversing positions section of the Strategies page to learn more. The script above uses the default calculation behavior: it places a new order only at the close of each bar. The broker emulator fills the order at the next bar’s opening price, as the trade markers on the chart above indicate. The executionNum and bar_index plots show the same values because the script executes only once per bar. If we include calc_on_order_fills = true in the strategy() declaration statement, the runtime system re-executes the script on a bar after each new order fill to update the calculations. Our script’s logic generates a new order on every execution, and the broker emulator considers historical bars to have four ticks for filling orders by default (the open, high, low, and close). Therefore, with this change, the script executes four times per historical bar instead of only once. As shown below, the strategy now shows four trade markers on each historical bar, and the executionNum value is four times that of the bar_index variable: Pine Script® Copied //@version=6 strategy("Calculation after order fill on historical bars demo", calc_on_order_fills = true) // Place a market order to close short trades and enter a long trade when there is a short position or no position. // Otherwise, if a long position is open, place a market order to close the long trades and enter a short trade. if strategy.position_size <= 0     strategy.entry("Long", strategy.long) else     strategy.entry("Short", strategy.short) //@variable Tracks the current execution number, where 0 represents the first execution. varip int executionNum = -1 // Add 1 to the `executionNum` value. executionNum += 1 // Display the `executionNum` and `bar_index` series in a separate pane. plot(executionNum, "Execution number", color.purple, 5) plot(bar_index,    "Bar index",        color.aqua,   2) // Highlight the chart's background in translucent orange when `barstate.ishistory` is `true`. bgcolor(barstate.ishistory ? color.new(color.orange, 70) : na, title = "Historical highlight", force_overlay = true) Note that: This script can execute more than four times per bar if it uses Bar Magnifier mode, because this mode enables the broker emulator to fill orders on historical bars using intrabar prices from a lower timeframe. The script can execute numerous times on a realtime bar, depending on the updates from the data feed, because each new update to the bar is a valid tick for filling the strategy’s orders. An alternative way to confirm the script’s increased execution count is to select and clear the “After order is filled” checkbox in the “Settings/Properties” tab while profiling the code. Executions on realtime bars After a script running on the chart or in an alert executes across all historical bars in a dataset, the runtime system continues to execute the script on the current bar, if it is open, and on any new bars that form later. We refer to these bars as realtime bars, because they represent incoming data from a separate data feed that the script can access only after it finishes loading. As explained in the previous section, historical bars represent confirmed data points. By contrast, a realtime bar represents an initially unconfirmed data point that evolves as new updates (ticks) arrive from the realtime data feed. With each new tick, the bar’s high, low, close, volume, and other values update to represent the latest data while the bar remains open. After the bar closes, it becomes an elapsed realtime bar, whose values no longer change. Then, a new realtime bar opens after another tick arrives, and that bar updates as new data becomes available. Tip Scripts can identify which bars have a realtime state with the barstate.isrealtime variable. Its value is true for every bar that closes after the script’s loading time and false for all previous bars. Additionally, scripts can detect closed bars with the barstate.isconfirmed variable. To learn more about these and other barstate.* variables, refer to the Bar states page. As an indicator or library script runs on an open realtime bar, its compiled code executes once after every new update from the data feed. With each new execution, the script recalculates its results for that bar using the latest data. Consequently, the states of the script’s variables, expressions, and objects can change with each new execution while the bar remains open. The system commits the script’s data for the realtime bar only after the bar closes. After each script execution that occurs before a bar’s closing tick, the runtime engine executes a rollback process. Rollback resets applicable script data to the latest committed states in the time series. This process enables the script to recalculate the bar’s results using only the latest available data — without the influence of temporary data from executions on the bar’s previous ticks. Below, we explain how recalculation and rollback affect a script’s data and outputs, along with some notable exceptions to this process: Reinitialize variables The runtime system erases the states of any variables that the script initializes during its executions before a bar’s close, excluding those declared using the varip keyword. When the script executes again after rollback, it reinitializes the variables with new values or references based on the latest available data. Likewise, the system does not preserve the temporary states of built-in variables that hold values for the current bar. Before the new script execution, it sets the variables to use the bar’s most recent data. For instance, the system updates close, high, and low with the latest, highest, and lowest prices reported since the bar’s opening time. Reset changes to var variables Variables that use the var keyword in their declaration are initialized only once — during the first execution of their scopes on a closed bar. Variables that use the var keyword in their declaration remain initialized after the first time that their scopes execute on a bar’s closing tick. Their assigned values or references persist across subsequent bars, changing only after reassignment or compound assignment operations. Although these variables preserve data across successive bars, they do not preserve data across executions on the ticks of an open bar. Rollback reverts all variables declared with var before the current bar to the last committed states in the time series as of the previous bar. For instance, if a variable declared with var has a value of 20 on the open bar and 19 on the previous bar, the variable’s value reverts to 19 before the script executes on the next tick of the same bar. The temporary value of 20 does not persist. Replace plotted outputs The plot*(), bgcolor(), barcolor(), and fill() functions create visual outputs on every bar. These outputs are temporary on the open realtime bar. When the script executes again after rollback, the new outputs for the bar from calls to these functions replace the ones from the previous tick. For example, when the expression plot(close) executes on the open bar, it displays the bar’s latest close value as of the current execution. However, the plotted result is temporary until the bar closes. After rollback, the close variable updates, then the script calls plot() again on the next execution to replace the output from the previous tick and display the new value. Remove and revert objects User-defined types (UDTs) and special types such as collections and drawing types are reference types. They define structures from which scripts create objects — independent entities that store data elsewhere in memory. Variables of these types hold references that provide access to specific objects and their data; the variables do not store objects directly. If a script creates objects on an open bar and does not assign their references to variables declared with the varip keyword, the rollback process removes those objects. During the next execution on the open bar, the script creates new objects if the updated logic allows it. For example, if a script calls label.new() to create a label object on the open bar, the system deletes that object during rollback. On the next execution, the script evaluates label.new() again, creating a new label that replaces the output. The label created on the previous tick no longer exists. Similarly, for objects of built-in or user-defined types with references assigned to var variables, the rollback process reverts any changes to those objects that occur on the open bar. The only exception is for UDTs with fields that include the varip keyword. See the Objects page for more information. Exceptions The runtime system does not revert all the data from script executions on an open bar. The following are notable exceptions to the rollback process: Variables or fields declared with the varip keyword do not revert to a previously committed state. They persist across all script executions after initialization, even those on the ticks of an open realtime bar. Logged messages in the Pine Logs pane do not disappear after rollback. The messages from any log.*() calls during executions on the ticks of realtime bars remain in the pane until the script reloads. The data from strategy orders placed or filled on the ticks within a bar is not subject to rollback. If a strategy script creates orders or the broker emulator fills orders on an open bar, the data from those events persists. Rollback does not erase logs for alerts from the “Alerts” menu. All messages from a script alert remain visible until the user restarts the alert. Runtime errors from the system or the runtime.error() function completely stop script executions. If an error occurs at any point while a script executes on an open bar, the system halts the script and does not revert the error after new updates from the data feed. Let’s inspect the behavior of a simple indicator on realtime bars. The following script calculates an RSI of close values using ta.rsi() and displays the result with a plot() call. To track the number of executions that occur per bar, the script increments an executions variable declared with varip and calculates its one-bar change using ta.change(). The script converts each bar’s execution count to a string with str.tostring(), then displays the result in a color-coded label at the bar’s high. The label is purple if the bar is open. Otherwise, it is gray. The script also highlights the background of realtime bars in orange using bgcolor(): Pine Script® Copied //@version=6 indicator("Executions on realtime bars demo") //@variable The 14-bar RSI of `close` prices. float rsi = ta.rsi(close, 14) //@variable Tracks the number of script executions, where 1 represents the first execution. varip int executions = 0 // Add 1 to the `executions` value. executions += 1 //@variable Is `color.gray` if the bar is confirmed (closed); `color.purple` otherwise. color labelColor = barstate.isconfirmed ? color.gray : color.purple // Calculate the one-bar change in `executions`, then convert the value to a string and display the result in a label. // Each call to `label.new()` creates a *new* `label` object. label.new(      bar_index, high, str.tostring(ta.change(executions)),      color = labelColor, textcolor = color.white, size = 20, force_overlay = true  ) // Plot the `rsi` value with colors based on whether the value is above 50 or not. plot(rsi, "RSI", rsi > 50 ? color.teal : color.maroon, 3) // Highlight the chart's background in translucent orange when `barstate.isrealtime` is `true`. bgcolor(barstate.isrealtime ? color.new(color.orange, 70) : na, title = "Realtime highlight", force_overlay = true) When we first add the script to the chart, it does not add an orange background to any bar because it calculates only on data that exists at the script’s loading time. This data is historical. Each bar’s label shows a value of 1 because indicators always execute once per historical bar: Notice the countdown timer and the purple label for the latest bar in the chart above. These both indicate that the bar is open and subject to changes. A new update from the data feed affects the bar’s values, triggering rollback and a new script execution to recalculate the results. When rollback occurs, the runtime system reverts the internal data of the ta.rsi() call to its last committed state, erases the state of the rsi variable, and deletes the latest label object. However, the system does not revert the executions variable because it uses the varip keyword. After rollback, the system updates the built-in close, high, and barstate.* variables using the current bar’s latest data, and the new execution begins. The script evaluates the ta.rsi() call using the new close price and reinitializes the rsi variable with the returned value. Then, it increases the executions value by one, evaluates ta.change() again, and creates a new label at the bar’s current high price to show the updated result. Lastly, it evaluates the plot() and bgcolor() calls to replace the bar’s plotted visuals. The last bar’s label remains purple because the bar is still open, but the background color is now orange because barstate.isrealtime is true: As subsequent updates become available from the data feed, the pattern of rollback and re-execution continues, and the script’s outputs for the bar update with each new execution: The last time that rollback and another execution occur on this bar is after the closing tick, when the bar becomes an elapsed realtime bar. After the final execution, the bar’s label is gray because barstate.isconfirmed is true. The runtime system then commits necessary data from this execution to the time series for calculations on future bars. Then, a new realtime bar opens after another update from the data feed, and the execution pattern continues: Note that: Although the previous bar is now confirmed, it still has an orange background corresponding to a realtime state because it closed after the script’s loading time. When the script later reloads after an execution-triggering event, that bar becomes historical. It’s important to note that strategies often execute differently than indicators on realtime bars. By default, they execute only once per bar at each closing tick without undergoing rollback. However, users can modify a strategy’s calculation behavior to allow rollback and re-execution on a bar before its closing tick. If the strategy() statement includes calc_on_every_tick = true, or if the user selects the “On every tick” checkbox in the “Settings/Properties” tab, the script executes on a realtime bar after each new update from the data feed, similar to an indicator. Additionally, if the strategy() statement includes calc_on_order_fills = true or the user selects “After order is filled” in the “Settings/Properties” tab, the script executes on each tick where the broker emulator fills an order. With this behavior, the system can execute the script multiple times on the open bar, but only on the ticks where an order fill occurs. Note Rollback typically occurs only after script executions on realtime bars. However, it can also happen on historical bars for strategies that recalculate after an order fills, because such scripts can execute more than once on any bar. To summarize the general process for script executions on realtime bars: An indicator or library script executes on the first available tick in an open realtime bar, then once per update to recalculate the results for the bar using the latest data. A strategy script executes only on the bar’s closing tick by default, but users can modify its calculation behavior to allow executions while the bar is open. Before each new script execution on an open bar, the runtime system executes a rollback process, which reverts all applicable variables, expressions, and objects to their last committed states as of the previous bar’s close. After the script executes on an elapsed realtime bar’s closing tick, the system commits necessary data from that execution to the time series for access on later bars. It does not commit the data from executions on the bar’s unconfirmed values from previous ticks. Events that trigger script executions Several events cause a script to load and execute across all the available bars in a dataset. The specific events that trigger the loading process depend on where the script runs. For a script on the chart, the following events always cause the script to load and perform new executions on every bar: The user adds the script to the chart for the first time from the Pine Editor or the “Indicators, metrics, and strategies” menu. The user saves an update to the script while it is active on the chart. The chart is refreshed while the script is active. Other events also trigger the loading process for a script on the chart. However, these events do not always cause new script executions on past bars. The results from running a script with a unique combination of settings are often temporarily cached. If cached data exists for a selected combination of settings, the system loads the script using that data. See the Caching section for more information. Below are the additional events that cause a script to load on the chart, either by performing new executions across the dataset or by using available cached data: The user selects new values for the inputs or strategy properties in the script’s “Settings” menu. The script uses the chart.left_visible_bar_time or chart.right_visible_bar_time variable, and the visible chart range changes. The script uses the chart.fg_color or chart.bg_color variable, and the user changes the chart’s background color. The chart loads a new dataset with a different timeframe or ticker identifier. Several user actions affect a chart’s ticker ID, such as selecting a symbol from the “Symbol Search” menu, changing the chart type, toggling data modifications in the chart’s settings, and activating Bar Replay mode. The user opens or closes the Pine Logs pane. The user activates or deactivates the Pine Profiler. For scripts used in other locations, the following events trigger the loading process: The user creates a new script alert from the “Create Alert” dialog box. The user pauses and restarts an alert instance from the “Alerts” menu. The user clicks the “Generate report” button in the Strategy Tester while Deep Backtesting mode is enabled. The user clicks the “Scan” button in the Pine Screener to run the script on the datasets from a chosen watchlist. After a script loads, either of the following causes new script executions on an open bar: One of the events above causes the script to load again and execute across the entire dataset up to the bar. The script runs on the chart or in an alert, and the bar updates after new data becomes available. The system performs rollback and re-executes the script on that bar using the latest data. The only exception is if the script is a strategy that does not allow recalculation on the new tick. When a script completely reloads on the chart or in an alert after an applicable event, all the elapsed realtime bars from the script’s previous run become historical bars in the new run, because they represent confirmed data points that the script accesses from a different data feed as it loads. The bars in a symbol’s dataset come from two distinct data feeds: the historical feed and the realtime feed. The historical feed reports only the final values for each bar, whereas the realtime feed includes the temporary values from all available ticks. When a realtime bar becomes historical after a script restarts, the values from the bar’s previous ticks are no longer accessible; only the final price, volume, and other values remain. Therefore, if a script relies on temporary data from realtime bars in its calculations, it might behave differently after reloading. For example, the following script calculates the one-bar arithmetic return of the close series and displays the result as a line plot. On each realtime bar, the script updates three variables declared with varip to track the first, highest, and lowest return values calculated during executions across the bar’s ticks, then calls plotcandle() to plot a candle showing the values. Additionally, it uses bgcolor() to highlight the background of realtime bars in orange: Pine Script® Copied //@version=6 indicator("Reloading a script demo", precision = 5) //@variable The one-bar arithmetic return of the `close` series. float priceReturn = ta.change(close, 1) / close[1] // Declare persistent variables to track the first, highest, and lowest `priceReturn` values across ticks in // each realtime bar. varip float o = na varip float h = na varip float l = na if barstate.isrealtime     // On the first tick in the realtime bar, reassign `o`, `h`, and `l` to hold the value of `priceReturn`.     if barstate.isnew         o := priceReturn         h := priceReturn         l := priceReturn     // Otherwise, reassign `h` and `l` to the bar's highest and lowest `priceReturn` value as of the current tick.     else         h := math.max(h, priceReturn)         l := math.min(l, priceReturn) // Plot candles to display the `o`, `h`, `l`, and `priceReturn` values for each realtime bar. // The candles do not appear on historical bars, because `o`, `h`, and `l` are `na` on those bars. plotcandle(o, h, l, priceReturn, "Return candles", color.blue, chart.fg_color, bordercolor = chart.fg_color) // Dispaly the `priceReturn` series as a purple line plot. plot(priceReturn, "Return plot", color.purple, 3) // Highlight the background of all realtime bars in orange. bgcolor(barstate.isrealtime ? color.new(color.orange, 70) : na, title = "Realtime highlight") After the script loads on the chart and executes on several realtime bars, all the elapsed realtime bars, as well as the open realtime bar, include plotted return candles and an orange background color: After an applicable event, such as a chart refresh, the script reloads and executes across the dataset again. All the closed bars with a realtime state in the previous run become historical bars in the new run. The results thus change because our script relies on realtime data. As shown below, the script does not display candles or background colors for previous bars after we refresh the chart. Those outputs appear only for the latest bar, after new ticks become available, because that bar is now the only one with a realtime state: Note that: The barstate.isnew variable has a value of true when a realtime bar opens, and false on all subsequent updates to the bar. If the script reloads midway through a realtime bar’s progression, only the background color appears on that bar. The script does not show a candle on the first realtime bar in that case, because its o, h, and l variables hold na until the first time that barstate.isnew is true. Note A script might also behave differently after reloading due to differences between bars constructed from realtime data and those retrieved from historical data feeds. Occasionally, a data provider must adjust the values of a closed bar originally built from realtime updates. In such cases, the script accesses the adjusted values only after restarting and retrieving the bar from the historical feed. Although such adjustments are typically minor and infrequent, they can cause slight variations in a script’s calculations on former realtime bars. Refer to the Repainting page to learn more about potential differences between historical and realtime script behaviors and their causes. Caching When a script runs on a chart for the first time using a unique configuration, the data from that run is often temporarily cached for reuse. The cached data is erased after the chart is refreshed or the user updates the script’s source code. In this context, the configuration refers to the combined state of all script, chart, and developer tool settings that can affect the script’s executions. This combination includes: The values of inputs in the script’s “Settings/Inputs” tab. The values of the strategy properties in the “Settings/Properties” tab. The values of the chart.* variables whose qualifiers are “input” (chart.left_visible_bar_time, chart.right_visible_bar_time, chart.fg_color, and chart.bg_color). The chart’s timeframe. The chart’s ticker identifier. Whether the Pine Logs pane is open or closed. Whether the Pine Profiler is active or not. Each time that a script runs using a unique combination of settings, it executes from start to end on each bar in the dataset to perform new calculations. If possible, the script’s data from the run is then cached. If cached data is available on past bars for a selected combination of settings, the runtime system loads the script using that data. This behavior enables users to change a script’s inputs, alter the chart, and toggle developer tools without losing information — including bar states — from previous script runs using different settings. Additionally, caching helps reduce loading times and resource requirements when switching between settings or adding multiple instances of the same script to the chart. To understand this behavior, let’s revisit the script from the previous section. The script has different behaviors on historical and realtime bars. In the version below, we’ve added a lengthInput variable that holds the value from an input.int() call. The script uses this variable to define the length of the ta.change() calculation and the offset of the history-referencing operator: Pine Script® Copied //@version=6 indicator("Caching demo", precision = 5) //@variable The bar span of the `priceReturn` calculation. int lengthInput = input.int(5, "Length", 1) //@variable The arithmetic return of the `close` series across `lengthInput` bars. float priceReturn = ta.change(close, lengthInput) / close[lengthInput] // Declare persistent variables to track the first, highest, and lowest `priceReturn` values across ticks in // each realtime bar. varip float o = na varip float h = na varip float l = na if barstate.isrealtime     // On the first tick in the realtime bar, reassign `o`, `h`, and `l` to hold the value of `priceReturn`.     if barstate.isnew         o := priceReturn         h := priceReturn         l := priceReturn     // Otherwise, reassign `h` and `l` to the bar's highest and lowest `priceReturn` value as of the current tick.     else         h := math.max(h, priceReturn)         l := math.min(l, priceReturn) // Plot candles to display the `o`, `h`, `l`, and `priceReturn` values for each realtime bar. // The candles do not appear on historical bars, because `o`, `h`, and `l` are `na` on those bars. plotcandle(o, h, l, priceReturn, "Return candles", color.blue, chart.fg_color, bordercolor = chart.fg_color) // Dispaly the `priceReturn` series as a purple line plot. plot(priceReturn, "Return plot", color.purple, 3) // Highlight the background of all realtime bars in orange. bgcolor(barstate.isrealtime ? color.new(color.orange, 70) : na, title = "Realtime highlight") After we add the script to our 1m chart and let it run for a few minutes with a “Length” input value of 5, the script plots candles and highlights the background for the latest few bars, because barstate.isrealtime is true on those bars: Let’s change the “Length” input to a new value, causing the script to reload and execute across the dataset again. Here, we changed the value from 5 to 10 and let the script execute on some new ticks. The script no longer displays candles and background colors for the same bars after restarting, because it now accesses the data for those formerly realtime bars from the historical data feed: As shown above, the realtime bar information from the first run is not available when we change the script’s input to a new value. However, the data from that previous run still exists in memory. If we revert the “Length” input’s value to 5, the candle plot and background colors start on the same bar as the first run: If we add a second instance of the script to the chart, using the same settings, the runtime system loads the new instance using the cached data instead of executing it entirely from scratch. As such, its outputs are identical to those from the first script instance, even though we added it to the chart a few bars later: Similarly, cached data usually remains available even if we remove the script from our chart and add it again. Tip You can clear cached data for a script on the chart at any time by simply reloading the chart. Time series A symbol’s dataset is a form of time series — a sequence of collected values indexed by time. Each bar represents a distinct data point, anchored to a specific time, that contains price and volume data for a particular period. This data format thus shows how a symbol’s values progress across time in successive periodic steps. Pine Script’s internal time series structure follows a similar format. After executing a script on a closed bar’s confirmed values, the runtime system commits (saves) the results of the script’s statements and expressions to internal time series for later use. Each bar with committed data has an assigned index in the series, where 0 represents the first bar, 1 represents the second, and so on. Scripts can retrieve this index with the bar_index variable. Scripts can access the data committed to the time series on past bars by using the [] history-referencing operator. The value between the operator’s square brackets specifies the position of the referenced bar in the time series as a relative offset behind the current bar. For variables and expressions in the global scope, an offset value of 1 refers to the previous bar at bar_index - 1 (one bar back), a value of 2 refers to the bar at bar_index - 2 (two bars back), and so on. An offset of 0 always refers to the current bar. For example, consider the open variable, which holds the opening price of the current bar on which the script executes. Before each script execution on a new bar, the runtime system commits the open value from the last execution on the previous bar. Then, it updates the variable to hold the current bar’s opening price. To access the committed open value for the previous bar, we can use the expression open[1]. To access the committed value from 10 bars back, we use open[10]. The script below performs three history-referencing operations to retrieve the current bar’s open value, the value from one bar back, and the value from a user-specified number of bars back. Then, it plots the retrieved values on the chart for comparison: Pine Script® Copied //@version=6 indicator("History referencing demo", overlay = true, behind_chart = false) //@variable The number of bars back from which to retrieve the `open` price for `pastOpen`. int offsetInput = input.int(10, "Bar offset", 0) //@variable The current bar's opening price. `open[0]` is equivalent to using `open` without the `[]` operator. float currOpen = open[0] //@variable The last committed `open` value. Represents the previous bar's value, or `na` if no previous bar exists. float prevOpen = open[1] //@variable The `open` value committed `offsetInput` bars back, or `na` if no bar exists at that offset. float pastOpen = open[offsetInput] // Plot `currOpen`, `prevOpen`, and `pastOpen` for comparison. plot(currOpen, "Current `open`",                 color.blue,    2) plot(prevOpen, "Previous bar `open`",            color.purple,  3) plot(pastOpen, "Past `open` from custom offset", color.orange,  4) Note that: The expression open[0] is equivalent to using open without the history-referencing operator, because an offset of 0 refers to the current bar. At the beginning of the chart’s dataset, the expressions open[1] and open[offsetInput] return na because they refer to previous bars that are unavailable. Each history-referencing expression also leaves a trail of values in the time series. Therefore, it is possible to retrieve past states of the expression using another history-referencing operation, e.g., (open[offsetInput])[1]. Internally, the system maintains a limited amount of time series data for variables and expressions in fixed-length historical buffers. These buffers define the maximum offsets allowed for history-referencing operations. See the next section, Historical buffers, to learn more. Another way that scripts use committed values from a time series is by calling the built-in functions that reference history internally, such as those in the ta.* namespace. For example, the expression ta.highest(high, 20) calculates the highest value from the high series over a 20-bar window. It compares the series’ current value to the committed values from the previous 19 bars to determine the result. The script below executes this call on each bar and plots the resulting series on the chart. Additionally, the script colors the background of the last 20 bars on the chart to highlight the bars used in the latest execution’s ta.highest() call: Pine Script® Copied //@version=6 indicator("History referencing in functions demo", overlay = true, behind_chart = false) //@variable The highest value from the `high` series across the 20 most recent bars. //          The `ta.highest()` call compares the current `high` to the last 19 committed values. float highest = ta.highest(high, 20) // Plot the `highest` series on the chart. plot(highest, "20-bar high", color.purple, 3) // Color the background of the last 20 bars, i.e., the bars used by the latest execution's `ta.highest()` call. bgcolor(color.new(color.blue, 70), show_last = 20, title = "Last 20 bar highlight") Note that: The first 19 bars of the chart have a plotted value of na, because the ta.highest() function call requires the high values from the current bar and 19 previous bars to calculate the result. All function calls and expressions that do not return “void” leave historical trails in the time series, just like variables. Therefore, scripts can use an expression such as ta.highest(high, 20)[10] to retrieve the 20-bar high from 10 bars back. The ta.highest() function and other functions that access past values from a time series must execute in the global scope for consistent calculations. Time series storage for variables and expressions in local scopes works differently than that for global values. See the Time series in scopes section for more information. Note Programmers should not confuse time series with the “series” qualifier. The time series concept describes how Pine’s runtime system stores and retrieves data across successive script executions. In contrast, the “series” qualifier describes variables and expressions whose values can change from bar to bar, such as open. To understand this distinction, consider the timeframe.period variable, which is of the “simple string” qualified type. The variable’s value cannot change because its qualifier is “simple”, but it still leaves a trail of successive values in the time series. It is possible, though not very useful, to retrieve the value from 10 bars back using an expression such as timeframe.period[10]. The returned value equals the timeframe.period value for all bars with a bar_index of 10 and above. However, the expression’s result is “series string”, because the expression returns a different value (na) on the first 10 bars. See the Qualifiers section of the Type system page to learn more about “series” and other qualifiers. Historical buffers To promote efficiency and help ensure computing resources remain available for all users, the Pine Script runtime system uses fixed-length historical buffers to maintain a limited amount of time series data for all variables and expressions. These historical buffers define the maximum number of committed data points that a script can access on any bar via the history-referencing operator or the built-in functions that reference past bars internally. For most series, the underlying historical buffer can contain data from up to 5000 past bars. The only exception is for some built-in series such as open, close, and time, whose buffers can store data for more than 5000 bars. Although these buffers can contain thousands of data points at their maximum size, a script might not require that much past data for its calculations on any bar. Therefore, the runtime system automatically optimizes the size of each series’ historical buffer based on the historical references that the script performs as it loads on the dataset. Each resulting buffer contains only the amount of past data required by the script’s calculations and not more. For instance, if the maximum number of bars back for which a script references the value of a variable on historical bars is 500, the system maintains a historical buffer that includes only the latest 500 committed values of that variable. The buffer does not store 5000 committed values, because the script does not require all that extra data. This behavior thus helps to minimize a script’s resource requirements while preserving the integrity of its calculations. To determine the sufficient buffer size for each variable and expression in a script, the runtime system performs the following process during the script’s loading time: It analyzes all the historical references that occur while executing the script on the dataset’s first 244 bars, then sets the initial size of each buffer to the minimum size that accommodates those references. While executing the script on subsequent bars, it checks if the script attempts to access data from previous bars that are beyond the limits of the defined buffers. If the script’s historical references exceed the buffer limits on any bar, the system restarts the loading process and tries a larger buffer size. In the rare case that a historical buffer’s size remains insufficient after several calculation attempts, the system stops the script and raises a runtime error. It’s crucial to emphasize that the runtime system defines the sizes of all historical buffers only while executing a script on historical bars. It does not adjust any historical buffers during executions on new bars from the realtime data feed. If a script references past data from beyond a historical buffer’s limits while executing on a realtime bar, it causes a runtime error. For example, the script below retrieves a past value from the close series using the history-referencing operator with an offset of 100 bars back on historical bars and 150 bars back on realtime bars. Because the script references data from 100 bars back during all historical executions, the system sets the close buffer’s size to include only 100 past values. Consequently, an error occurs when the script executes on the open realtime bar, because a historical offset of 150 is beyond the buffer’s limit: Pine Script® Copied //@version=6 indicator("Max bars back error demo", overlay = true) // @variable The historical offset for retrieving past values from the `close` series. //           If the bar is historical, the offset is 100. Otherwise, the offset is 150. int offset = barstate.ishistory ? 100 : 150 // @variable The value of `close` from `offset` bars back. //           This code causes a *runtime error* on a realtime bar. During all code executions on historical bars, //           the script requires only the latest 100 past values of `close`, so the system sets the buffer size to //           include only the past 100 values. The offset of 150 is thus *out of bounds*. float pastClose = close[offset] // Plot the `pastClose` series. plot(pastClose, "Past `close`", chart.fg_color, 3) // Highlight the background of all realtime bars in orange. bgcolor(barstate.isrealtime ? color.new(color.orange, 70) : na, title = "Realtime highlight") For cases like these, programmers can manually set the size of a historical buffer to ensure it contains a sufficient amount of data by doing any of the following: Modify the script to reference the maximum required number of bars back with the [] operator during its execution on the first bar. Call the max_bars_back() function to explicitly set the historical buffer size for a specific series. Include a max_bars_back argument in the indicator() or strategy() declaration statement to set the initial size of all historical buffers. Notice The larger the size of a historical buffer, the more memory resources it requires. A script with buffers that are too large can cause the “Memory limits exceeded” error. Therefore, when manually setting the buffer size for a series, use the smallest possible size that accommodates all required historical references to that series. Below, we modified the script by including the expression max_bars_back(close, 150), which sets the size of the close buffer to include 150 past values. With the appropriate buffer size manually defined, the script’s history-referencing operation no longer causes an error on realtime bars: Pine Script® Copied //@version=6 indicator("Manual buffer sizing demo", overlay = true) // @variable The historical offset for retrieving past values from the `close` series. //           If the bar is historical, the offset is 100. Otherwise, the offset is 150. int offset = barstate.ishistory ? 100 : 150 // Set the size of the `close` historical buffer to include 150 past values, ensuring the script has exactly // the amount of history that it requires on realtime bars. max_bars_back(close, 150) // @variable The value of `close` from `offset` bars back. //           This code no longer causes an error when it executes on a realtime bar, because the historical //           buffer has an appropriate size defined in advance. float pastClose = close[offset] // Plot the `pastClose` series. plot(pastClose, "Past `close`", chart.fg_color, 3) // Highlight the background of all realtime bars in orange. bgcolor(barstate.isrealtime ? color.new(color.orange, 70) : na, title = "Realtime highlight") Tip Manually setting historical buffer sizes can also improve a script’s resource efficiency in some cases. As explained above, the runtime system restarts a script to recalculate its buffers if any historical reference exceeds a buffer’s limit after the first 244 bars. This process increases the script’s loading time and memory use. Setting the appropriate buffer size in advance with max_bars_back() prevents the script from restarting for buffer calculations. See the Minimizing historical buffer calculations section of the Profiling and optimization page for more information. Time series in scopes The scope of a variable or expression refers to the part of the script where it is defined and accessible in the code. Every script has one global scope and zero or more local scopes. All variables and expressions in a script that are outside user-defined functions or methods, conditional structures, loops, and user-defined type or enum type declarations belong to the global scope. The script evaluates variables and expressions from this scope once for every execution across bars and ticks in the dataset. All functions, methods, conditional structures, and loops create their own local scopes. The variables and expressions defined within a local scope belong exclusively to that scope. In contrast to the global scope, a script does not always evaluate a local scope once per execution; the script might evaluate the scope zero, one, or several times per execution, depending on its logic. For the runtime system to commit data from a variable or expression and queue that data into a historical buffer on any bar, a script must evaluate the scope of that code once when it executes on the bar’s closing tick. If the script does not evaluate the scope, the runtime system cannot add new data to the historical buffer. Similarly, if the script evaluates the scope repeatedly within a loop, the historical buffer cannot store series data for each separate iteration, because each entry in the time series corresponds to a single bar. Therefore, time series behave differently in global and local scopes: the historical buffers for global variables and expressions always contain committed data for consecutive past bars, whereas the buffers for local variables often contain an inconsistent history of committed data. Note Function and method parameters have the same historical buffer behaviors as local variables. Each parameter in a function call has a unique buffer, and the system can add new data to that buffer only on a bar’s closing tick. If the script does not evaluate the function call on every closing tick, the buffers for its parameters contain an inconsistent history. This behavior applies even if the argument supplied to a parameter is a global variable. If a script references the history of a global variable using an expression such as myVariable[1], the historical offset of 1 always refers to the confirmed myVariable value from the previous bar. Likewise, an expression such as myVariable[9] retrieves the variable’s value from nine bars back. Either expression consistently accesses the value corresponding to a specific number of bars back, because the runtime system commits a new value for that global variable on every bar. In contrast, the result of using the [] operator on a local variable does not represent the value from a specific number of bars back. Instead, it represents the variable’s last committed value as of the bar at the specified offset. For instance, suppose myVariable is a local variable, and the script last evaluated the variable’s scope 10 bars before the current bar. A history-referencing operation with any offset from 1 to 9 on that variable — such as myVariable[1], myVariable[5], or myVariable[9] — returns the variable’s value from 10 bars back, because there is not a recent committed value after that point for the operator to access. This behavior often leads to unintended results. Therefore, to ensure consistency, we recommend using historical references only on variables or expressions that the script evaluates on every bar. The following example demonstrates how a simple history-referencing operation behaves inside a user-defined function’s scope when a script does not call the function on every bar. The script below defines a custom upDownColor() function, which compares the current value of its source parameter to the last committed value (source[1]) on each call. The function returns color.blue if the current source value is higher than the previous value. Otherwise, it returns color.orange. The script uses this function conditionally, inside a ternary operation, to determine the color of a plot that shows the remainder from dividing bar_index by a specified value. If the remainder variable’s value is nonzero, the operation calls upDownColor(remainder) to calculate the color (blue or orange). If the value is 0, the operation does not use the call and instead returns color.gray. The remainder value increases on each bar, except for when it returns to 0 — causing the gray color. Therefore, a user might expect the plot’s color to be only blue or gray on every bar. However, the color changes to orange on each bar after the one where the color is gray, even though the remainder value on that bar is higher than the value on the previous bar: Pine Script® Copied //@version=6 indicator("Local historical references demo") //@function  Returns `color.blue` if `source` is above its last committed value; `color.orange` otherwise. //           For consistent results, this function should execute on *every bar*, because it uses the //           history-referencing operator on the `source` series. // //           Even if the argument supplied to `source` comes from a global variable, the `source` parameter remains //           part of the function's *local scope*. The system maintains a *separate historical buffer* for the `source` //           series in each function call instance. The buffer contains only the committed `source` values from the bars //           where the function call occurs. If the call does not occur on a bar, the buffer for `source` contains //           **no new data** for that bar. upDownColor(float source) =>     source > source[1] ? color.blue : color.orange //@variable The value by which to divide the `bar_index` value. int divisorInput = input.int(5, "Divisor", 1) //@variable The remainder of dividing `bar_index` by `divisorInput`. float remainder = bar_index % divisorInput //@variable Is `color.gray` if `remainder` equals 0, and the result of `upDownColor(remainder)` otherwise. //          The `upDownColor()` call does not execute on every bar. Therefore, it does *not* always compare the //          `remainder` value from one bar back to calculate the color. Instead, the function compares the current //          `remainder` to the value from the last bar where `remainder` is nonzero. color plotColor = remainder == 0 ? color.gray : upDownColor(remainder) // Plot the `remainder` series and color it using `plotColor`. The plot is orange after each bar where `remainder == 0`, // because the `upDownColor()` function call does not have data for that bar to use in its logic. plot(remainder, "Remainder", plotColor, 5) The script behaves this way because upDownColor() uses the history-referencing operator on the source series, which is local to the function’s scope, and the script does not call the function on every execution. When the value of remainder is zero, the first expression in the ternary condition evaluates to true, and therefore the second branch of the ternary expression, which contains the function call, does not execute. The compiler issues the following warning about the function directly in the Pine Editor: The function `upDownColor()` should be called on each calculation for consistency. It is recommended to extract the call from the ternary operator or from the scope. The runtime system maintains a separate historical buffer for the local source series, but it cannot update that buffer unless the script calls the function. On each bar where remainder is 0, the call does not occur, and the system has no new value to commit to the time series. Therefore, the source buffer does not contain a value for that bar. On the bar that follows, the local expression source[1] refers to the source value from the last bar where the upDownColor() call occured — two bars back — and not the value of remainder from the previous bar. Because the value from two bars back is higher than the current value, the returned color is color.orange instead of color.blue. We can fix this script’s behavior by following the instructions in the compiler warning. Below, we modified the script by moving the upDownColor() call outside the ternary expression, enabling the script to execute it on every bar. The historical buffer for the function’s source series now contains remainder values from consecutive bars. With this change, an orange color does not appear because the function consistently compares values from one bar back: Pine Script® Copied //@version=6 indicator("Consistent historical references demo") //@function  Returns `color.blue` if `source` is above its last committed value; `color.orange` otherwise. //           For consistent results, this function should execute on *every bar*, because it uses the //           history-referencing operator on the `source` series. upDownColor(float source) =>     source > source[1] ? color.blue : color.orange //@variable The value by which to divide the `bar_index` value. int divisorInput = input.int(5, "Divisor", 1) //@variable The remainder of dividing `bar_index` by `divisorInput`. float remainder = bar_index % divisorInput //@variable Is `color.blue` if `remainder` is above its previous value, and `color.orange` otherwise. color secondColor = upDownColor(remainder) //@variable Is `color.gray` if `remainder` equals 0, and `secondColor` otherwise. //          This ternary operation no longer causes a warning. The scope of the `upDownColor()` call executes on //          every bar, meaning its historical buffer consistently includes data for consecutive past bars. color plotColor = remainder == 0 ? color.gray : secondColor // Plot the `remainder` series and color it using `plotColor`. The plot is now blue or gray, but never orange. plot(remainder, "Remainder", plotColor, 5) A similar behavior applies to all built-in functions that reference past values internally, such as those in the ta namespace. For example, the ta.sma() function uses the current value of a source series and length - 1 past values from that series to calculate a moving average. If a script calls this function only on some bars instead of on every bar, the historical buffer for source does not contain values for consecutive past bars. Therefore, such a call can cause unintended results, because the call calculates the returned average using an inconsistent history of previous values. The script below demonstrates how the results of the ta.sma() function can vary with the scope in which the function call occurs. The script declares three global variables to hold calculated SMA values: controlSMA, localSMA, and globalSMA. The script initializes controlSMA using the result of a ta.sma() function call, and it initializes the other two variables with na. Within the if structure, the script updates the value of globalSMA using controlSMA, and it updates localSMA using the result of another ta.sma() call with the same arguments as the first call. As shown below, the controlSMA and globalSMA variables have the same value. Both hold the result of the global ta.sma() call, which executes on every bar. The internal historical buffer for source in that call thus includes committed values for consecutive past bars. In contrast, the localSMA value differs, because the ta.sma() call for that variable does not execute on every bar. The buffer for that call’s local source series contains only the values from bars with an even bar_index value: Pine Script® Copied //@version=6 indicator("`ta.*()` functions in scopes demo", overlay = true, behind_chart = false) //@variable Is `true` if the `bar_index` is divisible by 2, and `false` otherwise. bool condition = bar_index % 2 == 0 //@variable The 20-bar moving average of `close` values. //          This `ta.sma()` call executes in the global scope, so the script evaluates it on every bar. float controlSMA = ta.sma(close, 20) // Declare two additional variables to modify later within the `if` structure's scope. float globalSMA = na float localSMA  = na if condition     // Assign the `controlSMA` value to `globalSMA`. This code does not cause a warning.     globalSMA := controlSMA     // Call `ta.sma()` with the same arguments as before within this block and assign the result to `localSMA`.     // The function call causes a warning, because it does not execute in the global scope.     // The historical buffers for this `ta.sma()` call contain data only for the bars where `condition` is `true`,     // thus leading to a *different* result.     localSMA := ta.sma(close, 20) // Plot `controlSMA`, `globalSMA`, and `localSMA` for comparison. plot(controlSMA, "Control SMA", color.blue,   2) plot(globalSMA,  "Global SMA",  color.purple, 3, style = plot.style_circles) plot(localSMA,   "Local SMA",   color.gray,   3, style = plot.style_circles) To summarize the behavior of time series in a script’s scopes: A script evaluates its global scope once on every execution. After each script execution on a bar’s closing tick, the system commits the data for variables and expressions in the global scope and updates their historical buffers. The resulting buffers thus include data for consecutive past bars, ensuring consistent results for operations and functions that rely on past data. A script evaluates local scopes zero, one, or several times per execution. The runtime system cannot maintain consistent bar-by-bar historical buffers for scopes that a script does not evaluate on every bar, or for scopes that the script evaluates more than once on a bar’s closing tick. Therefore, using the [] operator on local variables and expressions, or not calling functions that access past data once on each closing tick, can cause unintended results. Note Not all built-in functions use past data from historical buffers in their calculations. For example, most functions in the math namespace, excluding math.sum(), operate only on the current arguments of the function call. Functions that do not interact with their history in any way do not require special treatment to ensure intended results. If the use of a function call within a local block does not cause a compiler warning, it is typically safe to use in that block without affecting the result. However, if the warning occurs, it is usually best to move the call to the global scope — outside the operands of the or, and, or ?: operators — to ensure consistent calculations. Next Type system On this page Introduction The basics Bar-by-bar execution Storing and using data from previous bars Realtime bars The details Executions on historical bars Executions on realtime bars Events that trigger script executions Caching Time series Historical buffers Time series in scopes --- # https://www.tradingview.com/pine-script-docs/language/type-system User Manual / Language / Type system Type system Introduction Pine Script® uses a system of types and type qualifiers to categorize the data in a script and indicate where and how the script can use it. This system applies to all values and references in a script, and to the variables, function parameters, and fields that store them. Types in Pine Script indicate the kinds of information that a script’s data represents. Some types directly represent values, such as numbers, logical conditions, colors, or text, while others define structures for special tasks, such as displaying visuals on the chart. Qualifiers indicate when the values of any given type are accessible, and whether those values can change across script executions. The combination of a type and a qualifier forms a qualified type, which determines the operations and functions with which a value or reference is compatible. Note For the sake of brevity, we often use the term “type” when referring to a qualified type. The type system closely connects to the execution model and its time series structure — together, they determine how a script behaves as it runs on a dataset. Although it’s possible to write simple scripts without understanding these foundational topics, learning about them and their nuances is key to mastering Pine Script. Qualifiers Pine’s type qualifiers (const, input, simple, and series) indicate when values in a script are accessible — either at compile time, input time, or runtime — and whether those values can change across script executions: "const" Established at compile time, when the user saves the script in the Pine Editor or applies the script to a dataset. Values qualified as “const” remain constant during every script execution. "input" Established at input time, when the system confirms input data from the script’s “Settings/Inputs” tab or the chart. Similar to “const” values, “input” values do not change as the script runs on the dataset. "simple" Established by the script at runtime, on the first available bar. On all subsequent bars, values qualified as “simple” do not change. "series" Dynamic. Values qualified as “series” are available at runtime, and they are the only values that can change across bars. Note The “const”, “input”, and “simple” qualifiers apply exclusively to value types. Pine’s reference types, such as label and array types, automatically inherit the “series” qualifier. Pine Script uses the following qualifier hierarchy to determine the compatibility of values in a script’s calculations: const < input < simple < series In this hierarchy, “const” is the lowest (weakest) qualifier, and “series” is the highest (strongest). Any variable, parameter, or operation that accepts a value with a specific qualifier also allows a value of the same type with a weaker qualifier, but not one that is stronger. For instance, a function parameter that accepts a value of the “simple int” qualified type also allows a value of the “input int” or “const int” type, because “const” and “input” are lower than “simple” in the qualifier hierarchy. However, the parameter cannot accept a “series int” value, because “series” is higher in the hierarchy than “simple”. Pine also uses this hierarchy to determine the qualifiers assigned to the results of expressions, i.e., function calls and operations. The returned types of an expression always inherit the strongest qualifier in the calculation. For example, an expression that performs a calculation using “input” and “simple” values returns “simple” results, because “simple” is a stronger qualifier than “input”. Note that a script cannot change the qualifier of a returned value to one that is lower in the hierarchy to make it compatible with specific operations or functions. For instance, if a calculation returns a value qualified as “series”, the script cannot modify that value’s qualifier later to enable using it in code that requires “simple” or “const” values. The following sections explain the behavior of each type qualifier, as well as the built-in keywords that programmers can use to specify qualifiers in their code. const Values qualified as “const” are available at compile time, before the script starts its first execution. Compilation occurs when the user saves the script in the Pine Editor, and immediately before a script starts to run on the chart or in another location. Values with the “const” qualifier remain constant after compilation; they do not change during any script execution. All literal values and the results of expressions that use only values qualified as “const” automatically inherit the “const” qualifier. The following list shows a few values of each fundamental type. All of these represent literal values if a script includes them directly in its source code: literal int: 1, -1, 42 literal float: 1., 1.0, 3.14, 6.02E-23, 3e8 literal bool: true, false literal color: #FF55C6, #FF55C6ff literal string: "A text literal", "Embedded single quotes 'text'", 'Embedded double quotes "text"' Scripts can declare variables that hold “const” values, and use those variables to calculate other constants. In the example below, we use “const” variables to set the title of a script and its plots. The script compiles successfully, because the indicator() and plot() calls used in the code both require a title argument of the “const string” qualified type: Pine Script® Copied //@version=6 // All of the following global variables automatically inherit the "const" qualifier, // because their "string" values are constants that are available at *compile time*. //@variable The indicator's title. INDICATOR_TITLE = "const demo" //@variable The title of the first plot. var PLOT1_TITLE = "High" //@variable The title of the second plot. PLOT2_TITLE = "Low" //@variable The title of the third plot. PLOT3_TITLE = "Midpoint between " + PLOT1_TITLE + " and " + PLOT2_TITLE // Set the title of the indicator using `INDICATOR_TITLE`. indicator(title = INDICATOR_TITLE, overlay = true) // Set the title of each plot using the `PLOT*_TITLE` variables. plot(high, PLOT1_TITLE) plot(low, PLOT2_TITLE) plot(hl2, PLOT3_TITLE) Note that: All the variables above the indicator() call in this script have the “const” qualifier, because they hold a literal value or the result of operations that use only “const” values. All our “const” variables in this example have names in uppercase snake case so that they are easy to distinguish in the code, as recommended by our Style guide. Although the “const” variables in this script hold constant values, the script initializes them on every bar. The only exception is PLOT1_TITLE, which the script initializes only on the first bar, because its declaration includes the var keyword. See the Declaration modes section of the Variable declarations page to learn more. Any variable or function parameter that requires a “const” value cannot accept a value with the “input”, “simple”, or “series” qualifier, because “const” is the lowest qualifier in the qualifier hierarchy. For example, the following script combines a literal string with the value of syminfo.ticker to set the value of a scriptTitle variable. Then, it attempts to use the variable as the title argument of the indicator() declaration statement, causing a compilation error. The title parameter requires a “const string” argument, but scriptTitle holds a value of the type “simple string”: Pine Script® Copied //@version=6 //@variable Holds a value intended for use as the `title` argument in the `indicator()` call. //          However, this variable's type is "simple string", not "const string", because  //          the value of `syminfo.ticker` is not available until *after* compilation. var scriptTitle = "My indicator for " + syminfo.ticker // This statement causes an error. The `indicator()` statement cannot use a "simple string" // value as its `title` argument. It requires a "const string" value. indicator(title = scriptTitle) plot(close - open) Note that: The syminfo.ticker variable holds a “simple string” value because it depends on data that is available only at runtime. Combining this value with a literal string produces another “simple string” value, because “simple” is a stronger qualifier than “const”. We did not name the scriptTitle variable using snake case, because our Style guide recommends using lower camel case to name variables that do not hold “const” values. Programmers can restrict the behavior of a variable and force constant assignments on each execution by prefixing its declaration with the const keyword, followed by a type keyword or type identifier. If a variable includes const in its declaration, the script cannot change its value with the reassignment or compound assignment operators. This restriction applies even if the new assigned value is still a constant. For example, the script below declares a myVar variable using the const keyword. Then, it attempts to change the variable’s value with the += operator, causing a compilation error: Pine Script® Copied //@version=6 indicator("Cannot reassign `const` variable demo") //@variable A "float" variable declared using the `const` keyword. const float myVar = 0.0 // Using the `+=` operator on `myVar` causes an error, because scripts *cannot* modify variables declared using `const`. myVar += 1.0 plot(myVar) For a variable of any value type, applying the const keyword to the declaration prevents the script from assigning a value qualified as “input”, “simple”, or “series”. Likewise, if a user-defined function parameter of these types includes the keyword in its declaration, it accepts only “const” values. Note Using the const keyword in a script is usually optional. However, the keyword is required when defining exported variables in libraries. The following script attempts to use the value of the close variable as the initial value of a myVar variable declared using the const keyword. However, close is not compatible with the variable, so a compilation error occurs. The value of close is of the type “series float”, because it updates from bar to bar, but the myVar variable requires a “const float” value: Pine Script® Copied //@version=6 indicator("Cannot assign values with stronger qualifiers demo") // This declaration causes an error. The value of `close` is of the type "series float",  // but `myVar` accepts only a "const float" value.  const float myVar = close plot(myVar) Note that: If we remove the const keyword from the variable declaration, the myVar variable automatically inherits the “series” qualifier, and no error occurs. Note Scripts can also use the const keyword when declaring variables of most special types, such as line, label, and array types. However, this keyword does not set the qualifier of these variables; it only prevents the script from changing the variable’s assigned reference (ID) during each execution. Special types and other reference types always inherit the “series” qualifier. See the Value vs. reference types section for advanced details. input Values qualified as “input” are established at input time. They are similar to “const” values, because they are available before the first script execution and never change during runtime. However, unlike “const” values, “input” values depend on user input. All function parameters that have the “input” qualifier can accept only “input” or “const” values; they do not allow values qualified as “simple” or “series”. Most of the built-in input.*() functions return values qualified as “input”. These functions create adjustable inputs in the script’s “Settings/Inputs” tab, enabling users to change specific values in a script without altering its source code. Each time the user changes the value of an input, the script reloads across all bars on the chart — from the first available bar to the most recent bar — to update its results using the specified value, as explained in the Execution model page. Note The only input*() function that does not return a value qualified as “input” is input.source(). That function returns the value of a built-in price series, such as close, or a value from another script’s plots. Therefore, its return type is “series float”, which is not compatible with code that requires “input float” values. See the Source input section of the Inputs page to learn more. The following script requests the value of an RSI calculated on the dataset for a specific symbol and timeframe, and then plots the result on the chart as columns. The script includes two string inputs that specify the context of the request, and it uses a float input value to set the base of the plotted columns. If the user changes any of these inputs in the “Settings/Inputs” tab, the script reloads to update its results for every bar: Pine Script® Copied //@version=6 indicator("'input' values demo") //@variable An "input string" value representing the requested symbol. symbolInput = input.string("AAPL", "Symbol") //@variable An "input string" value representing the timeframe of the requested data. timeframeInput = input.string("1D", "Timeframe") //@variable An "input float" value for specifying the base of the plotted columns. colBaseInput = input.float(50.0, "Column base", 0.0, 100.0) //@variable The value of an RSI calculated on the `symbolInput` symbol and `timeframeInput` timeframe. //          The `request.security()` function's `symbol` and `timeframe` parameters accept "series string" values,  //          so they also allow weaker qualified types such as "input string". requestedRSI = request.security(symbol = symbolInput, timeframe = timeframeInput, expression = ta.rsi(close, 14)) // Plot the `requestedRSI` value as columns with the base defined by `colBaseInput`.  // This call works, because `histbase` requires an "int" or "float" value with the "const" or "input" qualifier. plot(requestedRSI, "RSI", color.purple, 1, plot.style_columns, histbase = colBaseInput) Note that: The plot() function’s histbase parameter, which sets the base of the plotted columns, has the expected type “input int” or “input float”. As such, it cannot accept “simple int/float” or “series int/float” values, because “simple” and “series” are stronger qualifiers than “input”. The request.security() function requests data from a specified dataset. Its symbol and timeframe parameters, which define the context of the request, accept “series string” values by default. Therefore, these parameters also accept “input string” values. See the Other timeframes and data page to learn more about request.*() functions. Some built-in chart.* variables also hold “input” values, because these variables update at input time based on changes to the chart. Scripts that use these variables reload, executing across the entire dataset again, if any chart changes affect their values. The example below uses some of these variables to display a gradient background color that incrementally changes over the chart’s visible bars. It uses chart.left_visible_bar_time and chart.right_visible_bar_time to get the timestamps of the leftmost and rightmost visible bars for its calculation, and it uses chart.bg_color and chart.fg_color to define the start and end colors of the gradient. If the user scrolls or zooms on the chart, or changes the chart’s background color, the script reloads to generate new results: Pine Script® Copied //@version=6 indicator("Built-in 'input' variables demo") //@variable The difference between `time` and the leftmost visible bar's time, relative to the visible range. //          The `chart.*` variables in this calculation depend on input data from the visible chart, so their type is //          "input int". gradientValue = (time - chart.left_visible_bar_time) / (chart.right_visible_bar_time - chart.left_visible_bar_time) //@variable The gradient color. The `chart.*` variables in this calculation are of the type "input color", because  //          they depend on the "Background" inputs in the "Canvas" tab of the chart's settings.  gradientColor = color.from_gradient(gradientValue, 0, 1, chart.bg_color, chart.fg_color) // Color the background using the `gradientColor` value. bgcolor(gradientColor) simple Values qualified as “simple” are established at runtime, while the script executes on the first available bar. Similar to values qualified as “input” or “const”, “simple” values do not change across bars. All variables and function parameters that have the “simple” qualifier can accept only “simple”, “input”, or “const” values; they do not allow values qualified as “series”. Many built-in variables, such as most syminfo.* and timeframe.* variables, hold “simple” values instead of “const” or “input” because they depend on information that a script can obtain only after it starts running on a dataset. Likewise, various built-in function parameters require values with the “simple” qualifier or a weaker one. The following script uses request.security() with a calc_bars_count argument to retrieve a limited history of daily close values. It determines the number of historical days in the request based on the “simple string” value of syminfo.type. For cryptocurrency symbols, the call requests 14 days of historical data. For other symbols, it requests 10 days of data. The script compiles successfully because the reqDays variable holds the type “simple int”, which matches the expected type for the calc_bars_count parameter: Pine Script® Copied //@version=6 indicator("'simple' values demo") //@variable The number of historical days in the data request. This variable's type is "simple int", //          because the strongest qualified type in the calculation is "simple string". reqDays = syminfo.type == "crypto" ? 14 : 10 //@variable The `close` value from the "1D" timeframe.  //          This call works because `calc_bars_count` expects a "simple int" argument. requestedClose = request.security(syminfo.tickerid, "1D", close, calc_bars_count = reqDays) plot(requestedClose) Programmers can explicitly define variables and parameters that require “simple” values, or values with a weaker qualifier, by prefixing their declaration with the simple keyword, followed by a type keyword. Variables declared with this keyword can hold runtime-calculated values that do not change across bars. These variables cannot accept values qualified as “series”, even if those values remain consistent on every bar. The script below attempts to assign the result of a math.random() call to a rand variable declared with the simple keyword, causing a compilation error. The math.random() function returns a different value on each call, meaning its return type is “series float”. However, the simple keyword forces the rand variable to require a “simple float”, “input float”, or “const float” value: Pine Script® Copied //@version=6 indicator("Cannot assign a 'series' value demo") // This declaration causes an error. `math.random()` returns a "series float" value, but the `rand` variable  // requires a "float" value with the "simple" qualifier or a weaker one. simple float rand = math.random() plot(rand) Note Using the simple keyword is optional in most cases. However, the keyword is required to define exported library functions that accept only arguments with “simple” or weaker qualifiers and return “simple” results. See the Libraries page to learn more. series Values qualified as “series” provide the most flexibility in a script’s calculations. These values are available at runtime, and they are the only values that can change from bar to bar. All variables and function parameters that accept a “series” value also allow values with any other qualifier, because “series” is the highest qualifier in the qualifier hierarchy. All built-in variables that store bar information — such as open, high, low, close, volume, time, bar_index, and barstate.isconfirmed — always hold “series” values. The same applies to variables that store data from internal calculations that update from bar to bar, such as ta.vwap and ta.pvi. If an expression’s result can vary on any execution, it automatically inherits the “series” qualifier. Similarly, even if an expression returns an unchanging result on every bar, that result is still qualified as “series” if the calculation depends on at least one “series” value. Note Special types and user-defined types automatically inherit the “series” qualifier, meaning any calculation involving these types returns “series” results. Scripts cannot create instances of these types with weaker qualifiers such as “simple” or “const”. See the reference types section for more information. The following script calculates highest and lowest values from a sourceInput series and a “const float” value over lengthInput bars. The highest and lowest variables automatically inherit the “series” qualifier because the ta.highest() and ta.lowest() functions always return “series” results. These functions never return a value with a weaker qualifier, even if they calculate on a constant, because their source parameter is of the type “series float”: Pine Script® Copied //@version=6 indicator("'series' values demo", overlay = true) //@variable The source series to process in the `ta.highest()` call.  //          This variable's type is "series float", because `input.source()` returns "series" values. sourceInput = input.source(close, "Source") //@variable The number of bars to analyze. This variable's type is "input int". lengthInput = input.int(20, "Length") //@variable The highest `sourceInput` value over `lengthInput` bars. This variable's type is "series float". highest = ta.highest(source = sourceInput, length = lengthInput) //@variable The result of calculating the lowest value from a constant. This variable's type is also "series float".  //          The `source` parameter's type is "series float", so the function returns a "series float" value, regardless  //          of the argument's type qualifier.  lowest = ta.lowest(source = 100.0, length = lengthInput) plot(highest, "Highest", color.green) plot(lowest,  "Lowest",  color.red) Programmers can use the series keyword to explicitly define variables and parameters that accept “series” values. A script cannot use a variable declared using this keyword in any part of the code that requires “simple” or weaker qualifiers, even if the variable’s assigned value never changes. For example, the script below declares a lengthInput variable with the series keyword. Then, it attempts to use the variable as the length argument of a ta.ema() function call, causing a compilation error. Although the variable’s value comes from an integer input, the series keyword causes its type to become “series int” instead of “input int”. This type is not compatible with the ta.ema() function’s length parameter, because the strongest qualified type that the parameter accepts is “simple int”: Pine Script® Copied //@version=6 indicator("`series` keyword demo", overlay = true) //@variable Holds a value intended for use as the `length` argument in `ta.ema()`. //          Although the variable's value is from an input, its type is "series int" because the declaration uses the  //          `series` keyword. series int lengthInput = input.int(20, "Length") // This function call causes an error. The `length` parameter requires a "simple int", "input int", or "const int" // argument; it cannot accept a "series int" value.  ema = ta.ema(close, length = lengthInput) plot(ema) Types Types define the categories of values in a script and determine the kinds of functions and operations with which those values are compatible. Each type represents a different kind of data. The primary types available in Pine Script consist of the following: Fundamental types: int, float, bool, color, and string Enum types (enums) Special types: plot, hline, line, linefill, box, polyline, label, table, chart.point, footprint, volume_row, array, matrix, and map User-defined types (UDTs) void Fundamental types and enum types are also known as value types. Variables of these types directly hold values. Additionally, value types can inherit any type qualifier, depending on their use in the code. By contrast, special types and UDTs are reference types. Variables of these types do not store direct values; they hold references (sometimes referred to as IDs) that provide access to data stored elsewhere in memory. Instances of these types always inherit the “series” qualifier, regardless of how the script uses them. Note Pine Script also includes a set of unique value types. These types are compatible only with specific built-in parameters and operators. For example, plot.style_line and other plot.style_* constants are of the “plot_style” type. A value of this type is required only by the style parameter of the plot() function; other built-ins cannot use it. The only other way scripts can use plot.style_* constants is by assigning their values to separate variables or comparing them with the == or != operators. See the “Constants” section of the Reference Manual to learn about other unique types and their uses. Programmers can explicitly define the type of a variable, function parameter, or field by prefixing its declaration with a type keyword (e.g., int) or a type identifier (e.g., array). Specifying types in code is usually optional, because the compiler can automatically determine type information in most cases. However, type specification is required when: Declaring variables, user-defined function parameters, or UDT fields with initial na values. Defining the parameters of exported library functions, or declaring exported constants. Using qualifier keywords in a variable or parameter declaration. Declaring the first parameter of a user-defined method. Tip Even when specifying a type in the code is not mandatory, doing so helps to promote code readability. Additionally, using type keywords helps the Pine Editor provide relevant code suggestions. The example below calculates a moving average and detects when the close series crosses over the value. The script uses values of different fundamental types in its calculations. It includes the int, float, bool, color, and string keywords in its variable declarations to specify which type each variable accepts: Pine Script® Copied //@version=6 indicator("Type keywords demo", overlay = true) // The `string`, `int`, `float`, `bool` and `color` keywords are *optional* in the following variable declarations: string MA_TITLE    = "MA" int    lengthInput = input.int(100, "Length", minval = 2) float  ma          = ta.sma(close, lengthInput) bool   crossUp     = ta.crossover(close, ma) color  maColor     = close > ma ? color.lime : color.fuchsia // Specifying a type is required in this declaration, because the variable's initial value is `na`. // The `float` keyword tells the compiler that the variable accepts "float" values. var float crossValue = na // Update the `crossValue` variable based on the `crossUp` condition. 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) Note that: The first five variables in this script do not require type keywords in their declarations, but including them helps promote readability. However, the crossValue variable does require a specified type in its declaration because its initial value is na. Tip To confirm a variable’s type, hover over its identifier in the Pine Editor. The editor displays a pop-up window containing the name of the variable’s type and additional information about the variable. The sections below explain the different types available in Pine Script and how they work. Value types The types covered in the following sections are value types. These types directly represent values, such as numbers, logical conditions, colors, or text sequences. Value types are compatible with any type qualifier, depending on their use in the code. Additionally, value types, unlike reference types, are compatible with arithmetic and logical operators. int Values of the “int” type represent integers: whole numbers without fractional parts. Literal integers in a script are sequences of decimal digits without a decimal point (.). These literals can also include the unary + or - operators at the beginning of the sequence to specify their sign (positive or negative). Below are a few examples of literal integers: Pine Script® Copied 1 -1 750 Many built-in variables hold “int” values, including bar_index, time, timenow, dayofmonth, and strategy.wintrades. float Values of the “float” type represent floating-point numbers. In contrast to “int” values, “float” values represent the whole and fractional parts of a number. Literal floating-point values in Pine have two different formats: A sequence of decimal digits that contains a decimal point (.) to separate the number’s whole and fractional parts. This format can include a unary + or - operator at the beginning to specify the number’s sign. A number, with an optional decimal point, followed by e or E and an additional whole number. The number before and after e or E can include the unary + or - operator. This format represents a floating-point number in E notation. It translates to “X multiplied by 10 raised to the power of Y”, where “X” is the number before e or E, and “Y” is the number that follows. This format provides a compact way to represent very large or very small values. Below are a few examples of floating-point literals: Pine Script® Copied 3.14159    // Rounded value of Pi (π) -3.0 6.02e23    // 6.02 * 10^23 (a very large number) 1.6e-19    // 1.6 * 10^-19 (a very small number) The internal precision of “float” values in Pine Script is 1e-16. Floating-point values in Pine cannot precisely represent numbers with more than 16 fractional digits. However, note that comparison operators automatically round “float” operands to nine fractional digits. Many built-in variables store “float” values, including close, hlcc4, volume, ta.vwap, and strategy.position_size. Note Pine Script automatically converts “int” values to the “float” type if a script passes those values to variables or function parameters that require “float” values. Likewise, Pine converts “int” values to “float” in arithmetic or comparison operations that include a “float” operand. See the Type casting section to learn more. bool Values of the “bool” type represent the Boolean truth values of conditions (true or false). Scripts use these values in conditional structures and expressions to trigger specific calculations in the code. All comparison and logical operators return “bool” values. There are only two possible “bool” literals in Pine Script: Pine Script® Copied true    // true value false   // false value In contrast to most other types, values of the “bool” type are never na. Any expression or structure with the “bool” return type returns false instead of na if data is not available. For example, if a script uses the history-referencing operator to retrieve the value of a “bool” variable from a previous bar that does not exist, that operation returns false. Likewise, an if statement with a return expression of the “bool” type returns false if none of its local blocks activate. By contrast, expressions and structures with other return types, excluding void, return na if there is no available data. All built-in variables that represent conditions store “bool” values, including barstate.isfirst, chart.is_heikinashi, session.ismarket, and timeframe.isdaily. Note In contrast to some other languages, Pine Script does not automatically convert other types to the “bool” type in logical expressions. Scripts can explicitly convert “int” or “float” values to the “bool” type by using the bool() function. See the Type casting section to learn more about type conversions. color Values of the “color” type represent RGB colors, which scripts use to define the colors of chart visuals. Color literals in Pine have the format #RRGGBB or #RRGGBBAA, where: Each symbol after the number sign (#) represents a hexadecimal digit, which is a numeral from 0 to 9 or a letter from A (for 10) to F (for 15). Each set of two digits represents one of the color’s component values, ranging from 0 (00) to 255 (FF). The RR, GG, and BB parts represent the color’s red, green, and blue components, respectively. The last pair of digits, AA, is optional; it specifies the color’s opacity (alpha). If the pair is 00, the color is transparent. If FF or not specified, the color is fully opaque. All letters in the literal value can be uppercase or lowercase. Below are several examples of literal “color” values: Pine Script® Copied #000000      // Black #FF0000      // Red #00FF00      // Green #0000FF      // Blue #FFFFFF      // White #808080      // A shade of gray #3ff7a0      // A custom green-cyan color #FF000080    // 50% transparent red #FF0000ff    // Equivalent to #FF0000; fully opaque red #FF000000    // Completely transparent (invisible) red Pine Script also includes several built-in color constants, such as color.green, color.orange, color.red, and color.blue. Note that color.blue is the default color for plots, and it is the default value for several color properties of drawing types. The color namespace contains functions for retrieving color components, modifying colors, and creating new colors. For instance, scripts can use color.new() to define a copy of a built-in color with different transparency, or use color.rgb() to create a new color with specific red, green, blue, and transparency components. Note that the red, green, and blue parameters of the color.rgb() function expect a number from 0 to 255, where 0 means no intensity and 255 means maximum intensity. The transp parameter of color.rgb() and color.new() expects a value from 0 to 100, where 0 means fully opaque and 100 means completely transparent. Both functions automatically clamp arguments to these ranges, and they round the specified values to whole numbers. The example below creates a new “color” value with color.rgb(), modifies the color’s transparency based on the current day of the week with color.new(), and then displays the resulting color in the chart’s background: Pine Script® Copied //@version=6 indicator("`color.*()` functions demo") //@variable A color with custom red, green, and blue components. The variable's type is "const color". color BASE_COLOR = color.rgb(0, 99, 165) //@variable A calculated transparency value based on the current day of the week. This variable's type is "series int". int transparency = 50 + int(40 * dayofweek / 7) //@variable A modified copy of `BASE_COLOR` with dynamic transparency.  //          This variable's type is "series color", because its calculation depends on a "series int" value. color modifiedColor = color.new(BASE_COLOR, transparency) // Color the background using the `modifiedColor` value. bgcolor(modifiedColor) Note that: The value stored by BASE_COLOR is of the type “const color” because it depends on only “const” values. However, the modified color returned by color.new() is of the type “series color”, because the dayofweek variable used in the calculation has the “series” qualifier. To learn more about working with colors in Pine, see the Colors page. string Values of the “string” type contain sequences of encoded characters representing text, including letters, digits, symbols, spaces, or other Unicode characters. Scripts use strings in many ways, such as to define titles, express symbols and timeframes, create alerts and debug messages, and display text on the chart. Literal strings in Pine Script are sequences of characters enclosed by two ASCII quotation marks (") or apostrophes ('). For example: Pine Script® Copied "This is a literal string enclosed in quotation marks." 'This is a literal string enclosed in apostrophes.' Quotation marks and apostrophes are functionally similar when used as the enclosing delimiters of literal strings. A string enclosed in quotation marks can contain any number of apostrophes. Likewise, a string enclosed in apostrophes can contain any number of quotation marks. For example: Pine Script® Copied "It's an example" 'The "Star" indicator' A literal string can prefix some characters with the backslash character (\) to change their meaning. For example, applying a backslash to a quotation mark or apostrophe adds that character directly into a literal string’s sequence instead of treating the character as the end of the string: Pine Script® Copied 'It\'s an example' "The \"Star\" indicator" Applying a backslash to the n or t characters in a literal string creates escape sequences for multiline text or indentation respectively, which scripts can render using plot*() functions, Pine Logs, or some drawing types. For example, this string represents multiline text with a single word per line: Pine Script® Copied "This\nString\nContains\nOne\nWord\nPer\nLine" Scripts can use two operators, + and +=, to concatenate (combine) two separate strings. These operators create a new string containing the first operand’s character sequence followed by the second operand’s sequence. For example: Pine Script® Copied "This creates a " + "concatenated string." The str namespace contains several built-in functions that perform string-based calculations or create new strings. For example, the script below calls str.format() on each bar to create a formatted string containing representations of “float” price values, and it displays the result as multiline text in a label positioned at the bar’s high value: Pine Script® Copied //@version=6 indicator("Formatted string demo", overlay = true) //@variable A "series string" value containing representations of the bar's OHLC prices. string ohlcString = str.format("Open: {0}\nHigh: {1}\nLow: {2}\nClose: {3}", open, high, low, close) // Draw a label to display the `ohlcString` value as multiline text at the bar's `high` value. label.new(bar_index, high, ohlcString, textcolor = color.white) Several built-in variables that contain symbol and timeframe information store “string” values, e.g., syminfo.tickerid, syminfo.currency, and timeframe.period. For detailed information about Pine strings and the built-in str.*() functions, refer to the Strings page. To learn more about displaying text from strings, see the Text and shapes and Debugging pages. Enum types The enum keyword enables the creation of an enum, otherwise known as an enumeration, enumerated type, or enum type. An enum is a unique type that contains distinct named fields. These fields represent the members (i.e., possible values) of the enum type. Programmers can use enums to maintain strict control over the values accepted by variables, parameters, conditional expressions, collections, and the fields of UDT objects. Additionally, scripts can use the input.enum() function to create enum-based dropdown inputs in the “Settings/Inputs” tab. The syntax to declare an enum is as follows: [export ]enum [ = ] [ = ] ... [ = ] Where: export is the optional keyword for exporting the enum from a library, enabling its use in other scripts. See the Enum types section of the Libraries page to learn more about exporting enums. enumName is the name of the enum type. Scripts can use the enum’s name as the type keyword in variable declarations, parameter and field declarations, and the type templates of collections. field_* is the name of an enum field. The field represents a named member (value) of the enumName type. Each field must have a unique name that does not match the name or title of any other member in the enum. To retrieve an enum member, use dot notation syntax on the enum’s name (e.g., enumName.field_1). title_* is a “const string” value representing the title of an enum member. If the enum declaration does not specify a member’s title, its title is the “string” representation of its name. The input.enum() function displays enum member titles within a dropdown input in the “Settings/Inputs” tab. To retrieve the “string” title of an enum member, use the str.tostring() function on that member (e.g., str.tostring(enumName.field_1)). As with member names, each enum member’s title must be unique; it cannot match the name or title of another member in the same enum. The following code block declares an enum named maChoice. Each field within the declaration represents a unique, constant member of the maChoice enum type with a distinct title: Pine Script® Copied //@enum       An enumeration of named values for moving average selection. //@field sma  Specifies a Simple Moving Average. //@field ema  Specifies an Exponential Moving Average. //@field wma  Specifies a Weighted Moving Average. //@field hma  Specifies a Hull Moving Average. enum maChoice     sma = "Simple Moving Average"     ema = "Exponential Moving Average"     wma = "Weighted Moving Average"     hma = "Hull Moving Average" The following script uses the input.enum() function to create a dropdown input from our maChoice enum in the “Settings/Inputs” tab. The dropdown displays each field’s title as a possible choice. The value of maInput is the maChoice member corresponding to the selected title. The script compares the maChoice value inside a switch structure to determine which ta.*() function it uses to calculate a moving average: Pine Script® Copied //@version=6 indicator("Enum types demo", overlay = true) //@enum       An enumeration of named values for moving average selection. //@field sma  Specifies a Simple Moving Average. //@field ema  Specifies an Exponential Moving Average. //@field wma  Specifies a Weighted Moving Average. //@field hma  Specifies 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. //          This variable's type is "input maChoice". 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 corresponding to the selected enum member. 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` value. plot(selectedMA, "Selected moving average", color.teal, 3) See the Enums page and the Enum input section of the Inputs page to learn more about using enums and enum inputs. Reference types All the types covered in the following sections are reference types. These types do not directly represent values. Instead, scripts use them to create objects: logical entities that store data in a distinct location. Variables of reference types hold references, also known as IDs, that identify objects in memory and enable access to their data. In contrast to value types, which support any type qualifier, instances of a reference type automatically inherit the “series” qualifier, because each instance is unique. Additionally, because reference types do not represent values, they are not compatible with any arithmetic or logical operators. For advanced information about how these types differ from value types, see the Value vs. reference types section at the bottom of the page. plot and hline Pine Script uses the “plot” and “hline” types to display plots and horizontal levels on the chart. The plot() and hline() functions create instances of these types. Each call to these functions returns a reference (ID) to a specific “plot” or “hline” instance. Scripts can assign the references returned by these functions to variables for use with the fill() function, which colors the space between two displayed plots or levels. Note Only the plot() and hline() functions return usable IDs. All other plot-related functions — including plotchar(), plotshape(), plotarrow(), plotbar(), plotcandle(), barcolor(), and bgcolor() — return void, because they produce only visual outputs. Scripts cannot use data from these functions in other parts of the code. The following example calculates two EMAs, and then uses two plot() calls to display their values on the chart. It assigns the “plot” IDs returned by the function calls to variables, then uses those variables in a call to fill() to color the visual space between the displayed plots: Pine Script® Copied //@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 Holds the ID of the plot that displays the `emaFast` series. emaFastPlot = plot(emaFast, "Fast EMA", color.orange, 3) //@variable Holds the ID of the plot that displays the `emaSlow` series. emaSlowPlot = plot(emaSlow, "Slow EMA", color.gray, 3) // Color the space between the outputs from the "plot" objects referenced by `emaFastPlot` and `emaSlowPlot`. fill(emaFastPlot, emaSlowPlot, color.new(color.purple, 50), "EMA Fill") Note that: Pine does not include type keywords for specifying variables of the “plot” or “hline” type. Variables of these types never hold na, so Pine can always determine their type information automatically. A single fill() function call cannot use both a “plot” and “hline” ID. The function requires two IDs of the same type. In addition to displaying the complete history of “series” values on the chart, “plot” objects enable indicator-on-indicator functionality. Scripts can access values from another script’s plots for their calculations by using the input.source() function. See the Source input section of the Inputs page to learn more. Note In contrast to variables of all other reference types, variables of the “plot” or “hline” type cannot refer to different plots or levels across bars. All variables of these types must consistently hold the references returned by the same plot() or hline() calls on every execution. Additionally, the functions that create “plot” and “hline” objects work only in the global scope; scripts cannot use them in the local scopes of user-defined functions, conditional structures, or loops. Drawing types Pine’s drawing types serve as structures for creating drawing objects, which scripts use to display custom chart visuals. The available drawing types are line, linefill, box, polyline, label, and table. Each drawing type has an associated namespace with the same name. This namespace contains all the available built-ins for creating and managing drawing objects. For example, the label namespace contains all the built-in functions and variables for creating and managing labels. To create new instances of any drawing type, scripts can use the following *.new() functions from each type’s namespace: line.new(), linefill.new(), box.new(), polyline.new(), label.new(), and table.new(). Each of these *.new() functions creates a new drawing object on every call, and it returns the ID (reference) of that specific object. The other functions in the type’s namespace require this ID to access and delete, copy, or modify the drawing. For example, a script can use the ID returned by line.new() later to delete the underlying line object with line.delete(), copy the object with line.copy(), or update the drawing’s color with line.set_color(). For detailed information about lines, boxes, and polylines, see the Lines and boxes page. To learn more about tables and labels, see the Tables page and the Labels section of the Text and shapes page. Chart points The chart.point type is a special type that scripts use to generate chart points. Chart points are objects that contain chart coordinates. Scripts use information from these objects to position lines, boxes, polylines, and labels on the chart. Objects of the chart.point type contain three fields: time, index, and price. The time and index fields both represent horizontal locations (x-coordinates). The price field represents the vertical location (y-coordinate). Whether a drawing instance uses the time or index field from a chart point as an x-coordinate depends on the drawing’s xloc property. By default, drawings use the index field from a chart point and ignore the time field. Multiple functions in the chart.point namespace create chart points: The chart.point.new() function creates a new chart point containing specified time, index, and price values. The chart.point.now() function creates a chart point with a specified price value. The object’s time and index field automatically contain the time and bar_index values from the bar on which the function call occurs. The chart.point.from_index() function creates a chart point with only specified price and index values. The time field of the created object is na. Therefore, all chart points from this function are intended for use with drawings whose xloc property is xloc.bar_index. The chart.point.from_time() function creates a chart point with only specified price and time values. The index field of the created object is na. Therefore, all chart points from this function are intended for use with drawings whose xloc property is xloc.bar_time. The chart.point.copy() function creates a new chart point with the same time, index, and price values as the one referenced by the specified id argument. The following script draws a new line from the previous bar’s high value to the current bar’s low value on each execution. It also displays labels at both points of the line. The script sets the coordinates of the line and label drawings using data from chart points created by the chart.point.from_index() and chart.point.now() functions: Pine Script® Copied //@version=6 indicator("Chart points demo", overlay = true) //@variable References a chart point containing the previous bar's `bar_index` and `high` values. firstPoint = chart.point.from_index(bar_index - 1, high[1]) //@variable References a chart point containing the current bar's `bar_index`, `time`, and `low` values. chart.point secondPoint = chart.point.now(low) //@variable References a line connecting the coordinates from the objects referenced by `firstPoint` and `secondPoint`.  line myLine = line.new(firstPoint, secondPoint, color = color.purple, width = 3) // Draw a label at the `index` and `price` coordinates of the chart point referenced by `firstPoint`. // The label displays a string representing the first chart point's `price` value.   label.new(      firstPoint, str.tostring(firstPoint.price), color = color.green,      style = label.style_label_down, textcolor = color.white  ) // Draw a label at the `index` and `price` coordinates of the chart point referenced by `secondPoint`. // The label displays a string representing the second chart point's `price` value.  label.new(      secondPoint, str.tostring(secondPoint.price), color = color.red,      style = label.style_label_up, textcolor = color.white  ) Refer to the Lines and boxes page for additional examples of using chart points. footprint and volume_row The footprint and volume_row types are special data types that scripts use when requesting volume footprint information with the request.footprint() function. An object of the footprint type stores the available volume footprint data for a specific bar. A volume_row object stores the data for an individual row within a bar’s volume footprint. The only way to create objects of the footprint type is by calling the request.footprint() function. A call to the function returns either the reference (ID) of a footprint object that contains the retrieved volume footprint data for the current bar, or na if no footprint data is available. Scripts can use footprint IDs in calls to the functions from the footprint namespace to retrieve the calculated volume footprint data. Each function has an id parameter that requires a non-na ID of the footprint type. Some of the available footprint.*() functions return values representing overall metrics from a specific bar’s volume footprint: The footprint.buy_volume() function calculates the total “buy” volume for the volume footprint. The footprint.sell_volume() function calculates the total “sell” volume for the volume footprint. The footprint.total_volume() function calculates the sum of the footprint’s total “buy” volume and total “sell” volume. The footprint.delta() function calculates the volume footprint’s overall volume delta. The value represents the difference between the footprint’s total “buy” volume and total “sell” volume. A positive value indicates that the total “buy” volume is greater than the total “sell” volume, and a negative value indicates the opposite. The other footprint.*() functions retrieve the IDs of volume_row objects that contain data for individual rows in the volume footprint represented by a footprint object: The footprint.poc() function finds the Point of Control (POC) row of the volume footprint and returns the ID of a volume_row object containing data for that row. The POC is the footprint row that has the largest total volume. The footprint.vah() function finds the Value Area High (VAH) row of the volume footprint and returns a volume_row ID for that row. The VAH row is the highest one in the footprint’s Value Area. The footprint.val() function finds the Value Area Low (VAL) row of the volume footprint and returns a volume_row ID for that row. The VAL row is the lowest one in the footprint’s Value Area. The footprint.get_row_by_price() function searches the volume footprint to find the row whose price range includes a specified price level. If the price belongs to one of the footprint’s rows, the function returns the ID of the volume_row object that contains the data for that row. If the price level does not belong to any row in the footprint, the function returns na. The footprint.rows() function creates an array that contains the volume_row IDs for every row within the volume footprint, sorted in ascending order by the rows’ price levels. The first element refers to the volume_row object for the lowest row, and the last refers to the one for the highest row. The array’s type identifier is array. See the Collections section below to learn more about collection type identifiers. Note The va_percent argument of a request.footprint() call specifies the percentage of the total volume that the resulting footprint object uses for the volume footprint’s Value Area. Therefore, changes to the argument directly affect the results of footprint.vah() and footprint.val() calls that use the returned ID. The only way to access objects of the volume_row type is by calling any of the above functions using a valid footprint ID. Scripts can retrieve data from objects of this type for detailed footprint analysis by using their IDs in calls to the functions in the volume_row namespace. Each function has an id parameter that requires a non-na ID of the volume_row type: The volume_row.up_price() function returns the upper price level of the footprint row. The volume_row.down_price() function returns the lower price level of the footprint row. The volume_row.buy_volume() function calculates the total “buy” volume for the footprint row. The volume_row.sell_volume() function calculates the total “sell” volume for the footprint row. The volume_row.total_volume() function calculates the sum of the footprint row’s total “buy” volume and total “sell” volume. The volume_row.delta() function calculates the volume delta for the footprint row. The value represents the difference between the row’s “buy” volume and “sell” volume. A positive value indicates that the row’s “buy” volume exceeds its “sell” volume, and a negative value indicates the opposite. The volume_row.has_buy_imbalance() function checks whether the footprint row has a buy imbalance, based on the imbalance_percent argument of the original request.footprint() call. It returns true if the row’s “buy” volume exceeds the “sell” volume of the row below it by the specified percentage, and false otherwise. The volume_row.has_sell_imbalance() function checks whether the footprint row has a sell imbalance, based on the imbalance_percent argument of the original request.footprint() call. It returns true if the row’s “sell” volume exceeds the “buy” volume of the row above it by the specified percentage, and false otherwise. Note The imbalance_percent argument of a request.footprint() call determines the percentage difference that the resulting footprint uses for detecting volume imbalances. Changing the argument directly affects the results of the volume_row.has_buy_imbalance() and volume_row.has_sell_imbalance() calls that use volume_row IDs from the footprint object created by the request. See the request.footprint() section of the Other timeframes and data page for more information about footprint requests, and for examples that demonstrate how to use the footprint.*() and volume_row.*() functions to retrieve footprint data. To learn more about volume footprints and how they work, refer to the Volume footprint charts article in our Help Center. Collections Pine Script collections (arrays, matrices, and maps) are objects that store values or the IDs (references) of other objects as elements. Collection types enable scripts to group multiple values or IDs in a single location and perform advanced calculations. Arrays and matrices contain elements of one specific type. Maps can contain data of two types: one type for the keys, and another for the corresponding value elements. The array, matrix, and map namespaces include all the built-in functions for creating and managing collections. A collection’s type identifier consists of two parts: a keyword defining the collection’s category (array, matrix, or map), and a type template specifying the types of elements that the collection stores. The type template for array or matrix types consists of a single type keyword enclosed in angle brackets (e.g., for a collection of “int” values). The type template for a map type consists of two comma-separated keywords surrounded by angle brackets (e.g., for a map of “string” keys and “int” values). Below, we list some examples of collection type identifiers and the types that they represent: array — an array type for storing “int” values. array