Charts

Performance

How to keep a script fast, so the chart stays smooth.

Scripts run in the chart's main thread and share its frame. A slow draw makes the whole chart stutter. The indicator list shows each script's handler and draw time (mean over the last second): check it while you work.

Handlers compute, draw emits

  • Do per-event work in handlers, once: sums, profiles, parsing, lookups. Store the results in state.
  • draw runs on every pan, zoom and crosshair move. It should only position and emit shapes.
  • Many events in one frame cost one draw: handlers do not redraw on their own.

More: State and draw.

Draw only what is in view

  • Skip items outside frame.visibleTime before calling timeToX.
  • For sorted data, binary search the first visible item instead of scanning from 0:
/** First index with `time >= target`. */
function lowerBound(list: readonly { readonly time: number }[], target: number): number {
  let low = 0;
  let high = list.length;

  while (low < high) {
    const mid = (low + high) >> 1;

    if (list[mid]!.time < target) low = mid + 1;
    else high = mid;
  }

  return low;
}

for (let i = lowerBound(points, frame.visibleTime.from); i < points.length; i += 1) {
  if (points[i]!.time > frame.visibleTime.to) break;
  // …
}
  • Hide details that are too small to see, e.g. text below a few px: the footprint switches from numbers to bars when rows get small.

Batch shapes

  • One g.polyline with all points is far cheaper than one g.line per segment.
  • g.polygon fills any outline in one call, e.g. a band between two lines.
  • g.text is the most expensive call. Keep it to what is readable.

Textures for grids

A heatmap of thousands of cells as rects is slow. g.texture draws a whole grid as one GPU quad:

  • Create the grid once: ctx.createGrid(columns, rows) returns a Result, check isErr().
  • Write columns in the handler (setColumn, pushColumn). Only changed columns upload.
  • Draw it in draw with a ColorMap. NaN cells are transparent.

See Liquidity heatmap and the Graphics reference.

Cache per view

Results needed only for what is visible, e.g. a profile per session, can be built lazily in draw and cached in state with a version. Rebuild one only when its data changed. See TPO and State and draw.

Streams

  • trades({ minSize }) drops small trades at the source, before they reach your handler.
  • Ask only for the history you need: warmup, history.
  • sessionOf is slow (time zones): call it once per session, not per bar.

On this page