import { CandlestickChart } from "semiotic"
Candlestick bars for OHLC (open/high/low/close) data. When onlyhighAccessor and lowAccessor are provided the chart degrades into a range chart — same rendering path, same scene builder — so you can reuse the component for things like daily high/low temperatures, p5/p95 bands, or any "two endpoints per x" series.
Quick Start
import { CandlestickChart } from "semiotic" const frameProps = { /* --- Data --- */ data: [ { session: 0, o: 100.00, h: 102.10, l: 98.40, c: 101.80 }, { session: 1, o: 101.80, h: 104.20, l: 100.90, c: 103.50 }, { session: 2, o: 103.50, h: 105.00, l: 101.20, c: 101.70 }, // ... 20 sessions ], /* --- Process --- */ xAccessor: "session", openAccessor: "o", highAccessor: "h", lowAccessor: "l", closeAccessor: "c", /* --- Customize --- */ title: "Simulated OHLC", xLabel: "Session", yLabel: "Price", /* --- Interact --- */ tooltip: true } export default () => { return <CandlestickChart {...frameProps} /> }
Range Chart (no open/close)
Omit openAccessor and closeAccessor and the component switches to range rendering: a single-color bar from low to high with endpoint caps, no up/down distinction. UsecandlestickStyle.rangeColor to override the color.
import { CandlestickChart } from "semiotic" const frameProps = { /* --- Data --- */ data: [ { day: 0, min: 58, max: 72 }, // Mon { day: 1, min: 60, max: 75 }, // Tue { day: 2, min: 62, max: 78 }, // Wed // ... ], /* --- Process --- */ xAccessor: "day", highAccessor: "max", lowAccessor: "min", /* --- Customize --- */ candlestickStyle: { rangeColor: "var(--semiotic-primary, #6366f1)" }, title: "Daily temperature ranges", xLabel: "Day", yLabel: "°F", xFormat: d => ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"][d], /* --- Interact --- */ tooltip: true } export default () => { return <CandlestickChart {...frameProps} /> }
Compact Modes
CandlestickChart honors the mode prop shared across the HOCs: primary (default 600×400), context(400×250, no axes), and sparkline (120×24, no axes, no title). Explicit width/heightoverride the mode default.
OHLC
Range
The range variant (no openAccessor/closeAccessor) degrades cleanly at every mode — the same endpoint-cap rendering, just scaled to the canvas the mode assigns.
Animation
Pass animate as true or{ duration, easing } and the frame's transition pipeline will:
- Morph open/high/low/close y-coords when a matched bar (same x-identity) gets new values on the next render.
- Fade in bars that appear in a scene rebuild (new x-identity).
- Fade out bars that disappear — either because they scrolled off a sliding window, got removed via the push API, or the data array dropped them.
Morph on data change
Click the button to swap in a new random OHLC sequence. Each bar at a given x-index smoothly interpolates its wick top/bottom and body top/bottom toward the new values.
JSX
const [seed, setSeed] = useState(1) const data = useMemo(() => makeOhlcSequence(seed), [seed]) return ( <> <button onClick={() => setSeed(s => s + 1)}>Regenerate data</button> <CandlestickChart data={data} xAccessor="t" openAccessor="o" highAccessor="h" lowAccessor="l" closeAccessor="c" animate={{ duration: 500 }} /> </> )
Enter / exit in a sliding window
Pushing a bar every 700ms while capping the window at 10 bars — new bars fade in on the right, old bars fade out on the left as they get dropped. pointIdAccessor tells the HOC which field identifies each datum so remove() can target it.
JSX
// Sliding window: push every 700ms, remove the oldest once we hit the cap. // pointIdAccessor lets the HOC identify which datum to remove by key. // Counters live in a ref so pause/resume doesn't restart the series. const chartRef = useRef() const stateRef = useRef({ t: 0, lastClose: 100, seen: [] }) useEffect(() => { const id = setInterval(() => { const s = stateRef.current const o = s.lastClose const c = o + (Math.random() - 0.5) * 4 const datum = { t: s.t++, o, h: Math.max(o,c) + 2, l: Math.min(o,c) - 2, c } chartRef.current?.push(datum) s.seen.push(datum) if (s.seen.length > 10) chartRef.current?.remove(String(s.seen.shift().t)) s.lastClose = c }, 700) return () => clearInterval(id) }, []) return ( <CandlestickChart ref={chartRef} xAccessor="t" openAccessor="o" highAccessor="h" lowAccessor="l" closeAccessor="c" pointIdAccessor="t" animate={{ duration: 400 }} /> )
Styling
The candlestickStyle object covers the body/wick colors and widths. In OHLC mode, upColor fills bars where close ≥ open, downColor fills the rest. In range mode,rangeColor supersedes both.
JSX
<CandlestickChart data={data} xAccessor="session" openAccessor="o" highAccessor="h" lowAccessor="l" closeAccessor="c" candlestickStyle={{ upColor: "var(--semiotic-success)", downColor: "var(--semiotic-danger)", wickColor: "var(--semiotic-text)", wickWidth: 1, // bodyWidth is auto-computed from data spacing if omitted }} />
Props
| Prop | Type | Required | Default | Description |
|---|
data | array | — | — | Array of data points. Omit when using the push API. |
xAccessor | string | function | — | "x" | Field name or function returning the x value (category/time) for each bar. |
highAccessor | string | function | Yes | "high" | Upper bound. Required in both OHLC and range mode. |
lowAccessor | string | function | Yes | "low" | Lower bound. Required in both OHLC and range mode. |
openAccessor | string | function | — | — | Opening value. Must be provided together with closeAccessor for OHLC mode. |
closeAccessor | string | function | — | — | Closing value. Must be provided together with openAccessor for OHLC mode. |
candlestickStyle | object | — | — | Style overrides: { upColor, downColor, wickColor, wickWidth, bodyWidth, rangeColor }. |
mode | "primary" | "context" | "sparkline" | — | "primary" | Compact-layout preset. Sparkline (120×24) and context (400×250) strip axes; primary is the default 600×400. |
tooltip | boolean | object | function | — | — | Default tooltip shows O/H/L/C in OHLC mode and H/L in range mode. Pass `false` to disable. |
width | number | — | 600 | Chart width (px). Overrides the mode default. |
height | number | — | 400 | Chart height (px). Overrides the mode default. |
margin | number | object | — | mode-driven | Margin around the chart area, merged with the mode defaults. |
xLabel | string | — | — | X-axis label (suppressed in compact modes). |
yLabel | string | — | — | Y-axis label (suppressed in compact modes). |
title | string | — | — | Chart title (suppressed in compact modes). |
annotations | array | — | — | Annotation configs forwarded to StreamXYFrame. |
frameProps | object | — | — | Escape hatch for passing additional props to the underlying StreamXYFrame. |
Graduating to the Frame
CandlestickChart is a thin wrapper overStreamXYFrame withchartType="candlestick". Drop to the frame when you need axis customization beyond what the HOC surfaces — everything under the hood is the same accessor/scene pipeline.
JSX
// HOC <CandlestickChart data={data} xAccessor="session" openAccessor="o" highAccessor="h" lowAccessor="l" closeAccessor="c" /> // Frame equivalent <StreamXYFrame chartType="candlestick" data={data} xAccessor="session" yAccessor="h" openAccessor="o" highAccessor="h" lowAccessor="l" closeAccessor="c" size={[600, 400]} />