AG Charts is optimised to handle large datasets with over 1 million points, while maintaining full, smooth interactivity. No additional configuration or modules required - it just works out of the box.
import {
AgCartesianAxisOptions,
AgCartesianChartOptions,
AgCartesianSeriesOptions,
AgCharts,
AnimationModule,
AreaSeriesModule,
BarSeriesModule,
BubbleSeriesModule,
CandlestickSeriesModule,
CategoryAxisModule,
ContextMenuModule,
CrosshairModule,
HistogramSeriesModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NavigatorModule,
NumberAxisModule,
OhlcSeriesModule,
OrdinalTimeAxisModule,
RangeAreaSeriesModule,
RangeBarSeriesModule,
ScatterSeriesModule,
TimeAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
let dataLabel = "1K";
let seriesType = "Line";
let datapoints = 1e3;
const timeAxes: Record<string, AgCartesianAxisOptions> = {
x: { type: "ordinal-time", parentLevel: { enabled: true } },
};
const numberAxes: Record<string, AgCartesianAxisOptions> = {
x: { type: "number" },
};
const baseData = getData(1e6);
ModuleRegistry.registerModules([
AnimationModule,
AreaSeriesModule,
BarSeriesModule,
BubbleSeriesModule,
CandlestickSeriesModule,
CrosshairModule,
HistogramSeriesModule,
LegendModule,
LineSeriesModule,
NavigatorModule,
NumberAxisModule,
OhlcSeriesModule,
OrdinalTimeAxisModule,
RangeAreaSeriesModule,
RangeBarSeriesModule,
ScatterSeriesModule,
TimeAxisModule,
ZoomModule,
CategoryAxisModule,
ContextMenuModule,
]);
const options: AgCartesianChartOptions = {
data: baseData.slice(-datapoints),
title: { text: `${seriesType} with ${dataLabel} datapoints` },
animation: { enabled: false },
zoom: {
enabled: true,
axes: "x",
anchorPointX: "pointer",
anchorPointY: "pointer",
autoScaling: {
enabled: true,
},
},
navigator: {
enabled: true,
miniChart: {
enabled: true,
},
},
series: [
{
type: "line",
xKey: "timestamp",
yKey: "close",
},
],
axes: timeAxes,
};
options.container = document.getElementById("myChart");
const chart = AgCharts.create(options);
function setSeries(type: string, label: string) {
seriesType = label;
let series: AgCartesianSeriesOptions[] = [];
switch (type) {
case "bar":
case "area":
case "line":
options.series = [
{
type,
xKey: "timestamp",
yKey: "high",
},
];
break;
case "stacked-bar":
case "stacked-area":
const stackedType = type === "stacked-bar" ? "bar" : "area";
options.series = [
{ type: stackedType, xKey: "timestamp", yKey: "open", stacked: true },
{ type: stackedType, xKey: "timestamp", yKey: "close", stacked: true },
];
break;
case "range-area":
case "range-bar":
options.series = [
{
type,
xKey: "timestamp",
yLowKey: "low",
yHighKey: "high",
},
];
break;
case "candlestick":
case "ohlc":
options.series = [
{
type,
xKey: "timestamp",
lowKey: "low",
highKey: "high",
openKey: "open",
closeKey: "close",
},
];
break;
case "scatter":
options.series = [
{
type,
xKey: "x",
yKey: "y",
fillOpacity: 0.2,
strokeOpacity: 0.2,
},
];
break;
case "bubble":
options.series = [
{
type,
xKey: "x",
yKey: "y",
sizeKey: "size",
fillOpacity: 0.2,
strokeOpacity: 0.2,
},
];
break;
case "histogram":
options.series = [
{
type,
xKey: "close",
},
];
break;
default:
return;
}
const newDatapoints = (options.series?.[0] as any)?.stacked
? datapoints / 2
: datapoints;
if (options.data?.length !== newDatapoints) {
options.data = baseData.slice(-newDatapoints);
}
if (type == "bubble" || type == "scatter" || type == "histogram") {
options.zoom!.axes = "xy";
options.zoom!.autoScaling!.enabled = false;
options.navigator!.enabled = false;
options.axes = numberAxes;
} else {
options.zoom!.axes = "xy";
options.zoom!.autoScaling!.enabled = true;
options.navigator!.enabled = true;
options.axes = timeAxes;
}
options.title!.text = `${seriesType} with ${dataLabel} datapoints`;
chart.update(options);
}
function setData(points: number, label: string) {
const newDatapoints = (options.series?.[0] as any)?.stacked
? points / 2
: points;
if (options.data?.length !== newDatapoints) {
options.data = baseData.slice(-newDatapoints);
}
dataLabel = label;
datapoints = points;
options.title!.text = `${seriesType} with ${dataLabel} datapoints`;
chart.update(options);
}
if (typeof window !== "undefined") {
// Attach external event handlers to window so they can be called from index.html
(<any>window).setSeries = setSeries;
(<any>window).setData = setData;
}
const startPrice = 100;
const maxDailyPriceChange = 5;
const maxRangeDelta = 1;
function sfc32(a: number, b: number, c: number, d: number) {
return function () {
a >>>= 0;
b >>>= 0;
c >>>= 0;
d >>>= 0;
let t = (a + b) | 0;
a = b ^ (b >>> 9);
b = (c + (c << 3)) | 0;
c = (c << 21) | (c >>> 11);
d = (d + 1) | 0;
t = (t + d) | 0;
c = (c + t) | 0;
return (t >>> 0) / 4294967296;
};
}
function seedRandom(seed = 1337): () => number {
const realSeed = seed ^ 0xdeadbeef; // 32-bit seed with optional XOR value
// Pad seed with Phi, Pi and E.
// https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
return sfc32(0x9e3779b9, 0x243f6a88, 0xb7e15162, realSeed);
}
const referenceDate = new Date(2024, 0, 1, 0).getTime();
export function getData(hours: number) {
let currentPrice = startPrice;
const random = seedRandom();
const period = 60 * 60 * 1000;
const startDate = new Date(2024, 0, 1, -hours);
return Array.from({ length: hours }, () => {
// Note time is reversed
const close = currentPrice;
const open = close + (random() * 2 - 1) * maxDailyPriceChange;
currentPrice = open;
const high = Math.max(open, close) + random() * maxRangeDelta;
const low = Math.min(open, close) - random() * maxRangeDelta;
startDate.setHours(startDate.getHours() + 1);
const timestamp = startDate.getTime();
let x = random();
let y = random();
x *= random();
y *= random();
const size = random();
return { timestamp, open, close, high, low, x, y, size };
});
}
In the above example:
- Use the controls to select different series types and data sizes.
- Use the mouse, Navigator or zoom controls to zoom, scroll and pan the data.
Performance may vary based on your specific use case, environment and hardware.
How it works Copy Link
Behind the scenes, AG Charts applies advanced data aggregation techniques, such as the M4 algorithm, to ensure accurate representation across scales. As you zoom and pan, the chart dynamically adapts to the visible range, preserving both performance and clarity.