Create informational dashboards directly on your chart to display key metrics and strategy status.
Pine Script tables let you embed a persistent, customisable data panel directly on the chart. Unlike plots and labels which move with price, a table occupies a fixed screen position — top-right, bottom-left, or any corner you choose — and shows data without interfering with the price action itself.
The practical use case is displaying information that would otherwise require you to scroll across multiple indicators or consult external tools. A well-designed dashboard puts the current RSI reading, ATR value, trend direction, position status, and session context all in one visible location at a glance.
Tables are especially useful during live trading and strategy development because they make the internal state of your script visible — you can see exactly what your strategy sees, which helps both in validating logic and in understanding why a trade was or was not triggered.
### The table.new() call `table.new()` creates the table object and defines its structure: position, number of columns, number of rows, background colour, and border settings. It is declared with `var` so the table persists across bars rather than being recreated every bar.
### The barstate.islast guard `barstate.islast` is true only on the most recently confirmed bar — the last visible bar on the chart. All cell content updates happen inside this guard. This is essential for two reasons:
### table.cell() structure Each call to `table.cell()` populates one cell. The arguments are: table reference, column index, row index, text content, text colour, background colour, and text size. Columns and rows are zero-indexed.
A good dashboard shows information that is genuinely useful at a glance. The goal is to make your strategy's current decision context immediately visible without having to hunt across multiple indicator panes.
Useful things to display:
Things to avoid:
One of the most powerful features of tables is the ability to conditionally colour cells. Rather than reading a number and evaluating it, colour-coding lets you assess status instantly:
The example code already demonstrates this with RSI colouring. Extending the same pattern to trend direction and position status makes the dashboard genuinely useful in live trading.
### Strategy status dashboard Shows the current state of all filters and conditions your strategy checks before entering. If your strategy requires: HTF bullish AND RSI < 50 AND price above 200 EMA AND ADX > 20, the dashboard shows each of these as a green/red row. You can see at a glance which conditions are currently met.
### Multi-timeframe summary Request indicator values from several timeframes (`request.security()`) and display them in a single table — daily trend, 4H trend, 1H signal direction, all visible simultaneously.
### Trade management panel Once in a trade, show the entry price, current stop level, current target, unrealised P&L, and R-multiple (how many R units the trade is currently showing). This replaces the need to manually calculate these from the chart during live trading.
Tables are evaluated on every bar, but the `barstate.islast` guard ensures cell updates only run once (on the final bar). The `var` declaration for the table object ensures the table is not destroyed and recreated each bar. Both of these are important for keeping script execution time reasonable, especially on larger data sets.
//@version=6
indicator("Dashboard Table", overlay=true)
// Create a table in the top-right corner
var table dashboard = table.new(position.top_right, 2, 6, bgcolor=color.new(color.black, 80), border_width=1)
// Data
rsiVal = ta.rsi(close, 14)
atrVal = ta.atr(14)
ema20 = ta.ema(close, 20)
ema50 = ta.ema(close, 50)
trend = ema20 > ema50 ? "BULLISH" : "BEARISH"
trendCol = ema20 > ema50 ? color.green : color.red
if barstate.islast
// Headers
table.cell(dashboard, 0, 0, "Metric", text_color=color.white, text_size=size.small)
table.cell(dashboard, 1, 0, "Value", text_color=color.white, text_size=size.small)
// Data rows
table.cell(dashboard, 0, 1, "RSI(14)", text_color=color.gray, text_size=size.small)
table.cell(dashboard, 1, 1, str.tostring(rsiVal, "#.##"), text_color=rsiVal > 70 ? color.red : rsiVal < 30 ? color.green : color.white, text_size=size.small)
table.cell(dashboard, 0, 2, "ATR(14)", text_color=color.gray, text_size=size.small)
table.cell(dashboard, 1, 2, str.tostring(atrVal, "#.####"), text_color=color.white, text_size=size.small)
table.cell(dashboard, 0, 3, "EMA 20", text_color=color.gray, text_size=size.small)
table.cell(dashboard, 1, 3, str.tostring(ema20, "#.##"), text_color=color.white, text_size=size.small)
table.cell(dashboard, 0, 4, "Trend", text_color=color.gray, text_size=size.small)
table.cell(dashboard, 1, 4, trend, text_color=trendCol, text_size=size.small)
table.cell(dashboard, 0, 5, "Volume", text_color=color.gray, text_size=size.small)
table.cell(dashboard, 1, 5, str.tostring(volume, "#"), text_color=color.white, text_size=size.small)