KDB knowledge

20. Charts and dashboards

What you will learn

Everything runs in the web console (chapter 6). No internet is needed. The charts are drawn by KDB's own code: plain JavaScript files in the console folder (web/charts next to kdb, web/console/charts in the source). No chart library is loaded from the internet.

Before you start

Load the sample data (see the quick start) and start the server:

kdb serve demo

Open http://127.0.0.1:9900. The examples in this chapter use the tables ochl_day, ochl_minutes, live_data and securities of the sample data.

At the top left of the console is the Home menu. Home is the SQL page you know from chapter 6. Dashboard is new. The button shows where you are, for example Home / Dashboard. The console remembers which page you used last.

Part 1: Charts from a query

Your first chart

  1. Choose Home in the Home menu at the top left.

  2. Type this query and press Ctrl+Enter:

    SELECT time, open, high, low, close, volume FROM ochl_day WHERE symbol = 'CITY' ORDER BY time;
  3. Click Chart above the result.

Picture: a dark chart area. Green and red candles go from left to right. Under them is a smaller area with green and red volume bars. Prices are on the right side, dates at the bottom. At the top left a line shows the date, O, H, L, C, the change in % and the volume of the last candle.

  1. Move the mouse over the candles. A dashed crosshair follows the mouse. The top line shows the candle under the mouse. The price and the date are marked on the axes.
  2. Turn the mouse wheel to zoom. Drag to move left and right. Double-click to go back.

Above the chart there is a toolbar:

PartWhat it does
Chart type listShows the chart type (here Candlestick). Choose another type here.
ColumnsChoose which columns the chart uses.
Symbol listOnly when the result has several symbols: the chart shows one symbol at a time.
IndicatorsAdd SMA, EMA, Bollinger bands, RSI, MACD or stochastic.
LogLogarithmic price scale. Good for long periods with big moves.
No gapsLeave out time without rows (nights, Friday and Saturday, holidays). Every candle gets the same width.
LegendNames and colors of the lines. Click a name to hide or show that line.
ResetBack to the first view (like double-click).
PNGSave the chart as a picture.
CSVSave the rows of the chart as a CSV file (no new query).
Full screenShow only the chart. Press Escape to go back.
Add to dashboardPut this query and this chart on a dashboard (Part 2).

The console remembers the chart type and the columns for each query. Run the same query again and you get the same chart.

How KDB picks the chart

KDB looks at the columns of the result and picks a chart. You can always change it.

The result hasKDB showsExample query
a time column and open, high, low, closeCandlestick (volume bars when there is a volume column)SELECT time, open, high, low, close, volume FROM ochl_day WHERE symbol = 'CITY'
a time column and numbersLineSELECT time, close FROM ochl_day WHERE symbol = 'CITY'
a time column, a symbol column (up to 8 symbols) and a numberLine, one line per symbolSELECT time, symbol, close FROM ochl_day
a time column, many symbols (more than 8) and one numberMatrix heatmap (symbols down, days across)SELECT time, symbol, pct_change FROM ...
a text column and a numberBar (horizontal bars when the names are long or many)SELECT symbol, sum(volume) AS volume FROM ochl_day GROUP BY symbol
a text column and a share that adds up to 100 (or 1)DonutSELECT symbol, weight FROM my_portfolio
two text columns like sector and symbol, and numbersTreemapsee Treemap
two label columns and a number, like a correlation tableMatrix heatmapsee Matrix heatmap
the same text many times and a numberBox plot (one box per text)SELECT symbol, (close - open) / open * 100 AS change_pct FROM ochl_day
one number columnHistogramSELECT (close - open) / open * 100 AS change_pct FROM ochl_day
two number columnsScatterSELECT close, volume FROM ochl_day
three number columnsBubble (the third number is the size)
one row of numbersKPI numberSELECT count(*) AS trades, sum(amount) AS amount FROM trade_log
no number columnno chart: the Chart button is offSELECT symbol, name FROM securities

All chart types

Open the chart type list to see them all. Types that do not fit the result are grey, with a short note of what they need (for example Candlestick (needs a time column and open, high, low, close)).

GroupTypes
PriceCandlestick, OHLC bars
TimeLine, Step line, Area, Stacked area
CompareBar (vertical), Bar (horizontal), Grouped bar, Stacked bar
SpreadHistogram, Box plot, Scatter, Bubble
SharePie, Donut, Treemap
HeatmapCalendar heatmap, Matrix heatmap
NumberKPI number, Gauge
TableTable (green and red change columns, color scales, sparklines)

Colors: green and red always mean up and down. Lines and bars use eight other colors (blue, orange, violet, pink, yellow, cyan, brown, purple) that people with color blindness can also tell apart. The charts follow the Light / Dark button of the console.

Choose the columns

Click Columns. A small box opens. What you can choose depends on the chart type:

Chart typeChoices
Candlestick, OHLCTime, Open, High, Low, Close, Volume, One chart per (for example symbol)
Line, Step, Area, Stacked areaTime (X), Values (Y, several), Series (group), One chart per
BarsCategory, Values (several), Series (group), Colors (one color per series, or green above 0 and red below)
HistogramValue, Bins (how many bars; empty = chosen for you)
Box plotGroup, Value
Scatter, BubbleX, Y, Size, Color, Label
Pie, DonutCategory, Value
TreemapGroup, Label, Size, Color (change)
Calendar heatmapDate, Value, One chart per
Matrix heatmapColumns (X), Rows (Y), Value; or Value columns (wide table)
KPI numberNumbers, Change column, Order by time, Minimum, Maximum
GaugeValue, Order by time, Minimum, Maximum
TableGreen/red columns, Color scale columns, Sparkline: one row per, time, value

Press Escape or Close to close the box.

Candles, volume and indicators

Indicators from the menu

  1. Run the candle query from Your first chart and click Chart.
  2. Click Indicators.
  3. Choose RSI in the list and click Add. A new area opens under the volume bars. It shows the RSI line with lines at 30 and 70.
  4. Choose MACD and click Add. Another area shows the MACD line, the signal line and green/red histogram bars.
  5. Choose SMA and click Add. A line is drawn over the candles.
  6. Change the numbers in the box, for example period 50 for SMA. The chart changes at once.
  7. Click Remove to take an indicator away.
IndicatorWhereSettings
SMA, EMAover the candlesperiod (20)
Bollinger bandsover the candles, with a light bandperiod (20), mult (2)
RSIown area, 0 to 100, lines at 30 and 70period (14)
MACDown area: MACD, signal, histogramfast (12), slow (26), signal (9)
Stochasticown area, 0 to 100, lines at 20 and 80period (14), smooth (3), signal (3)

The indicators are computed in the browser from the close prices of the rows. They also work on a line chart with one line.

At the top left of each area, a small line shows the values under the mouse, for example RSI RSI 14 56.21.

Indicators from SQL

Chapter 19 has many indicator functions. When your query returns them as columns, the chart puts them in the right place by their name:

SELECT time, open, high, low, close, volume,
       sma(close, 5)  AS sma_5,
       rsi(close, 5)  AS rsi_5
FROM ochl_day WHERE symbol = 'CITY' ORDER BY time;
Column name containsDrawn
sma, ema, wma, vwap, bb_upper, bb_middle, bb_lower, upper, lowera line over the candles
rsithe RSI area (0 to 100)
macd, signal, histthe MACD area; a name with hist is drawn as bars
stoch, _k, _dthe stochastic area
atr, adx, cci, obv, mfi, roc, willran own area with that name
any other numberover the candles when its values are near the prices, else an own area

A name without AS works too, for example the column sma(close, 20).

Big data: 100,000 candles

The chart stays fast with many rows. When you zoom out so far that several candles fall on one pixel, KDB joins them: one candle then stands for 2, 4, 8, ... rows (first open, highest high, lowest low, last close, total volume). The top line then shows the time range, for example 2026-01-04 - 2026-02-11 O 505 H 530 L 498 C 521. Zoom in and you see every candle again.

For more than 100,000 rows, raise Rows at the top of the editor. The most is 1,000,000 rows per query.

Zoom, pan and read values

Mouse or touchWhat it does
WheelZoom in and out around the mouse
DragMove left and right (scatter: in every direction)
Two fingers (phone, tablet)Pinch to zoom, move to pan
Tap (phone)Show the values at that place
Double-clickReset
Move the mouseCrosshair and values (candles and lines: top line; other charts: a small box next to the mouse)

The chart can be used with the keyboard. Press Tab until the chart has a blue frame:

KeysWhat it does
Left / Right (or Up / Down)Move a cursor from row to row, bar to bar, slice to slice. The values are shown and read out by screen readers.
Shift + arrowMove the view
+** / **-Zoom in / out
Home or 0Reset
FFull screen
EscapeHide the cursor, or leave full screen

Log scale

Tick Log for a logarithmic price scale. The same percent move then has the same height at a low price and at a high price. Log works when all visible values are above 0. It is not offered for bars.

No gaps (trading-day time axis)

A stock market is open only some hours of some days (for example 11:00 to 15:00, Sunday to Thursday, as on some exchanges). With real time, a minute chart has big empty spaces for nights and weekends. With No gaps, those spaces are left out and each row gets the same width. The date labels are then placed on the first row of a new day, month or year.

Tables with colors and sparklines

Choose Table in the chart type list.

Sparklines: when the result has a symbol, a time and a price, the table shows one row per symbol with the last price, the change in % and a small price line:

SELECT symbol, time, close FROM ochl_day ORDER BY time;

Picture: a table with the columns symbol, close, change % and trend. Each row has a small line in the trend column. The line is green when the price went up, red when it went down.

A cell with a JSON list of numbers, like [505.2, 507.1, 503.9], also becomes a sparkline.

The table shows at most 2,000 rows. The Grid view shows all rows.

Examples for each chart type

All examples use the sample data. Run the query, click Chart, and choose the type when KDB picks another one.

Line, step line, area

SELECT time, symbol, close FROM ochl_day ORDER BY time;

One line per symbol, with a legend. Choose Step line for prices that jump, or Area for one line with a light fill.

Stacked area

SELECT o.time, o.symbol, o.close * h.qty AS value
FROM ochl_day o
JOIN (SELECT 'CITY' AS symbol, 100 AS qty UNION ALL SELECT 'COAST', 150) h ON h.symbol = o.symbol
ORDER BY o.time;

Choose Stacked area. The layers add up to the total value of the two holdings.

Bars

SELECT symbol, sum(volume) AS volume FROM ochl_day GROUP BY symbol ORDER BY volume DESC;

Picture: three blue bars with rounded tops, one per symbol, with the value on top of each bar.

For long names KDB turns the bars sideways:

SELECT s.name, sum(o.volume) AS volume FROM ochl_day o JOIN securities s ON s.symbol = o.symbol GROUP BY s.name;

Grouped and stacked bars

SELECT time, symbol, volume FROM ochl_day WHERE time >= '2026-08-23' ORDER BY time;

Choose Stacked bar: one bar per day, one colored part per symbol. Choose Grouped bar to put the symbols side by side. In Columns, Category is time, Values is volume and Series is symbol.

Bars with plus and minus values: open Columns and choose Colors: Green above 0, red below.

Histogram

SELECT (close - open) / open * 100 AS change_pct FROM ochl_day;

How many days moved by how much. Hover over a bar to see the range and the number of days. Set Bins in Columns for more or fewer bars.

Box plot

SELECT symbol, (close - open) / open * 100 AS change_pct FROM ochl_day;

One box per symbol. The box goes from the 25% value to the 75% value; the thick line is the middle value (median). The thin lines reach the lowest and the highest normal values. Dots are unusual values (outliers). Hover over a box to see all numbers.

Scatter and bubble

SELECT close, volume FROM ochl_day;

One dot per row. Hover over a dot to see its values. For a bubble chart, return three numbers and choose Bubble; the third number is the size of the bubble. A text column in Color gives each group its own color and a legend.

Pie and donut

SELECT s.sector, sum(l.ltp * l.volume) AS turnover
FROM (SELECT symbol, ltp, volume FROM live_data LATEST ON timestamp PARTITION BY symbol) l
JOIN securities s ON s.symbol = l.symbol
GROUP BY s.sector;

Choose Donut. The middle shows the total. The legend shows the share of each sector in %. More than 8 parts are joined into Other.

Treemap

SELECT s.sector, l.symbol, l.ltp * l.volume AS turnover, l.pct_change
FROM (SELECT symbol, ltp, volume, pct_change FROM live_data LATEST ON timestamp PARTITION BY symbol) l
JOIN securities s ON s.symbol = l.symbol;

Picture: big rectangles, one per sector, with the sector name on top. Inside, one box per symbol. A bigger box means more turnover. Green boxes went up, red boxes went down; the stronger the color, the bigger the move. Each box shows the symbol and the change, for example CITY +2.71%.

Calendar heatmap

SELECT time, (close - open) / open * 100 AS change_pct FROM ochl_day WHERE symbol = 'CITY';

Choose Calendar heatmap. One small square per day, in columns of weeks (a week starts on Sunday) and rows of weekdays. Green days went up, red days went down. When the days span less than about a year, the calendar shows only those weeks; otherwise one row per year.

Matrix heatmap

Daily moves of every symbol:

SELECT time, symbol, (close - open) / open * 100 AS change_pct FROM ochl_day ORDER BY time;

Choose Matrix heatmap. Symbols go down, days go across.

A correlation matrix (with the corr function of chapter 19):

SELECT a.symbol AS a, b.symbol AS b, corr(a.close, b.close) AS corr
FROM ochl_day a JOIN ochl_day b ON a.time = b.time
GROUP BY a.symbol, b.symbol;

KDB picks the matrix by itself. Values from -1 to 1 get red (-1), grey (0) and green (1), and each cell shows its number when there is room.

KPI number and gauge

SELECT count(*) AS trades, sum(amount) AS amount FROM trade_log;

Picture: two big numbers side by side, with the column names above them.

With several rows in time order, the KPI shows the last value, the change from the row before with ▲ or ▼, and a small line of all values:

SELECT time, close FROM ochl_day WHERE symbol = 'CITY' ORDER BY time;

Choose KPI number. For a Gauge (a half circle that fills up), set Minimum and Maximum in Columns. Without them the gauge uses 0 to a round number above the value (0 to 100 for names with pct, rsi or share).

Part 2: Dashboards

A dashboard is a page with many charts. Each chart is a panel. A panel has a title, one SQL query and chart settings. Dashboards are saved in the database (table _dashboards), so they are in every backup.

Open the example dashboards

Choose Dashboard in the Home menu at the top left. The first time, the list at the top left has three examples:

ExamplePanels
Market overviewMarket turnover (KPI), advancers and decliners (KPI), market average close (area), top gainers and top losers (tables with green and red), sectors (treemap), turnover by symbol (bars). Refreshes every 5 seconds.
Stock viewCandles with volume, SMA 20, RSI 14 and MACD; last close (KPI); daily move % (calendar); size of daily moves (histogram). Pickers for symbol, from and to.
PortfolioPortfolio value (area), allocation (donut), value by symbol (stacked area), holdings with trend (table with sparklines), a note.

Picture (Market overview): a row of three boxes at the top: a big number 126,481,156 for turnover, the numbers 3 and 1 for advancers and decliners, and a small area chart. Below are two tables with green ▲ and red ▼ values, then a treemap of sectors and horizontal bars of turnover.

The examples use the tables of the sample data. On your own database, open one and change the SQL of the panels (see Edit a panel). Click Save to keep your own copy; the example itself does not change.

Make your own dashboard

  1. Open Dashboard in the Home menu, then More > New dashboard.

  2. Type a title, for example My watchlist, and click Create. The empty dashboard opens in edit mode.

  3. Click Variables, then Add variable. Fill in:

    • name: symbol
    • label: Symbol
    • type: Values from SQL
    • default value: CITY
    • SQL for the values: SELECT DISTINCT symbol FROM ochl_day ORDER BY symbol

    Click OK. A Symbol list appears under the title bar.

  4. Click Add panel. Fill in:

    • Title: Candles

    • SQL:

      SELECT time, open, high, low, close, volume FROM ochl_day
      WHERE symbol = $symbol ORDER BY time

    Click Run preview (or press Ctrl+Enter in the SQL box). The chart appears on the right. Change the chart type or the columns there, if you want. Click Add.

  5. Click Add panel again:

    • Title: Volume share
    • SQL: SELECT symbol, sum(volume) AS volume FROM ochl_day GROUP BY symbol
    • Run preview, choose Donut in the chart type list, click Add.
  6. Click Save. The message says Saved "My watchlist" in the table _dashboards of database demo.

  7. Click Done to leave edit mode.

  8. Choose another symbol in the Symbol list. Every panel that uses $symbol runs again.

Reload the page: the Dashboards page and your dashboard open again.

Add a chart from the SQL page

  1. Run a query on the Home page and click Chart.
  2. Pick the chart type and the columns.
  3. Click Add to dashboard in the chart toolbar.
  4. Choose the dashboard (or A new dashboard) and a panel title. Click Add.
  5. The dashboard opens with the new panel. Click Save.

Edit a panel

Each panel has a ... button at its top right:

Menu itemWhat it does
EditChange the title, the SQL, the number of rows, the chart type and the columns
RefreshRun this panel again
Open SQL in editorPut the SQL (with the variable values filled in) into the SQL page
Save PNG, Save CSVSave the chart as a picture, or its rows as CSV
Full screenShow only this chart
Reset zoomBack to the first view
DuplicateA copy of the panel
RemoveDelete the panel (edit mode only)

In the panel editor, Kind can also be Text note: a panel with plain text, for example how to use the dashboard.

A panel runs one statement that reads: SELECT (also WITH, SHOW, EXPLAIN). The console sends it with GET /exec, and the server refuses SQL that changes data there (chapter 8). So a dashboard can never change or delete data, even when someone sends you a bad dashboard file.

On a dashboard, the mouse wheel scrolls the page. To zoom a chart, hold Ctrl and turn the wheel, or click the chart first.

Move and resize panels

Click Edit. The panels get a grip (three lines) at the top left and a corner at the bottom right.

The page has 12 columns. Panels move up when there is space above them, so there are no holes.

On a phone (narrow screen), the panels are shown one under the other, in the order top to bottom and left to right.

Variables

A variable is a picker at the top of the dashboard. Write $name (or ${name}) in the SQL of a panel.

TypePickerPut into SQL asExample
Texta text boxa quoted text: 'CITY'WHERE symbol = $symbol
List of valuesa list with the values you typed (CITY, COAST, VOLT)'COAST'WHERE symbol = $symbol
Values from SQLa list with the first column of a query'COAST'SELECT DISTINCT symbol FROM ochl_day ORDER BY symbol
Numbera number box25 (only a number is allowed)LIMIT $top
Datea date box'2026-09-11'WHERE time BETWEEN $from AND $to
Table or column namea text boxthe name, only letters, digits and _SELECT * FROM $table
Intervala text box1d, 15m, 1MSAMPLE BY $step

Why this is safe: text values are always written as SQL text with quotes, and a quote inside the value is doubled (O'Brien becomes 'O''Brien'). A number must be a number, a date must look like a date, and a name may only have letters, digits and _. A wrong value shows an error in the panel and nothing runs. So a value like CITY'; DROP TABLE x; -- stays harmless text.

Auto refresh

The Auto refresh list has Off, 5 s, 10 s, 30 s, 1 min, 5 min and 15 min. With 5 s, every panel runs again every 5 seconds, for example for live prices from live_data.

Save, share, export and import

Save writes the dashboard into the table _dashboards of the chosen database. The console makes the table the first time:

CREATE TABLE IF NOT EXISTS _dashboards (id STRING PRIMARY KEY, title STRING NOT NULL, body STRING NOT NULL, updated TIMESTAMP, updated_by STRING);

You can look at it on the SQL page:

SELECT id, title, updated, updated_by FROM _dashboards;
id            | title        | updated                  | updated_by
--------------+--------------+--------------------------+-----------
my-watchlist  | My watchlist | 2026-09-11T08:15:02.113Z | null

(updated_by is the user name when the server has users.)

Each database has its own dashboards. Choose another database at the top and the list changes. BACKUP DATABASE and Backup now save the dashboards too.

The More menu:

ItemWhat it does
New dashboardAn empty dashboard
DuplicateA copy of this dashboard (Save to keep it)
Export JSON fileDownload the dashboard as a .json file
Copy JSONCopy the same text to the clipboard, to paste in a chat or email
Import JSONPaste JSON or choose a .json file. The dashboard opens; click Save to keep it. An import never replaces a saved dashboard.
DeleteDelete the saved dashboard from the database

A dashboard file looks like this (shortened):

{
  "kdb_dashboard": 1,
  "id": "my-watchlist",
  "title": "My watchlist",
  "description": "",
  "refresh": 0,
  "variables": [
    { "name": "symbol", "label": "Symbol", "type": "query", "default": "CITY",
      "sql": "SELECT DISTINCT symbol FROM ochl_day ORDER BY symbol" }
  ],
  "panels": [
    { "id": "candles", "kind": "chart", "title": "Candles", "x": 0, "y": 0, "w": 6, "h": 5,
      "sql": "SELECT time, open, high, low, close, volume FROM ochl_day\nWHERE symbol = $symbol ORDER BY time",
      "chart": { "type": "candle", "indicators": [{ "kind": "rsi", "period": 14 }] },
      "limit": 10000 }
  ]
}

Who can do what

When the server has users (chapter 16), the role decides:

Role on the databaseLook at dashboardsChange them on the pageSaveDelete
readyesyes (not saved; use Export JSON file)no, the Save button is offno
writeyesyesyes, when the table _dashboards existsyes
adminyesyesyes; the first save creates _dashboardsyes

The first dashboard in a database creates the table _dashboards. Creating a table needs the admin role. If a write user saves first, the message says: The first dashboard in a database creates the table _dashboards. That needs the admin role; ... Ask an admin to save one dashboard, or to run the CREATE TABLE above.

Panels run with the role of the person who looks at the dashboard. A read user sees only data that read may see.

Limits

Try it yourself

  1. Show the daily moves of CITY as a calendar heatmap. Which day had the biggest drop?
  2. Make a chart of the CITY minute candles for 15 minutes, with Bollinger bands and a stochastic area.
  3. Make a dashboard Two stocks with two variables $a and $b (lists of symbols) and a line panel that shows both closes.
  4. A friend sends you a dashboard file. How do you open it without replacing your own dashboards?
Answers
  1. Run
    SELECT time, (close - open) / open * 100 AS change_pct FROM ochl_day WHERE symbol = 'CITY';

    Click Chart, choose Calendar heatmap, and move the mouse over the reddest square. The box next to the mouse shows the date and the value.

  2. Run
    SELECT time, first(open) AS open, max(high) AS high, min(low) AS low, last(close) AS close, sum(volume) AS volume
    FROM ochl_minutes WHERE symbol = 'CITY' SAMPLE BY 15m;

    Click Chart, then Indicators: add Bollinger bands and Stochastic.

  3. More > New dashboard, title Two stocks. Variables: add a and b, type List of values, values CITY, COAST, VOLT, defaults CITY and COAST. Add panel with
    SELECT time, symbol, close FROM ochl_day WHERE symbol IN ($a, $b) ORDER BY time

    KDB draws one line per symbol. Save.

  4. More > Import JSON, choose the file, Open. Check the SQL of the panels, then Save. An import always gets a new id, so it never replaces a saved dashboard.

Back to the tutorial overview