Guide

JavaScript Stock Chart

Candles need four numbers per period, in one array, in one order. This covers getting your price data into that shape and what you can compute once it is.

Basic Stock ChartOpen in new tab

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 haveAdapter
Array of objects, any reasonable column namesApexStock.normalize(rows, mapping?)
[x, open, high, low, close, volume] tuplesApexStock.normalize(rows)
Parallel column arraysApexStock.fromArrays({ open, high, low, close, ... })
CSV text, straight from a downloadApexStock.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 showReach for
Open, high, low and close per periodA 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 anywayA line chart, or aggregate to weekly candles first.
Two or more symbols compared over the same windowComparison mode with rebasing, not extra series. Absolute prices on one axis compare share price, not performance.
Volume alongside priceThe volumes oscillator pane, not a second instrument.
Portfolio composition at a point in timeNot a stock chart at all. A treemap or bar chart of holdings.
A full trading terminal with order entryA 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 registrationYes
Drawing tools and trading price linesYes
Event markers and data-space annotationsYes
On-chart data legendYes
Multi-symbol comparison with rebasingYes
Real-time streaming via appendDataYes
Range measurement and statisticsYes
Versioned state save and restoreYes
Timeframe aggregationYes
Cross-chart synchronisationYes
Theming, light and darkYes
Image, PDF and data exportYes

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 grid

See 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.

Get started