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 { createApp, defineComponent, ref } from "vue";
import { AgCharts } from "ag-charts-vue3";
import type { AgChartOptions } from "ag-charts-types";
import {
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";
import clone from "clone";
let dataLabel = "1K";
let seriesType = "Line";
let datapoints = 1e3;
const timeAxes = {
x: { type: "ordinal-time", parentLevel: { enabled: true } },
};
const numberAxes = {
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 ChartExample = defineComponent({
template: `
<div class="example-controls">
<div class="controls-row">
<span>Series Type:</span>
<select v-on:change="setSeries($event.target.value, $event.target.selectedOptions[0].text)">
<option value="line" selected="">Line</option>
<option value="area">Area</option>
<option value="bar">Bar</option>
<hr />
<option value="stacked-bar">Stacked Bar</option>
<option value="stacked-area">Stacked Area</option>
<hr />
<option value="range-area">Range Area</option>
<option value="range-bar">Range Bar</option>
<hr />
<option value="candlestick">Candlestick</option>
<option value="ohlc">OHLC</option>
<hr />
<option value="scatter">Scatter</option>
<option value="bubble">Bubble</option>
<hr />
<option value="histogram">Histogram</option>
</select>
<span class="gap-left">Data Size:</span>
<div class="button-group" role="group" aria-label="Data Size">
<input type="radio" id="data-size-1k" name="data-size" value="1000" data-label="1K" checked="" v-on:change="setData($event)">
<label for="data-size-1k">1K</label>
<input type="radio" id="data-size-10k" name="data-size" value="10000" data-label="10K" v-on:change="setData($event)">
<label for="data-size-10k">10K</label>
<input type="radio" id="data-size-100k" name="data-size" value="100000" data-label="100K" v-on:change="setData($event)">
<label for="data-size-100k">100K</label>
<input type="radio" id="data-size-500k" name="data-size" value="500000" data-label="500K" v-on:change="setData($event)">
<label for="data-size-500k">500K</label>
<input type="radio" id="data-size-1m" name="data-size" value="1000000" data-label="1M" v-on:change="setData($event)">
<label for="data-size-1m">1M</label>
</div>
</div>
</div>
<ag-charts
:options="options"
/>
`,
components: {
"ag-charts": AgCharts,
},
setup(props) {
const options = ref<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,
});
const setSeries = (type, label) => {
const optionsCopy = { ...options.value };
seriesType = label;
let series = [];
switch (type) {
case "bar":
case "area":
case "line":
optionsCopy.series = [
{
type,
xKey: "timestamp",
yKey: "high",
},
];
break;
case "stacked-bar":
case "stacked-area":
const stackedType = type === "stacked-bar" ? "bar" : "area";
optionsCopy.series = [
{
type: stackedType,
xKey: "timestamp",
yKey: "open",
stacked: true,
},
{
type: stackedType,
xKey: "timestamp",
yKey: "close",
stacked: true,
},
];
break;
case "range-area":
case "range-bar":
optionsCopy.series = [
{
type,
xKey: "timestamp",
yLowKey: "low",
yHighKey: "high",
},
];
break;
case "candlestick":
case "ohlc":
optionsCopy.series = [
{
type,
xKey: "timestamp",
lowKey: "low",
highKey: "high",
openKey: "open",
closeKey: "close",
},
];
break;
case "scatter":
optionsCopy.series = [
{
type,
xKey: "x",
yKey: "y",
fillOpacity: 0.2,
strokeOpacity: 0.2,
},
];
break;
case "bubble":
optionsCopy.series = [
{
type,
xKey: "x",
yKey: "y",
sizeKey: "size",
fillOpacity: 0.2,
strokeOpacity: 0.2,
},
];
break;
case "histogram":
optionsCopy.series = [
{
type,
xKey: "close",
},
];
break;
default:
return;
}
const newDatapoints = optionsCopy.series?.[0]?.stacked
? datapoints / 2
: datapoints;
if (optionsCopy.data?.length !== newDatapoints) {
optionsCopy.data = baseData.slice(-newDatapoints);
}
if (type == "bubble" || type == "scatter" || type == "histogram") {
optionsCopy.zoom.axes = "xy";
optionsCopy.zoom.autoScaling.enabled = false;
optionsCopy.navigator.enabled = false;
optionsCopy.axes = numberAxes;
} else {
optionsCopy.zoom.axes = "xy";
optionsCopy.zoom.autoScaling.enabled = true;
optionsCopy.navigator.enabled = true;
optionsCopy.axes = timeAxes;
}
optionsCopy.title.text = `${seriesType} with ${dataLabel} datapoints`;
options.value = optionsCopy;
};
const setData = (event) => {
const optionsCopy = { ...options.value };
const input = event.target;
const points = Number(input.value);
const label = input.dataset.label;
const newDatapoints = optionsCopy.series?.[0]?.stacked
? points / 2
: points;
if (optionsCopy.data?.length !== newDatapoints) {
optionsCopy.data = baseData.slice(-newDatapoints);
}
dataLabel = label;
datapoints = points;
optionsCopy.title.text = `${seriesType} with ${dataLabel} datapoints`;
options.value = optionsCopy;
};
return {
options,
setSeries,
setData,
};
},
});
createApp(ChartExample).mount("#app");
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.