Candlestick Chart

Plot prices over time as candles, each showing a period's open, high, low and close, with a touch tooltip, pan and zoom, and dark mode built in.

Installation

npx @blotch/dolan add candlestick-chart
import { UiCandlestickChart } from '../components/ui/candlestick-chart';

This also adds Chart, which draws the preset, and installs the npm packages @tanstack/charts and d3-shape.

Usage

import { Component } from '@angular/core';
import { UiCandlestickChart } from '../components/ui/candlestick-chart';

@Component({
  selector: 'app-share-price',
  imports: [UiCandlestickChart],
  template: `<ui-candlestick-chart [data]="prices" label="Share price" />`,
})
export class SharePrice {
  protected readonly prices = [
    { x: 1, open: 100, high: 108, low: 98, close: 106 },
    { x: 2, open: 106, high: 110, low: 103, close: 104 },
    { x: 3, open: 104, high: 105, low: 96, close: 98 },
    { x: 4, open: 98, high: 102, low: 95, close: 101 },
  ];
}
  • data is an array of { x, open, high, low, close } items, all numbers. x is the period, such as a session index.
  • Each candle has a thin wick from low to high and a wider body from open to close.
  • The y axis fits the lowest low to the highest high, with room for the candles at each end.
  • Touch a candle to see its four prices in a tooltip. Tap to pin it. Set label so a screen reader can say what the chart shows.

Customize

Colors

upColor colors candles that closed at or above their open (default green). downColor colors those that closed below (default red).

<ui-candlestick-chart
  [data]="prices"
  upColor="rgba(20, 184, 166, 1)"
  downColor="rgba(244, 63, 94, 1)"
/>

Shape

bodyWidthRatio sets how much of its slot each body fills, from 0 to 1 (default 0.6). A slot is the smallest gap between two x values. wickWidth is the wick's thickness in px.

<ui-candlestick-chart [data]="prices" [bodyWidthRatio]="0.8" [wickWidth]="2" />

Axes and range

xAxisLabel and yAxisLabel add axis titles. Horizontal gridlines are on by default: showGrid turns them off, and showXGrid adds vertical ones.

<ui-candlestick-chart
  [data]="prices"
  xAxisLabel="Session"
  yAxisLabel="Price ($)"
  [yMin]="90"
/>
  • xMin, xMax, yMin and yMax fix a bound instead of fitting the data, for example to compare two charts on one scale.
  • xTickFormat and yTickFormat turn tick values into labels, for example a session index into a date.
  • tickCount sets roughly how many ticks each axis gets. The axis picks round values near it.

Padding

padding insets the plot, each side as a fraction (0 to 1) of its size.

<ui-candlestick-chart [data]="prices" [padding]="{ top: 0.05, bottom: 0.05 }" />

Size

The chart fills its container's width and is 200px tall. Set height for another height and width for a fixed width, both in px. Use height rather than a height class, because the preset always sets a height.

<ui-candlestick-chart [data]="prices" [height]="260" />

Interaction

Taps

candleTap emits the tapped candle's { x, open, high, low, close } item. A tap on empty space emits nothing. Set tooltip to false to turn the tooltip off.

<ui-candlestick-chart [data]="prices" (candleTap)="selected.set($event)" />

candleTap fires inside the touch, so wrap router navigation in setTimeout (see the setTimeout rule).

Pan and zoom

Set zoomable to let users drag to pan, with +, − and 1:1 buttons. Zooming in on the x axis spreads the candles apart, which suits a long price history.

<ui-candlestick-chart [data]="prices" [zoomable]="true" zoomAxes="x" />

To zoom from your own controls, set showZoomControls to false and call zoomIn(), zoomOut() or resetZoom() on the component. See Pan and zoom for how it behaves.

API

UiCandlestickChart

InputTypeDefaultDescription
datareadonly CandlestickDatum[]RequiredCandles, each { x, open, high, low, close }.
upColorstring'rgba(34, 197, 94, 1)'Color of a candle that closed at or above its open.
downColorstring'rgba(239, 68, 68, 1)'Color of a candle that closed below its open.
wickWidthnumber1Wick thickness in px.
bodyWidthRationumber0.6Body width as a fraction of its slot, 0–1.
widthnumberfills containerFixed width in px.
heightnumber200Height in px.
tickCountnumber5Target number of ticks per axis.
showGridbooleantrueHorizontal gridlines at the y ticks.
showXGridbooleanfalseVertical gridlines at the x ticks.
xAxisLabelstring''X-axis title.
yAxisLabelstring''Y-axis title.
paddingChartPadding{}Inner inset, each side a 0–1 fraction of the plot.
xMinnumberfits the dataFixed x-axis lower bound.
xMaxnumberfits the dataFixed x-axis upper bound.
yMinnumberfits the dataFixed y-axis lower bound.
yMaxnumberfits the dataFixed y-axis upper bound.
xTickFormat(value: number) => stringautomaticFormats x-axis tick labels.
yTickFormat(value: number) => stringautomaticFormats y-axis tick labels.
tooltipbooleantrueShow a tooltip for the touched candle. A tap pins it.
labelstring'Chart'What a screen reader announces for the chart.
colorScheme'light' | 'dark' | 'system'from provideChartThemeWhich color table the marks use.
zoomablebooleanfalseDrag to pan, with zoom buttons.
zoomAxes'x' | 'y' | 'xy''xy'Which axes pan and zoom.
showZoomControlsbooleantrueShow the +/−/1:1 buttons when zoomable.
minZoomnumber1Smallest zoom. 1 shows all the data.
maxZoomnumber8Largest zoom.
zoomStepnumber1.4Zoom factor per button press or method call.
classstring''Extra Tailwind classes for the chart's root view.
OutputTypeDescription
candleTapCandlestickDatumThe candle a tap landed on.
MethodDescription
zoomIn()Zooms in by zoomStep.
zoomOut()Zooms out by zoomStep.
resetZoom()Shows all the data again.

CandlestickDatum is { x: number; open: number; high: number; low: number; close: number }, exported from ../components/ui/candlestick-chart. ChartPadding is { top?, right?, bottom?, left? }, each a fraction (0–1) of the plot's size, with omitted sides 0. It's exported from ../components/ui/chart.

candlestickChartDefinition

function candlestickChartDefinition(
  options: CandlestickChartOptions,
): ChartDefinition;

Returns the TanStack Charts definition <ui-candlestick-chart> renders: a link mark for the wicks, a rect mark for the bodies, and a tooltip that lists all four prices. The options are the inputs that shape the chart, with the same defaults: data, upColor, downColor, wickWidth, bodyWidthRatio, tickCount, showGrid, showXGrid, xAxisLabel, yAxisLabel, padding, xMin, xMax, yMin, yMax, xTickFormat, yTickFormat and tooltip. Size, label, color scheme and zoom are inputs of <ui-chart> instead.

Start from it to add marks of your own, such as a price level or a moving average, and render the result with <ui-chart>. Added marks get a plain x and y tooltip.

import { defineChart, ruleY } from '@tanstack/charts/universal';
import { candlestickChartDefinition } from '../components/ui/candlestick-chart';

const base = candlestickChartDefinition({ data: prices });

protected readonly withPreviousClose = defineChart({
  ...base,
  marks: [
    ...base.marks,
    ruleY([104], { stroke: 'var(--muted-foreground)', strokeDasharray: '4 4' }),
  ],
});