Skip to content

Start here

Quickstart

Install the package, draw a chart, add an indicator, in about five minutes.

What You Will Have in Five Minutes

A candlestick chart of your own bars, with a volume pane, a moving average, a crosshair readout and live updates, in about 30 lines of code. There are two paths through this page: one for an application with a bundler (Vite, webpack, Next.js, Rollup) and one for a plain HTML page with no build step at all. Both end at the same library.

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

Path One: A Project With a Bundler

Install the Package

The packages are published to a private registry, so a one-line .npmrc has to come first. Installation and delivery explains where the token comes from and what the other delivery modes are.

# .npmrc, next to package.json
# @ortex-charts:registry=https://gitlab.com/api/v4/projects/86057283/packages/npm/
# //gitlab.com/api/v4/projects/86057283/packages/npm/:_authToken=${ORTEX_NPM_TOKEN}

npm install @ortex-charts/financial

@ortex-charts/financial re-exports everything in @ortex-charts/core, so one dependency is enough for a price chart. Data types such as Bar and Tick come from @ortex-charts/math, which arrives as a transitive dependency. Add @ortex-charts/ui when you want the toolbar, @ortex-charts/react for components, @ortex-charts/lite for sparklines and @ortex-charts/viz for treemaps and heatmaps.

Set the License Key

Call setLicenseKey once, as early in the application as you can, before any chart is created. It returns a promise that resolves when the signature check finishes, but you do not have to await it: the check is asynchronous and the chart never blocks on it.

import { setLicenseKey } from "@ortex-charts/financial";

void setLicenseKey(process.env.NEXT_PUBLIC_ORTEX_CHARTS_KEY!);

Without a key the library still works completely. The only difference is that the ORTEX mark stays on the first pane and one warning is written to the console. See the ORTEX mark and license keys.

Draw the Chart

The container needs a height. The chart follows its container with a ResizeObserver, so a height in CSS is enough and no resize handling is required.

import { createFinancialChart } from "@ortex-charts/financial";
import type { Bar } from "@ortex-charts/math";

const bars: Bar[] = [
  { time: 1735689600000, open: 219.4, high: 221.8, low: 218.9, close: 221.2, volume: 41200000 },
  { time: 1735776000000, open: 221.3, high: 224.0, low: 220.6, close: 223.7, volume: 38900000 },
  // …
];

const el = document.getElementById("chart") as HTMLDivElement;

const chart = createFinancialChart(el, {
  theme: "dark",              // "dark", "light", or your own token object
  timeZone: "America/New_York",
  resolution: "1D",           // datafeed notation: 1, 5, 15, 60, 1D, 1W, 1M
  data: bars,
});

time is milliseconds since the Unix epoch, in UTC. Every timestamp in the library is. The time zone controls where bar boundaries fall and how axis labels read, not the timestamps themselves. Bars must be sorted ascending; duplicates and gaps are a question for your feed, not for the chart.

createFinancialChart returns a small handle, not the engine itself. chart.chart is the core Chart and chart.main is the candlestick series, and everything the handle does not cover is reachable through them.

Add an Indicator

chart.addIndicator("ema", { length: 21 });
chart.addIndicator("sma", { length: 200 });
chart.addIndicator("rsi");                 // lands in a pane of its own

There are 18 built-in indicators, listed in Indicators. Registering your own takes about 20 lines and it then behaves like a built-in one, including in the toolbar menu and in saved layouts.

Read Values Under the Crosshair

chart.chart.subscribeCrosshairMove((e) => {
  if (e.index === null) return;             // pointer left the chart
  const v = e.seriesValues.get(chart.main.id);
  if (v && "close" in v) {
    console.log(v.open, v.high, v.low, v.close, v.volume);
  }
});

seriesValues is a Map keyed by series id. Bar series give OHLCV, value series give { value }.

Push Live Updates

The simplest form is a single call per update. An incoming bar whose time equals the last bar replaces it in place; a later time appends.

chart.update({ time: 1735862400000, open: 223.7, high: 226.1, low: 223.0, close: 225.8, volume: 12400000 });

For a stream of trades rather than bars, hand the chart a source and let it fold ticks into bars of the chart resolution:

import { websocketSource, type LiveItem } from "@ortex-charts/financial";

const source = websocketSource<LiveItem>({
  url: "wss://feed.example.com/trades",
  onOpen: (send) => send(JSON.stringify({ subscribe: "ACME" })),
  parse: (raw) => {
    const m = JSON.parse(String(raw)) as { t: number; p: number; s: number };
    return { time: m.t, price: m.p, size: m.s };   // a Tick
  },
});

const stop = chart.live(source);   // call stop() to unsubscribe

The socket opens on the first subscriber, reconnects with backoff, and closes when the last subscriber leaves. Your own data covers datafeeds, adapters and every input shape.

A live tick stream@ortex-charts/financial
Loading a live tick stream
Ticks arriving every 200 ms, aggregated into the forming bar, with the price line and axis label following.

Open this example with its source

Clean Up

chart.remove();

In a component, call this in the teardown of the effect that created the chart.

Path Two: A Plain HTML Page

Customers on the hosted kit get a single script tag with their key already baked in. There is nothing to install and nothing to configure.

<div id="chart" style="height: 480px"></div>

<script src="https://cdn.ortexcharts.com/v1/kit/YOUR_KIT_ID.js"></script>
<script>
  const bars = [
    { time: 1735689600000, open: 219.4, high: 221.8, low: 218.9, close: 221.2, volume: 41200000 },
    { time: 1735776000000, open: 221.3, high: 224.0, low: 220.6, close: 223.7, volume: 38900000 }
  ];

  const chart = OrtexCharts.createFinancialChart(document.getElementById("chart"), {
    theme: "dark",
    timeZone: "America/New_York",
    resolution: "1D",
    data: bars
  });

  chart.addIndicator("ema", { length: 21 });
</script>

The kit is an immediately-invoked bundle of @ortex-charts/financial and @ortex-charts/ui that puts everything on one global, OrtexCharts, and installs the license key for you. OrtexCharts.createChartShell is there too, so the whole toolbar is one more call:

<script>
  const shell = OrtexCharts.createChartShell(document.getElementById("chart"), {
    symbol: "ACME",
    data: bars
  });
</script>

The CDN serves a kit only to the domains registered on it, so the same script tag on another site returns a 403. Evaluating without a kit works too: load the packages from your own build and leave the key unset.

The Whole Thing, Once

Everything above, together, as one module. This is the shape of nearly every integration.

import { createFinancialChart, setLicenseKey } from "@ortex-charts/financial";
import type { Bar } from "@ortex-charts/math";

void setLicenseKey(import.meta.env.VITE_ORTEX_CHARTS_KEY);

export function mountChart(el: HTMLDivElement, bars: Bar[], onHover: (text: string) => void) {
  const chart = createFinancialChart(el, {
    theme: "dark",
    timeZone: "America/New_York",
    resolution: "1D",
    watermark: { text: "ACME", visible: true, fontSize: 44 },
    data: bars,
  });

  chart.addIndicator("ema", { length: 21 });
  chart.addIndicator("sma", { length: 200 });

  chart.setMarkers([
    { time: bars[120].time, position: "belowBar", shape: "arrowUp", text: "E", tooltip: "Earnings" },
  ]);

  chart.chart.subscribeCrosshairMove((e) => {
    const v = e.index === null ? null : e.seriesValues.get(chart.main.id);
    onHover(v && "close" in v ? `O ${v.open}  H ${v.high}  L ${v.low}  C ${v.close}` : "");
  });

  // Open on the last year rather than on the whole history.
  chart.chart.timeScale.setVisibleRange(bars.length - 260, bars.length - 1);

  return () => chart.remove();
}

Common First Problems

  • Nothing is drawn and the container is zero pixels high. The chart follows its container, so the container needs a height from CSS or from options.height.
  • Every bar is at the wrong hour. Timestamps are milliseconds, not seconds; multiply a Unix second timestamp by 1,000 before passing it in.
  • Daily bars land on the wrong day. Set timeZone to the exchange time zone rather than leaving it at the default of UTC; see Time, sessions and resolutions.
  • The ORTEX mark is on a licensed chart. Only a license that grants the whitelabel feature may hide it, and the console says which of the two cases applies.
  • The chart works in development and is blank after a server-side render. The library is browser-only; create it inside an effect, never during a render on the server.

Where to Go Next

If you want to Read
Understand panes, scales, series and the render loop Concepts
Connect a real feed rather than a static array Your own data
Change every color, font and number format Colors, fonts and formatting
Get the toolbar, symbol search and saved layouts The shell and layouts
Write React instead of imperative calls React
Know what the library does not do Migrating and Questions we are asked