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.
@ortex-charts/financialPath 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.
@ortex-charts/financialClean 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
timeZoneto the exchange time zone rather than leaving it at the default ofUTC; see Time, sessions and resolutions. - The ORTEX mark is on a licensed chart. Only a license that grants the
whitelabelfeature 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 |