Tables
Introduction
Tables are drawing objects that display text and colors within organized cells. They visually arrange cells in columns and rows, similar to a spreadsheet. Unlike other visuals in Pine Script®, tables do not anchor to specific bars or chart coordinates. Instead, scripts display each table drawing at one of nine fixed positions within a chart pane – one of the four corners, one of the midpoints between the corners, or the space’s center – or stack the tables vertically in the bottom panel. Tables do not move or vanish as users scroll or zoom on the chart; they consistently remain visible at their position until the script directly modifies or deletes them.
A script creates and displays tables in two distinct steps:
- First, it must call the table.new() function to create a table object. The function call defines specific base properties of the table, including the table’s position, the number of columns and rows, and the default border and background colors, then returns the table’s reference (ID).
- After creating the table object, the script can pass its ID in calls to the table.cell() function to populate and display each cell. Each call to the function defines the properties for one specific cell, including the cell’s text, colors, formatting, and size. The overall size of the table depends on the width and height of each displayed cell. See the Sizing table cells section to learn more.
Scripts can modify all base properties of a table, excluding the force_overlay setting, and the number of columns and rows, by passing the object’s ID to the built-in table.set_*() functions. Likewise, they can modify all properties of individual cells by passing the ID to either the table.cell_set_*() functions or to additional calls to the table.cell() function. Scripts can also merge a specific subset of a table’s cells via the table.merge_cells() function. Refer to the Modifying table and cell properties and Merging cells sections to learn more about these functions and how to use them.
A single script can display a maximum of nine tables at once – one for each available position. If a script creates a table using the same position as an existing table, it replaces the existing table with the new one. The contents of each table always reflect the final state of the table as of the last chart bar. Past states of the table, or any replaced tables, are never visible.
Creating and displaying tables
To create a table, start by calling the table.new() function. The function creates a table object with specified base properties and returns its ID. The function’s signature is as follows:
table.new(position, columns, rows, bgcolor, frame_color, frame_width, border_color, border_width, force_overlay) → series tableWhere:
- The
position,columns, androwsparameters require arguments. Supplying arguments to all other parameters is optional. - The
positionparameter specifies the table’s position on the chart. It accepts one of the followingposition.*constants: position.top_left, position.top_center, position.top_right, position.middle_left, position.middle_center, position.middle_right, position.bottom_left, position.bottom_center, or position.bottom_right. - The
columnsparameter requires an “int” value specifying the maximum number of columns that the table can display. - The
rowsparameter requires an “int” value specifying the maximum number of rows that the table can display. - The
bgcolorparameter sets the table’s main background color. If not specified, the default background color is na (invisible). If the table’s cells also have specified background colors, those colors overlay onto the table’s main background. - The
frame_colorparameter sets the color of the table’s outer frame. The default color is na. - The
frame_widthparameter sets the width of the frame, in pixels. It accepts an “int” value greater than or equal to 0. A value of 0 (the default) makes the frame invisible. - The
border_colorparameter sets the color of the inner borders between each table cell. The default color is na. - The
border_widthparameter sets the width of the inner borders, in pixels. It does not affect the table’s outer frame. It accepts an “int” value greater than or equal to 0. A value of 0 (the default) makes the borders invisible. - The
force_overlayparameter accepts a “bool” value specifying which pane the table occupies when displayed on the chart. Iftrue, the table appears on the main chart pane, even if the script occupies a separate pane. Iffalse, it appears in the script’s pane. Users can move the table into a tab in the chart’s bottom panel, regardless of theforce_overlayargument, by selecting “Move tables to bottom” from the script’s “More” menu. See the Displaying tables in the bottom panel section to learn more.
The table object created by a table.new() call does not initially display any visuals, because it does not contain data for any cells. For the table to display visuals, the script must call the table.cell() function to define and populate at least one cell. The function has the following signature:
table.cell(table_id, column, row, text, width, height, text_color, text_halign, text_valign, text_size, bgcolor, tooltip, text_font_family, text_formatting) → voidWhere:
- The
table_id,column, androwparameters require arguments. Supplying arguments to all other parameters is optional. - The
table_idparameter requires the ID of the table object. - The
columnparameter specifies the column index of the cell to populate. It requires an “int” value from 0 to one less than thecolumnsargument in the table.new() call. A value of 0 refers to the first column, 1 refers to the second, and so on. - The
rowparameter specifies the row index of the cell to populate. It requires an “int” value from 0 to one less than therowsargument in the table.new() call. A value of 0 refers to the table’s first row, 1 refers to the second, and so on. - The
textparameter accepts a “string” argument specifying the text to display in the table’s cell. The default is an empty string. - The
widthparameter accepts a “float” value specifying the base width of the cell. A value greater than 0 sets the width as a percentage of the pane’s width if the table is on the chart, or as a proportion of the table’s total width if the table is in the bottom panel. A value of 0 (the default) specifies that the width automatically adjusts based on the cell’s text. The cell’s final width is the maximum of the base width and the widths of all other cells in the same column. - The
heightparameter accepts a “float” value specifying the base height of the cell. A value greater than 0 sets the height as a percentage of the pane’s height if the table is on the chart, or as a proportion of the total allocated height if the table is in the bottom panel. A value of 0 (the default) specifies that the height automatically adjusts based on the cell’s text. The cell’s final height is the maximum of the base height and the heights of all other cells in the same row. - The
text_colorparameter specifies the color of the displayed text. The default is#363a45. - The
text_halignparameter sets the horizontal alignment of the cell’s text. The possible arguments are text.align_left, text.align_center, and text.align_right. The default is text.align_center. - The
text_valignparameter sets the vertical alignment of the cell’s text. The possible arguments are text.align_top, text.align_center, and text.align_bottom. The default is text.align_center. - The
text_sizeparameter sets the size of the cell’s text. It accepts an “int” value greater than or equal to 0 for specifying the size in typographic points, or one of thesize.*built-in “string” constants for specifying a preset size. Thesize.*constants and the equivalent “int” sizes are as follows: size.auto (0), size.tiny (8), size.small (10), size.normal (14), size.large (20), and size.huge (36). The default is size.normal. - The
bgcolorparameter controls the background color of the cell. The specified color overlays onto the table’s main background defined by the table.new() call. If thebgcolorargument of the table.cell() call is na (the default), the cell displays the table’s main background color. If the argument is a “color” value with zero transparency, the cell displays that color instead of the main background color. If the argument represents a partially transparent color, the cell displays a mixture between that color and the table’s main background color. - The
tooltipparameter accepts a “string” value that defines the tooltip text for the cell. If not na or an empty string, the cell displays the specified text within a tooltip when the user hovers over the cell. The default is an empty string. - The
text_font_familyparameter specifies the font family of the cell’s main text. The possible arguments are font.family_default and font.family_monospace. The default is font.family_default. - The
text_formattingparameter sets the formatting behavior of the displayed text. It accepts one of thetext.format_*constants (text.format_none, text.format_italic, or text.format_bold) or an addition expression that combines formatting settings (e.g.,text.format_bold + text.format_italicfor bold, italicized text). The default is text.format_none.
The following example script demonstrates how to create and display a basic table. It displays a fixed, single-cell table at an input-specified position on the first chart bar. The script first calls the table.new() function to create the table object, then assigns the returned ID to the persistent displayTable variable. Then, within a once structure, the script passes the variable to a table.cell() call to populate the table’s cell with a formatted string and display the result on the chart:

Note that:
- The table.new() call does not display any visuals; it only creates the table object and defines the object’s base properties. The table displays data only after the script populates its cell with the table.cell() call.
- The script assigns the table.new() call to a persistent variable declared with the var keyword to prevent repeated table creation after the first bar. Without using the keyword, an alternative way to achieve the same effect in our example is to move the variable declaration into the once structure.
- The string input in the script displays the “string” values of the
position.*constants as options. Programmers can use these strings directly in table.new() calls to set a table’s position. - The table does not move as the user scrolls or zooms on the chart. It remains fixed at the input-specified pane position, even while viewing the first chart bar.
- The table.cell() call in this script does not specify a
widthorheightargument. Therefore, the script sets the table to use the minimum width and height that accommodates the cell’s text. See the Sizing table cells section below for more information. - This script creates and populates the table on only a single bar because the data it displays does not change across bars. To display dynamic data in tables, the typical best approach is to use the table.cell() function or other
table.*()setter functions inside conditional structures that execute code on the last available bar (i.e., where the barstate.islast value istrue). See the Modifying table and cell properties section to learn more about these functions and how to use them efficiently.
A script must call the table.cell() function or other setter functions for each cell to display. If a script does not define data for a cell at least once, the table automatically excludes that cell from its display. Likewise, if the script does not define cell data for an entire row or column, the table automatically omits that row/column when possible.
For example, the simple script below creates a table that can display up to three rows and three columns. It then uses one call to the table.cell() function to populate the table’s middle cell, but does not define data for the other cells. Consequently, the table displays only one cell rather than nine:

Note that:
- Most built-in functions for reference types, including tables, are callable as functions or methods. This script calls the table.cell() function using method syntax for convenience. We also use method syntax across other examples on this page.
To configure the table to display all possible cells, the script must call the table.cell() function separately for each one. The modified script below calls the function within nested for loops. The outer loop iterates over the table’s column indices, and the inner loop iterates over the row indices. Each iteration of the inner loop calls the function to populate the cell at the current column and row. The table in this version displays all nine cells, because the script directly defines data for each one:

Sizing table cells
The total width and height of a table depend on the width and height of each displayed cell. The width, height, text, and text_size arguments in each call to the table.cell() function and the corresponding table.cell_set_*() functions affect the visual sizes of table cells. When a table displays on the chart, the script sizes each cell using the following logic:
- If the
widthorheightvalue specified for a cell is 0, the script sets the initial width or height of the cell to fit the horizontal or vertical span of the text, which depends on both thetextandtext_sizevalues. - If the
widthorheightvalue for a cell is a positive number, the script sets the initial width or height as a percentage of the chart pane’s total width or height. For example, awidthvalue of 50 specifies that the cell is at least half (50%) of the width of the chart pane. The specified width/height takes precedence over the cell’s text; if the values are too small to fit the entire text, the cell shows only a truncated portion of the text. - After determining the initial width and height of a cell, the script checks the width of all other cells in the same column and the height of all other cells on the same row. If necessary, it then increases the cell’s width or height to align with the neighboring cells.
- The directions in which the table expands the width and height of its cells depend on the table’s position in the pane. For instance, a table in the middle-center position of the pane expands in all directions, whereas a table in the bottom-right corner expands leftward and upward.
The following script provides an interactive way to understand the sizing behavior of table cells on the chart. The script creates a single-cell table that displays the text from a text area input. It also includes other inputs to specify the text size, the cell width, the cell height, and the table’s chart position. The table expands in all possible directions from the input position based on the span of the rendered text, or the specified “Cell width” and “Cell height” inputs if their values are greater than 0:

Note that:
- The script uses a multiline literal string to set the text area input’s default value. The string automatically includes the newline control character for each separate line of text, without requiring the
\nescape sequence. See the Multiline strings section of the Strings page for more information.
When explicitly sizing table cells with nonzero width and height arguments, we recommend configuring each cell’s dimensions carefully to ensure the total width and height of the table do not exceed 100% of the available space, and configuring the text in each cell to avoid truncation. If a table exceeds the available display space, or if a cell’s width or height percentage is too small to display the specified text, there is no way to view all of the table’s contents when displaying it on the chart. However, users can view all the contents by moving the table to the bottom panel. See the next section below to learn more.
A common technique to fit multi-cell tables into a specified space is to divide the required total width and height of a table by the number of columns and rows it contains. For instance, if a table contains five columns, and the required total width is 50% of the pane, set each cell’s width value to 10 (50 / 5). If a table requires non-uniform cell sizes, one possible technique to set dimensions is to compute normalized width and height weights that sum to one, then multiply the weights by the table’s total required width/height percentage.
For example, the advanced script below includes a user-defined function named calcCellDims(), which creates an array containing the cell width or height percentages for fitting a specified total pane percentage. The function divides elements from an array of arbitrary weights by the array’s sum to normalize them, then multiplies the elements by a total percentage to calculate the final width or height values. The script uses the function to calculate cell widths and heights for a multi-cell table on the chart. Each cell in the table displays formatted text indicating the cell’s final width and height percentages:

Displaying tables in the bottom panel
When a user applies a table-drawing script to the chart, the tables appear in the script’s pane by default, or in the main chart pane if the force_overlay argument of the table.new() call is true. Users can then optionally display the tables within a tab in the chart’s bottom panel by selecting the “Move tables to bottom” option from the “More” menu in the script’s status line:

Selecting the option moves all tables drawn by the script into the same tab in the bottom panel, regardless of which chart pane each table occupies. The user can move the tables back onto the chart at any time by selecting “Move tables to chart” from the script’s “More” menu, or from the context menu opened by a dropdown arrow in the tab’s header:

Users can move the tables from more than one script into the bottom panel’s tab. However, the panel displays the tables from only one script at a time. To specify which script’s tables to analyze, select the script name from the list in the tab’s context menu:

Tables use different positioning and sizing rules in the bottom panel, and often appear differently from how they appear on the chart. Key differences in the display behaviors include the following:
- The panel stacks all tables vertically based on the order in which the script creates them, regardless of the
positionorforce_overlayargument in each table.new() call. The first table created by the script automatically appears at the top, and the last table appears at the bottom. - Each table automatically stretches horizontally to at least the width of the panel, regardless of the
widthargument in the table.cell() calls. - The cells of tables in the bottom panel do not truncate displayed text to fit a specified width or height. They fit the entire text when possible. If a table’s text is too tall or wide to fit within the panel’s visible space, the panel adds vertical or horizontal scroll bars as necessary to make the full text viewable.
- If a table contains more than one column, a positive
widthargument in each table.cell() call specifies the width of the cell as a relative proportion of the table’s total width rather than a percentage of the panel’s width. For example, suppose a two-column table defines a width of 10 for the cells in the first column and a width of 30 for those in the second. In this case, the first column spans approximately one-fourth (10 / (10 + 30)) of the table’s total width, and the second spans approximately three-fourths (30 / (10 + 30)) of the width. - If at least one table.cell() call includes a positive
heightargument, the corresponding table automatically stretches other necessary rows vertically to fit the allocated display space in the pane, and it treats the specified value as a relative proportion of the space’s height rather than a direct percentage. Otherwise, the table automatically uses the minimum amount of vertical space required to display the specified text. - The available vertical space for each table depends on the total number of tables and the rows that they contain. If a specified height proportion is too small for the text on a row, the table automatically adjusts its row height to fit the text.
The following example demonstrates how tables behave differently when moved to the bottom panel. The script below creates up to nine tables on the first bar, one for each position.* constant. It stores each position.* string in an array, then loops through a slice of the array. On each loop iteration, the script calls table.new() to create a table that supports two columns and three rows, then populates it with table.cell() calls using defined widths and heights. The first row in each table includes a number indicating the table’s creation order, where 0 refers to the first one, as well as the string indicating the table’s chart position:
Note that:
- The script uses the table.merge_cells() function to merge the cells on each table’s first row. The merged cell inherits all properties, excluding width and height, from the first cell in the merge range (at column 0, row 0). See the Merging cells section below to learn more about this function.
When we apply the script to a chart, it displays all the tables in a separate chart pane at their specified positions by default. The total defined width and height of each table is 30. Therefore, when displaying in the pane, each table occupies approximately 30% of the pane’s total width and height:

If we select “Move tables to bottom” from the script’s “More” menu, all tables move into a tab in the chart’s bottom panel, and their display behavior changes. Rather than appearing at the same relative positions in the panel, the tables stack vertically in order from first to last. Each table stretches horizontally to fit the tab, and the tab automatically allocates the necessary amount of vertical space required to display each one:

Note that:
- The columns in each table span a proportional amount of the table’s width when they display in the panel. For instance, the first table defines a width of 7 (5 + 2) for the first column and 23 (30 - 7) for the second. Therefore, the first column spans approximately 7 / 30 of the total width, and the second spans approximately 23 / 30 of the total width.
- The rows in each table also span a proportional amount of the total allocated height when possible. However, the available height for each table depends on the total number of tables and their contents. If the bottom panel displays more than one table, it automatically adjusts the row heights to fit the space, because the table.cell() calls specify
heightarguments. If the panel displays enough tables to require scrolling, it sets each row to use the minimum height that fits the displayed text. To see this behavior in action, adjust the “# of tables to draw” input in the script’s “Settings/Inputs” tab.
Tables in the bottom panel also process text differently from those on the chart. When a table displays on the chart, there is no direct way to interact with the table’s text. Users can only view the text or hover over cells to inspect defined tooltips. By contrast, when a table displays in the bottom panel, users can select any of the main text in the table and copy it to their clipboard. For instance, in the following image, we selected all the text in the first table from our example script. We can then copy the entire text at once and paste it elsewhere:

Note that:
- This behavior applies only to the main text in a table’s cells. Users can view any tooltips in a bottom-panel table by hovering over its cells, just like with tables on the chart, but they cannot select the text from those tooltips.
Modifying table and cell properties
The table namespace features multiple setter functions, which enable scripts to update the data and visual properties of a table and its cells over time. Programmers often use these functions when creating tables that display dynamic “series” data, or when modifying the appearance of a table in response to specific conditions.
The table.set_*() functions modify specific base properties of a table, including its position, frame settings, and border settings. The available table.set_*() functions are table.set_bgcolor(), table.set_border_color(), table.set_border_width(), table.set_frame_color(), table.set_frame_width(), and table.set_position(). Each of these functions has the following general signature:
function_name(table_id, table_property) → voidWhere:
function_nameis the function’s identifier.- The
table_idparameter requires the ID of the table object. table_propertyrefers to the table’s base property to update. The corresponding parameter name in each function matches one of the parameters in the table.new() function (e.g.,positionfor table.set_position()).
For updating specific table cells, scripts can use either the table.cell() function or the table.cell_set_*() functions. Each successive call to these functions overwrites the current properties for a specific cell. The table.cell() function overwrites all cell properties at once. In essence, it redefines the cell entirely on each call. By contrast, each table.cell_set_*() function updates only one specific cell property, such as the cell’s width, height, text, or background color, while leaving all other properties unchanged.
The available table.cell_set_*() functions include table.cell_set_bgcolor(), table.cell_set_width(), table.cell_set_height(), table.cell_set_text(), table.cell_set_text_color(), table.cell_set_text_size(), table.cell_set_text_halign(), table.cell_set_text_valign(), table.cell_set_text_font_family(), table.cell_set_text_formatting(), and table.cell_set_tooltip(). The general signature for each of these functions is as follows:
function_name(table_id, column, row, cell_property) → voidWhere:
function_nameis the function’s identifier.- The
table_idparameter requires the ID of the table object. - The
columnparameter requires the column index of the cell to modify. The index of the first column is 0, and the index of the last is one less than thecolumnsargument of the table.new() call. - The
rowparameter requires the row index of the cell to modify. The index of the first row is 0, and the index of the last is one less than therowsargument of the table.new() call. cell_propertyrefers to the cell property to update. The corresponding parameter name in each function matches one of the parameters of the table.cell() function (e.g.,textfor table.cell_set_text()).
To learn more about the table.cell() function and its signature, refer to the Creating and displaying tables section above.
When using any of these functions to update a table, remember that only the latest state of the table, as of the last available bar, is visible to the user. All previous states of a table are never visible. Therefore, updating tables before the last available bar is typically unnecessary. Furthermore, updating tables across a chart’s history can significantly increase the script’s runtime requirements, especially if the tables display many dynamic strings or colors.
The most efficient way to populate a table with dynamic data is to call the table.set_*(), table.cell(), or table.cell_set_*() functions only on the bars where the output is visible, i.e., the last historical bar or the current realtime bar. Restricting these calls to the latest available bar avoids consuming resources on repeated display updates that users cannot view. The simplest approach to achieve this effect is to place the setter calls that must work across bars into an if structure that uses barstate.islast in its condition. If the table’s outputs depend on data from previous historical bars, use variables, collections, or history-referencing operations to make necessary data available for the setter calls on the last bar.
The following example script calculates the change in the close series over multiple lengths and displays formatted values in a two-column, nine-row table. It performs all calculations on the last bar, within an if structure that uses barstate.islast as the condition. On the first bar where the condition is true, the script executes multiple table.cell() calls within a nested once structure to set the initial properties of all the table cells. Then, within a loop that executes on all bars where the if structure executes, the script calculates the change values and updates all cells after the first in the second column. The script calls the table.cell_set_text() function to update each cell’s text, and it calls table.cell_set_bgcolor() to update the background color:

Note that:
- This script can produce the same result by calling the table.cell() function instead of the table.cell_set_text() and table.cell_set_bgcolor() functions. However, each call to the function overwrites all properties of a cell. If a property is not specified, the table.cell() function uses the default property when updating a cell. Therefore, all necessary properties must be explicitly defined in each call. For instance, if we replace the two
table.cell_set_*()calls with a single table.cell() call, the script displays the same result only if the call includestext,bgcolor, andtext_colorarguments.
Clearing cells
Scripts can clear specific cells within a table by using the table.clear() function. The function deletes the properties for one or more cells within a rectangular region from a starting column and row to an ending column and row. The function’s signature is as follows:
table.clear(table_id, start_column, start_row, end_column, end_row) → voidWhere:
- The
table_idparameter requires the ID of the table object. - The
start_columnparameter requires the column index of the first cell to clear. The argument must be less than or equal to theend_columnvalue. - The
start_rowparameter requires the row index of the first cell to clear. The argument must be less than or equal to theend_rowvalue. - The
end_columnparameter requires the column index of the last cell to clear. The argument must be greater than or equal to thestart_columnvalue. - The
end_rowparameter requires the row index of the last cell to clear. The argument must be greater than or equal to thestart_rowvalue.
This function is often useful if a script must dynamically reduce the number of cells in a table’s display. After a script clears a table cell with this function, the cell’s data is no longer defined. Therefore, the table automatically excludes the cell from its display. If the call clears an entire row or column, the table automatically omits that row/column when possible. To display a cell after clearing it, the script must repopulate the cell by calling the table.cell() or table.cell_set_*() functions.
The following example script demonstrates the behavior of cleared cells. The script populates and then clears a table in 50-bar cycles, starting on the last bar. The script first creates a table that supports 10 columns and 5 rows. It then calls the table.cell() function to populate one specific cell on each successive bar. After the script populates all 50 cells, it calls the table.clear() function on the next bar to clear the cells, then begins populating the table again:

Note that:
- The table does not initially display any data after each table.clear() call, because the call in our example erases the properties of every cell. The table displays data again only after the table.cell() call. The table gradually expands its display across bars to show all columns and rows as the script redefines them.
Merging cells
Scripts can merge multiple cells within a table into a single cell by calling the table.merge_cells() function. The function merges all cells within a rectangular region from a starting column and row to an ending column and row. After a table merges cells, it cannot separate those cells or merge them with other cells. The final merged cell inherits all properties from the first cell in the merge range, excluding its width and height. The dimensions of the merged cell depend on the width and height of all neighboring cells in the columns and rows covered by the range.
The signature of the table.merge_cells() function is as follows:
table.merge_cells(table_id, start_column, start_row, end_column, end_row) → voidWhere:
- The
table_idparameter requires the ID of the table object. - The
start_columnparameter requires the column index of the first cell to merge. The argument must be less than or equal to theend_columnvalue. - The
start_rowparameter requires the row index of the first cell to merge. The argument must be less than or equal to theend_rowvalue. - The
end_columnparameter requires the column index of the final cell to merge. The argument must be greater than or equal to thestart_columnvalue. - The
end_rowparameter requires the row index of the final cell to merge. The argument must be greater than or equal to thestart_rowvalue.
The following example script demonstrates the behavior of merged cells. On the first bar, the script creates a five-column, five-row table with a white background and a black border and frame, then populates its cells with single-character alphabetical strings from "A" to "Y". The script calls the table.merge_cells() function as a method to merge all cells from an input start column and row to an input end column and row. It also uses the table.cell_set_bgcolor() function to color the background of the first cell in the merge range blue:

Note that:
- The merged cell automatically displays the text and background color of the first cell in the merge range. It ignores the properties of all other cells in the range, even though the script also defines them with the looped table.cell() call.
- If the start and end column indices are equal, as well as the start and end row indices, the table.merge_cells() call does not affect the table’s appearance, because the merge range spans only a single cell. If the start row or column index is greater than the end index, a runtime error occurs, because tables merge cells exclusively from top to bottom and left to right.
- The table.merge_cells() function can merge cells before or after the script defines their properties with the table.cell() function. In this example, we placed the call before the loop that executes the table.cell() calls. Moving the call after the loop achieves the same effect.
Deleting and replacing tables
Tables persist indefinitely on the chart or in the bottom panel until a script deletes them. Scripts can delete a table, if necessary, by doing either of the following:
- Calling the table.delete() function. Each call explicitly deletes a table object and removes its ID. Any variables or collections that reference the table store na after the call. This function is most useful if a script must display a table on only some of the latest bars.
- Calling the table.new() function with the same
positionargument as a previous call. Each successive call to the function automatically deletes the existing table using the specified position and replaces it with a new table object that displays cells at that position. Replacing a table can be useful if a script must increase the number of cells in a table or dynamically change merged cell ranges.
The following example script uses tables to create a right-to-left scrolling display of colors indicating the direction of changes in the close series over the latest 30 bars. The script uses an array as a queue to store the change direction values. Then, on the last historical bar or the current realtime bar, it creates a new table object and loops through the array to populate the cells. It populates the second row’s cells by calling the table.cell() function for each element that does not match the previous element. For consecutive elements that have the same value, the script calls table.merge_cells() instead to merge all the corresponding cells in the second row, preventing the table’s black border from appearing between each one.
Each execution of the table.new() call automatically replaces the table from the previous bar with a new one, because the call uses an unchanging position argument. Replacing the table on each realtime bar is useful for our intended effect in this case, as the script cannot dynamically overwrite the merged cell ranges in a single table across bars:

Tips
Follow these tips to use tables efficiently, and to display updates to tables dynamically in strategies.
Updating tables only when necessary
As mentioned in the introduction, tables often require more computational resources than other types of drawings, especially if a script creates or updates them frequently or populates many cells. In some cases, excessive table creation or updates can significantly increase a script’s runtime requirements.
To minimize resource use when working with tables, ensure the script creates and updates them only when necessary. Below are the best practices for optimizing table performance:
- If the table or cell properties do not require changes across bars, place all the required
table.*()function calls inside a conditional structure that executes its local block on a single bar. All table visuals persist across executions until explicitly deleted or replaced. Therefore, callingtable.*()functions repeatedly is not necessary to display fixed visuals continuously. - If any table or cell properties do require changes across bars, place the required
table.set_*(), table.cell(), ortable.cell_set_*()calls inside a conditional structure that executes only on the last available bar, where the result is visible. Users cannot view the past states of tables. Therefore, updating tables repeatedly across history is an unnecessary waste of runtime resources. - When creating and modifying tables, it is typically best to assign the table.new() call to a persistent variable declared with the var keyword to prevent repeated table creation. Each successive call with the same
positionargument replaces any existing table at that position with a new one to populate, as explained above. Replacing tables when not required can cause excessive resource use. - If a script must replace a table over time to change its cell count, merged cells, etc., keep all successive table.new() calls inside a conditional structure that executes only on the last bar. Repeatedly recreating and repopulating tables across history can significantly elevate resource use.
Displaying dynamic tables in strategies
Strategies differ from indicators in the way they execute across a dataset. By default, they execute only on the closing tick of each bar, regardless of whether that bar has a historical or realtime state. Consequently, with this behavior, if a strategy populates tables with data that can fluctuate during realtime bars, the tables lag behind the latest data while a bar is open and show new results only when the bar closes.
For a strategy to update its tables or other drawings on each update to an open realtime bar, the script must execute on each new tick – not just each bar’s close. Programmers can configure a strategy to execute on each new tick by default by including calc_on_every_tick = true in the strategy() declaration statement. Script users can also override a strategy’s default calculation behavior and enable executions on each new tick by selecting “On realtime bar tick” in the strategy’s Script execution settings.
The example strategy below places market orders to enter and close trades based on the value of a 14-length RSI. The script also draws a two-column, two-row table to display the current strategy.equity value and its one-bar change. The script includes calc_on_every_tick = true in the strategy() statement, enabling the table to update after each tick in a realtime bar by default. To promote consistency in the order logic across historical and realtime bars, the script uses the order placement commands inside an if structure that executes only when the barstate.isconfirmed value is true: