Candlestick

Pro

Open/high/low/close sessions on an ordinal axis, with direction carried by the body fill as well as the colour, an optional volume pane and a logarithmic price scale. Pure SVG.

</>sv-candlestick-chart chartcandlestickohlcfinancialstock
import { SvCandlestickChartComponent } from '@styloviz-pro/charts';

Demo & usage

Basic usage

Pro

Usage example — open the gallery for the full interactive demo.

Pro

An interactive demo for this component lives in the gallery.

Open live demo

More examples

With volume

Pro

Logarithmic price axis

Pro

Back-adjust a 4-for-1 split

Pro

React to a session

Pro

When to use

  • A price history where the open, high, low and close of each session all matter.
  • Any period where the RANGE within a session is part of the story, not just the closing level.
  • For a closing price alone, a Line Chart is read faster; for a live feed, use the Live Stock variant.

Async states

  • loading
  • empty
  • error
  • retry

Inputs

NameTypeDefaultDescription
loadingbooleanfalseTrue while the sessions are being fetched.
errorSvDataError | string | nullnullLoad failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`.
emptyMessagestring'No sessions to plot'Copy for the no-data state.
filteredbooleanfalseTrue when the emptiness is caused by a filter rather than an empty set.
sessionsreadonly SvCandle[][]Trading sessions, oldest first. The axis is ORDINAL — one slot per session you supply — so weekends, holidays and halts simply are not in the list. Do not pad them: a market has no Saturday, and a padded session is a candle that never traded. Labels are printed verbatim and never parsed, so the chart has no time zone of its own: format each session's label in the exchange's zone before it arrives, and it reads the same for every viewer. Labels need not be unique.
priceScaleSvPriceScale'linear'How the price axis is spaced. `log` is the correct choice over a long history: on a linear axis a move from 10 to 20 and one from 100 to 110 are drawn the same height, though the first doubled the money and the second added a tenth. It falls back to linear when the range touches zero or every price is the same, and the chart says which it used and why.
showVolumebooleanfalseDraw the volume pane beneath the price pane.
splitsreadonly SvCandleSplit[][]Share splits to back-adjust across: `{ at, ratio }`, where `at` is the label (first match) or index of the first session AFTER the split and `ratio` is new shares per old (`4` for 4-for-1, `0.1` for a 1-for-10 reverse split). Every earlier session's prices are divided by the ratio and its volume multiplied by it, so an unadjusted split does not draw as a crash nobody lost money on. The adjustment is disclosed under the chart and in each adjusted session's accessible name. Leave empty when the feed is already adjusted — adjusting twice is its own crash.
minSlotWidthnumber3Narrowest slot, in pixels, a single session may be drawn in. When there are more sessions than fit at this width, consecutive sessions are merged into one candle each (first open, highest high, lowest low, last close, summed volume) — the bar a coarser timeframe would draw — and the merge is disclosed. `0` turns aggregation off and draws hairline candles instead.
widthnumber720Plot width in pixels.
heightnumber360Plot height in pixels, both panes included.
upColorstring'var(--color-accent-500)'Colour for a rising session.
downColorstring'var(--color-danger-500)'Colour for a falling session.
pricePrefixstring''Currency or unit prefix for prices, e.g. `$`.
titlestring''Chart title, rendered above the plot.
ariaLabelstring''Accessible name override. Falls back to a generated summary.
customClassstring''Extra CSS classes applied to the root wrapper.

Outputs

EventPayload typeDescription
retryvoidThe host should re-run the failed load.
sessionHoverSvCandleEventEmitted when a session is hovered or keyboard-focused.
sessionSelectSvCandleEventEmitted when a session is clicked or activated with Enter/Space.

Wire it to your API

This component is headless about transport: it renders what you give it and reports what the user asks for. Nothing below requires @styloviz/data — drive it from whatever you already use.

Template

Pro

Bound to the names this component actually exposes.

Host class

Pro

Drive the three async inputs from your own fetch.

  • This component does not emit a query, so it renders what you give it. Fetch on your own trigger and bind `sessions`, `loading` and `error`.
  • Set `filtered` when a search or filter is active. It is the difference between "you have no orders" and "no orders match this filter" — telling a customer with 4,000 orders the first one reads as a bug.
  • Normalize failures with `toSvDataError()` from `@styloviz/core`. It derives `retryable` from the status, so a 404 does not get a "Try again" button that can only fail again.

Full walkthrough: Connecting to a backend.

Customization & theming

  • Linear or logarithmic price axis, with the scale actually in force named under the plot — including when log was asked for and could not be given. Negative prices (spreads, futures) plot on the linear axis; log is positive-only and falls back to linear over any range that reaches zero or below (or a series whose prices are all equal), naming the reason.
  • `splits` back-adjusts prior sessions for a split or reverse split, disclosed in the note, in each adjusted session's accessible name and tooltip, and in its table row.
  • `minSlotWidth` (default 3 px): when one candle per session would be narrower, consecutive sessions are merged OHLC-style (first open, highest high, lowest low, last close, summed volume) — the bar a coarser timeframe draws — and the merge is disclosed; the table still lists every session. `0` draws hairlines instead.
  • Optional volume pane beneath the price pane.
  • Up and down colours, a currency prefix, and plot size.
  • A session missing any of the four prices is dropped and counted rather than patched, because filling a missing close with the open invents a doji and filling it with the previous close invents a gap.

Best practices

  • Don't: Do not pad weekends, holidays or halts. The axis is ORDINAL — one slot per session you supply — because those are real absences of trading rather than missing data. A padded session is a candle that never traded, and drawing a continuous time axis across the gap squeezes the sessions that did happen. The chart states that horizontal distance is not time.
  • Do: Use the logarithmic scale for any history longer than a year or so. On a linear axis a move from 10 to 20 and one from 100 to 110 are the same height though the first doubled the money — so the early years flatten into a line while recent noise fills the frame.
  • Do: Send a session exactly as the feed reported it, contradictions included. A high below the body is not a rounding error: drawn as sent, the wick lands inside the body and the candle looks ordinary while asserting the price closed above the highest price it reached. The chart widens the range, marks the session and counts it in the note — repairing it upstream hides that the feed is wrong.
  • Do: Pass `splits` when the feed is NOT already split-adjusted. An unadjusted 4-for-1 split draws as a 75 % collapse nobody lost money on; with `{ at, ratio }` every earlier session's prices are divided by the ratio (volume multiplied by it) and the adjustment is disclosed under the chart. Leave it empty for an adjusted feed — adjusting twice is its own crash.
  • Don't: Do not expect the chart to convert time zones. It has none, by the same policy as the Line Chart: session labels are printed verbatim and never parsed as dates, so they cannot shift with the viewer's offset. Format each label once, in the exchange's zone — a session belongs to the exchange's calendar day, not the reader's.
  • Tip: The up and down colours are emphasis rather than the encoding. Direction is also in the body fill — hollow for a rise, solid for a fall — and written as a word in every accessible name and table row, so recolouring them for a brand costs the reader nothing.

Accessibility

  • The series is ONE tab stop: Arrow keys step session by session (clamped, not wrapping — a time series has a first and a last), Home/End jump to the ends, Enter/Space select. Each candle is a `role="button"` named with its direction as a word, its percentage move and all four prices.
  • Sessions are keyed by position, not label, so two sessions that share a label are both drawn, both listed, and never highlight together.
  • Direction is encoded three times over: colour, body fill (hollow up, solid down, which is what paper charts used before colour was available), and the word itself. Colour alone reaches nobody using a screen reader, is unreliable with a colour-vision deficiency, and is gone in print.
  • `sv-data-summary-table` carries every session's direction, all four prices, the percentage change and the volume — figures the body and wick geometry only ever approximates. It lists every session individually even when dense data is drawn as merged candles, and a Note column marks split-adjusted prices and widened ranges.
  • The volume bars are not a second set of tab stops: each belongs to a session that is already focusable.

Feedback on Candlestick

Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.

Loads giscus.app and GitHub — nothing is fetched until you click.