Built with ApexStock
A stock chart plots price over time as candles rather than a line, so each period shows four numbers instead of one: where it opened, how high and low it traded, and where it closed. Add technical indicators, a crosshair that reads values off the series, and zoom that keeps its place, and you have the chart every trading and portfolio screen is built around.
ApexStock is the one in this family. It is built on ApexCharts.js, which it takes as a peer dependency, so install both:
npm install apexstock apexcharts
import ApexCharts from 'apexcharts'
import ApexStock from 'apexstock'
const chart = new ApexStock(document.getElementById('chart'), {
chart: { height: 500 },
theme: { mode: 'light' },
series: [
{
name: 'AAPL',
data: [
{ x: 1704067200000, y: [187, 189, 185, 188], v: 1_000_000 },
{ x: 1704153600000, y: [188, 191, 187, 190], v: 1_200_000 },
],
},
],
})
chart.render()
Two things in that data shape catch people out, and both are worth reading twice.
The four prices live in one y array, ordered [open, high, low, close].
Not four separate keys. A config with { open, high, low, close } at the top
level of a point is the single most common reason a first stock chart draws
nothing.
Only series[0] is the instrument. Extra entries in series are treated
as indicators, not as additional symbols. To show two tickers together, use
comparison mode rather than a second series.
What shape is your price data in?
Market data arrives in a handful of recognisable shapes, and ApexStock ships an adapter for each rather than making you write the conversion. All three are static methods, so they work before you have a chart, and in Node.
| What you have | Adapter |
|---|---|
| Array of objects, any reasonable column names | ApexStock.normalize(rows, mapping?) |
[x, open, high, low, close, volume] tuples | ApexStock.normalize(rows) |
| Parallel column arrays | ApexStock.fromArrays({ open, high, low, close, ... }) |
| CSV text, straight from a download | ApexStock.fromCSV(text, options?) |
Column names resolve by case-insensitive alias, so the shape your API already returns usually needs no mapping at all:
ApexStock.normalize([
{ date: '2026-01-02', o: 187, h: 189, l: 185, c: 188, vol: 1000 },
{ date: '2026-01-03', o: 188, h: 191, l: 187, c: 190, vol: 1200 },
])
// [ { x: 1767312000000, y: [187, 189, 185, 188], v: 1000 },
// { x: 1767398400000, y: [188, 191, 187, 190], v: 1200 } ]
date and time resolve to x, o/h/l/c to the price tuple, vol to
volume. Date strings become epoch milliseconds. Numeric strings are coerced, so
'187' from a CSV or a JSON API is fine.
Close-only data does not make candles
If all you have is a closing price per day, fromArrays will take it, and the
result is worth looking at before you ship it:
ApexStock.fromArrays({ close: [188, 190, 189] })
// [ { x: 0, y: [188, 188, 188, 188] },
// { x: 1, y: [190, 190, 190, 190] },
// { x: 2, y: [189, 189, 189, 189] } ]
Two things happened. Open, high and low were filled from the close, so
every candle is flat and a candlestick chart of it is a row of dashes. And x
became the array index, not a date, so the axis counts rather than showing
time.
Both are reasonable defaults for a quick plot and wrong for a real one. With
close-only data, pass an x column and switch the chart type to line or
area, which is the honest rendering of one number per period.
Why is my stock chart missing bars?
Because they were rejected, and ApexStock tells you so. A point needs a
parseable x and a y whose first four entries are finite numbers; anything
else is dropped and the count is logged.
Measured on apexstock 0.5.0, feeding four rows where one has no close and
one has an unparseable date:
[ApexStock] Dropped 2 malformed OHLC point(s): each needs a parseable `x` and
a `y` whose first four entries [open, high, low, close] are finite numbers.
Two rows come back out of four. Worth knowing because the chart still renders happily with what survived, so a partial dataset looks like a real chart of a quiet market rather than an error. If your bar count looks low, the console already has the answer.
What indicators are built in?
Twenty-three, split by where they draw. Overlays share the price axis; oscillators each get their own stacked pane and several can be open at once. Keys are case-insensitive.
Overlays: moving average, exponential moving average, vwap,
bollinger bands, donchian channels, keltner channels,
fibonacci retracements, linear regression, ichimoku cloud indicator.
Oscillators: rsi, macd, volumes, price volume trend,
stochastic oscillator, standard deviation indicator,
average directional index, atr, chaikin oscillator,
commodity channel index, trend strength index, accelerator oscillator,
bollinger bands %b, bollinger bands width.
chart.updateIndicator('rsi', { period: 21 }) // adds it, or re-parameterises it
chart.removeIndicator('rsi')
chart.listIndicators() // introspect what is available
updateIndicator never toggles off, which makes it safe to call from a
controlled UI without tracking whether the indicator is already active. For
anything not on the list, ApexStock.registerIndicator(name, definition)
registers your own globally.
Computing without drawing
ApexStock.stats is the analysis engine on its own, with no chart and no DOM
required, which means it runs in a worker, a test, or on a server generating a
report. Verified importable in plain Node on 0.5.0:
import ApexStock from 'apexstock'
const series = ApexStock.normalize(rows)
ApexStock.stats.returns(series, { mode: 'simple' }) // percent, index 0 is null
ApexStock.stats.drawdown(series) // max, current, episodes, basis
ApexStock.stats.volatility(series, from, to) // stdev and annualized
ApexStock.stats.align(instruments, { join: 'union', fill: 'hold' })
align is the one to know about if you compare instruments. Two tickers rarely
share a calendar, and fill: 'hold' carries the last observation forward, which
is the finance default. A point before an instrument's first observation stays
null and is never backfilled, so a newly listed symbol does not acquire an
invented history.
When is a candlestick chart the wrong choice?
| What you have or want to show | Reach for |
|---|---|
| Open, high, low and close per period | A candlestick or OHLC chart. This is the case. |
| One value per period (a close, a NAV, a balance) | A line or area chart. Candles need four numbers; faking three of them from the fourth draws flat marks that imply a precision you do not have. |
| Long history where the daily range is invisible anyway | A line chart, or aggregate to weekly candles first. |
| Two or more symbols compared over the same window | Comparison mode with rebasing, not extra series. Absolute prices on one axis compare share price, not performance. |
| Volume alongside price | The volumes oscillator pane, not a second instrument. |
| Portfolio composition at a point in time | Not a stock chart at all. A treemap or bar chart of holdings. |
| A full trading terminal with order entry | A different class of product. ApexStock is built to embed charts in an application, not to be a standalone terminal. |
That last row is the library's own framing and worth taking at face value when you scope the work.
What ApexStock ships
ApexStock is a commercial library, included from the Premium plan upward. It is the most narrowly licensed product in the family: neither the Community tier nor Pro includes it, so the under-$2M waiver that covers ApexCharts.js does not extend to it. It is not open source, and published source is not an open licence. Every feature renders in full without a licence key, watermarked, so a real evaluation costs nothing. See the pricing page for what each plan includes.
| Included | |
|---|---|
| Six switchable chart types (candlestick, line, area, column, heikin-ashi, renko) | Yes |
| 23 built-in indicators, plus custom registration | Yes |
| Drawing tools and trading price lines | Yes |
| Event markers and data-space annotations | Yes |
| On-chart data legend | Yes |
| Multi-symbol comparison with rebasing | Yes |
Real-time streaming via appendData | Yes |
| Range measurement and statistics | Yes |
| Versioned state save and restore | Yes |
| Timeframe aggregation | Yes |
| Cross-chart synchronisation | Yes |
| Theming, light and dark | Yes |
| Image, PDF and data export | Yes |
Streaming live prices into it
appendData adds closed bars incrementally and updates any active indicators
in place, rather than recomputing the chart from a new series. The real-time
streaming guide covers the tick-rate
decisions around it.
A price chart is rarely alone on the screen. Clicking it to filter a positions table beside it is its own recipe:
Drill down from a chart into a gridSee the pieces running
Reference documentation
Frequently Asked Questions
What data format does a JavaScript candlestick chart need?
One point per period as `{ x, y: [open, high, low, close], v? }`. The four prices sit in a single `y` array in that order, not as four separate keys, and `v` is optional volume. `x` takes a timestamp, a Date or a category. In ApexStock only `series[0]` is the instrument; further series entries are read as indicators rather than as extra symbols.
How do I convert my API response into OHLC candles?
Use one of the three static adapters rather than writing the conversion. `ApexStock.normalize(rows)` takes an array of objects with any reasonable column names, or `[x, o, h, l, c, v]` tuples; `ApexStock.fromArrays({ close, ... })` takes parallel column arrays; `ApexStock.fromCSV(text)` takes CSV text. Column names resolve by case-insensitive alias, so `date`, `o`, `h`, `l`, `c` and `vol` are understood without a mapping.
Why is my stock chart missing bars?
They were rejected as malformed. A point needs a parseable `x` and a `y` whose first four entries are finite numbers; anything else is dropped and the count is logged to the console, naming the requirement. The chart still renders with whatever survived, so a partial dataset can look like a real chart of a quiet market. Check the console before the data.
Can I plot a single closing price per day as candles?
You can, but you should not. `fromArrays({ close })` fills open, high and low from the close, so every candle is flat, and `x` becomes the array index rather than a date. Both are sensible defaults for a quick look and wrong for anything shipped: with one value per period, a line or area chart is the honest rendering.
How many technical indicators does ApexStock include?
Twenty-three: nine overlays that share the price axis (moving average, exponential moving average, VWAP, Bollinger bands, Donchian channels, Keltner channels, Fibonacci retracements, linear regression, Ichimoku cloud) and fourteen oscillators that each take their own stacked pane, including RSI, MACD, volumes, stochastic, ATX-style directional index and ATR. Several oscillators can be active at once, and `registerIndicator` adds your own.
Related
Start with ApexStock
Every licensed feature runs in full, watermarked, so you can evaluate it with your own data.