Dashboard Tables

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.

How table rendering works

### 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:

  1. Performance — populating table cells on every bar is expensive. Running the cell updates only on the last bar ensures the script does not needlessly process hundreds or thousands of bars.
  2. Correct values — you want the table to show the current state of each indicator, not the value from some earlier bar. Running updates on `barstate.islast` guarantees you are always showing the most recent values.

### 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.

What to include in a dashboard

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:

Colour-coding cells for instant readability

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.

Practical dashboard patterns

### 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.

Table performance notes

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)