Skip to content

Charts

Series types

Candlesticks, bars, line, area, baseline, histogram, Heikin-Ashi and your own.

The Six Built-In Types

Every series is created through chart.addSeries(type, options) and returns a SeriesModel. Three of the types hold OHLCV bars and three hold single values; the difference matters, because a value series cannot be redrawn as candles.

Type Kind Data Draws
candlestick bars Bar[] Body between open and close, wicks to high and low. Hollow and Heikin-Ashi are options on this type, not separate types.
bar bars Bar[] A vertical high-low line with an open tick left and a close tick right.
line values points A polyline, optionally curved, optionally with point markers.
area values points A line with a vertical gradient fill beneath it.
baseline values points A line whose halves above and below a base value are colored and filled separately.
histogram values points Columns from a base value, colored fixed, by sign, or by the main series direction.
const price = chart.addSeries("candlestick", { title: "AAPL" });
price.setData(bars);

const ma = chart.addSeries("line", { title: "EMA 21", color: "#F5A524", lineWidth: 1.5 });
ma.setData(emaPoints);
Stock chart@ortex-charts/financial
Loading stock chart
Ten years of IBM daily bars with a moving average pair, volume, earnings markers and a crosshair readout.

Open this example with its source

Options Every Series Has

These come from SeriesOptionsBase and apply to all six types and to any type you register yourself.

Option Type Default What it does
id string generated Stable identity, useful when you restore a saved chart.
title string "" Name in the legend and in the layout. Placement is keyed by it.
pane string first pane Which pane the series lives on.
priceScaleId string "right" right, left, or any other id for an undrawn overlay scale.
visible boolean true Hides the series without removing it; it keeps updating.
color string next palette color Line, area and histogram color.
lineWidth number 2 Stroke width in CSS pixels.
lineStyle "solid" | "dashed" | "dotted" "solid" Stroke pattern.
priceFormat PriceFormat price, 2 decimals Number formatting for the axis, legend and labels.
lastValueVisible boolean true Value label on the price axis.
priceLineVisible boolean false Dashed horizontal line at the last value. The bar and candlestick types default it to true.
align AlignMode "forwardFill" How timestamps map onto the main timeline.
zIndex number 0 Draw order within a pane.
legendVisible boolean true Whether the series appears in the legend.
crosshairMarkerVisible boolean true Dot on the series at the crosshair index.

color left unset takes the next color from the theme palette, so several overlays added in a row are distinguishable without any color management on your side.

Candlesticks

chart.addSeries("candlestick", {
  title: "AAPL",
  upColor: "#22C08A",
  downColor: "#EF4E5A",
  wickUpColor: "#22C08A",
  wickDownColor: "#EF4E5A",
  borderVisible: true,
  hollowUp: false,
  heikinAshi: false,
  bodyWidthRatio: 0.7,
});

hollowUp draws up candles with a transparent body, which is the classic hollow-candle style. bodyWidthRatio is the fraction of the bar spacing a body occupies; bodies never grow wider than the gap allows and never shrink below one pixel, so a chart at maximum zoom out still reads as candles rather than as mush.

Colors left unset fall back to the theme up, down, upWick and downWick tokens, which is what makes a theme switch repaint candles correctly without touching the series.

Heikin-Ashi

Heikin-Ashi is an option rather than a type, so it can be toggled without recreating the series or losing the indicators computed from it.

price.applyOptions({ heikinAshi: true });

The transform runs over the raw bars whenever the data or the flag changes. Note the limitation: on a live chart the transform currently re-runs over all bars on every tick, which is measurable on very long intraday histories. Indicators still read the raw columns, so an EMA over Heikin-Ashi candles is an EMA of the real closes.

OHLC Bars

chart.addSeries("bar", { title: "AAPL", thin: false, openVisible: true });

thin drops the open and close ticks and draws only the high-low line, which is what most people want below about four pixels of bar spacing. openVisible: false keeps the close tick and drops the open one.

Line

chart.addSeries("line", {
  title: "Cost to borrow",
  curve: "linear",        // "linear" | "step" | "monotone" | "smooth"
  pointMarkers: false,
  pointRadius: 2.5,
  align: "forwardFill",
});

curve: "step" is the right choice for anything that holds a value until it changes, such as a rating, a threshold or a daily statistic drawn on an intraday chart. monotone is a shape-preserving spline; smooth is a basis spline that does not pass through its points, so avoid it where the exact values matter.

Line series decimate when they are denser than the pixels available: one minimum and one maximum per pixel column. The visual result is identical and the cost stops growing with the data.

Area

chart.addSeries("area", {
  title: "Estimated Short Interest",
  color: "#7C5CE6",
  topColor: "rgba(124, 92, 230, 0.30)",
  bottomColor: "rgba(124, 92, 230, 0.02)",
  baseValue: 0,
});

topColor and bottomColor are the ends of a vertical gradient; unset, they come from the theme areaTop and areaBottom tokens. baseValue fills down to a value instead of to the bottom of the pane, which is what you want for a series that can go negative.

Baseline

chart.addSeries("baseline", {
  title: "Relative performance",
  baseValue: 0,              // or "first" for the first visible value
  topLineColor: "#22C08A",
  bottomLineColor: "#EF4E5A",
  topFillColor: "rgba(34, 192, 138, 0.18)",
  bottomFillColor: "rgba(239, 78, 90, 0.18)",
});

baseValue: "first" re-bases on every pan, which turns the series into "performance since the left edge of the view" without any recomputation on your side.

Histogram

chart.addSeries("histogram", {
  title: "Volume",
  colorMode: "mainDirection",     // "fixed" | "sign" | "mainDirection"
  base: 0,
  widthRatio: 0.7,
  upColor: "rgba(34, 192, 138, 0.45)",
  downColor: "rgba(239, 78, 90, 0.45)",
  priceFormat: { type: "volume", precision: 0, minMove: 1 },
});

The three color modes cover the three things histograms are used for. fixed is one color for a count. sign colors above and below base differently, which is what a MACD histogram or a delta pane wants. mainDirection colors each column by whether the main series bar at the same index closed up or down, which is what a volume pane wants — and it is why createFinancialChart does not need you to precompute colored volume rows.

Setting and Updating Data

setData replaces everything. update handles one row and is the path live data takes.

series.setData(rows);

const appended = series.update({ time, open, high, low, close, volume });
// true when a new bar was appended, false when the last bar was replaced in place

The rules update follows are worth knowing, because the fast paths depend on them:

  • A time equal to the last bar replaces that bar in place.
  • A later time appends into a capacity-doubling backing store, so no column is copied.
  • An earlier time takes a slower rebuild path; feeds that emit out of order pay for it.

series.holdUpdates() and series.releaseUpdates() queue updates and apply them in order later. Bar replay uses this so a live feed can keep running while the user replays history.

Switching Type Without Losing State

price.setType("line", { lineWidth: 2 });

setType keeps the data, the id, the pane, the scale, the title, the alignment and every base option, and swaps only the renderer and its type-specific defaults. Indicators, markers and drawings attached to the chart are untouched. This is what the toolbar's chart-type buttons call, and it is why switching from candles to a line does not flash.

A value series cannot become a bar series, because it has no open, high or low. The call throws with a message that says exactly that.

Reading Values

series.valueAt(index);       // { open, high, low, close, volume } or { value } or null
series.primaryValue(index);  // the number the axis label and price line use
series.lastIndex();          // index of the last finite value, or -1
series.rawLength;            // rows as given, before alignment
series.raw;                  // the struct-of-arrays form, or null before setData
series.columns;              // aligned columns — a view, never hold it across a data change

Price Lines

A price line is a horizontal line at a fixed value on the series' scale, with an axis label. It belongs to the series, so it moves with the scale and disappears with it.

const id = chart.addPriceLine(series.id, {
  price: 182.5,
  title: "Entry",
  color: "#F5A524",
  lineStyle: "dashed",
  lineWidth: 1,
  axisLabelVisible: true,
});

chart.removePriceLine(series.id, id);

For lines the user can drag, and for crossing notifications, use alerts instead.

Writing Your Own Series Type

A series type is four functions and a defaults object. Register it once and chart.addSeries("your-type", …) works, including in saved layouts and the legend.

import { registerSeriesType, pixelColumns, type SeriesOptionsBase, type SeriesTypeDef } from "@ortex-charts/core";
import { valueExtent } from "@ortex-charts/math";

interface DotOptions extends SeriesOptionsBase {
  radius: number;
}

const dotSeries: SeriesTypeDef<DotOptions> = {
  kind: "values",
  defaults: { radius: 3 },
  draw(ctx, view) {
    const { xs, ys } = pixelColumns(view, "value");
    ctx.fillStyle = view.options.color;
    for (let i = 0; i < xs.length; i++) {
      if (ys[i] !== ys[i]) continue;                   // NaN is a gap
      ctx.beginPath();
      ctx.arc(xs[i], ys[i], view.options.radius, 0, Math.PI * 2);
      ctx.fill();
    }
  },
  extent: (columns, from, to) => valueExtent(columns.value, from, to),
  valueAt: (columns, i) => (columns.value[i] === columns.value[i] ? { value: columns.value[i] } : null),
  primaryValue: (columns, i) => columns.value[i],
};

registerSeriesType("dot", dotSeries);

chart.addSeries<DotOptions>("dot", { title: "Signals", color: "#F472B6", radius: 4 }).setData(points);

The SeriesView handed to draw carries aligned columns, the visible index bounds from and to, the mappings x(index) and y(value), the bar spacing, the resolved theme, the device pixel ratio, the plot rectangle, the hovered index and the main series columns. Draw only between from and to; the engine has already clipped to the pane.

Four rules keep a custom type well behaved:

  • Return null from extent when the series should not affect autoscale.
  • Return null from valueAt where there is no data, so the legend shows nothing rather than NaN.
  • Treat NaN as a gap in every column rather than as a zero.
  • Add an optional colorAt when the series is drawn in more than one color, so the last-value label matches what is on screen.

What Is Not Here

Renko, Kagi, Point and Figure, line break, step-line bars, scatter and HLC area are not built in. They are transforms plus a renderer on the same interfaces shown above, and they are on the roadmap, but today writing one is your work rather than a configuration flag.