---
title: "High-Frequency Update"
enterprise: true
framework: javascript
version: "14.1.0"
---

# High-Frequency Update

AG Charts is optimised for **high-frequency data updates**, maintaining smooth performance with rapid or real-time data updates on large datasets, while maintaining full functionality.

#### High-Frequency Updates

```ts
import {
  AgCartesianChartOptions,
  AgCartesianSeriesOptions,
  AgCharts,
  AreaSeriesModule,
  BarSeriesModule,
  CandlestickSeriesModule,
  CrosshairModule,
  LineSeriesModule,
  ModuleRegistry,
  NumberAxisModule,
  OhlcSeriesModule,
  RangeAreaSeriesModule,
  RangeBarSeriesModule,
  TimeAxisModule,
  ZoomModule,
} from "ag-charts-enterprise";
import { Datum, SeriesType, createSeedData, generateNextDatum } from "./data";

let initialPoints = 1000;
let batchSize = 10;
let currentSeriesType: SeriesType = "line";
let currentUpdateMode: "rolling" | "append" = "rolling";
const seedResult = createSeedData(initialPoints, currentSeriesType);
let data: Datum[] = seedResult.data;
let nextIndex = data.length;
let lastBasePrice: number | undefined = seedResult.lastBasePrice;
function createSeriesConfig(
  seriesType: SeriesType,
): AgCartesianSeriesOptions[] {
  switch (seriesType) {
    case "ohlc":
    case "candlestick":
      return [
        {
          type: seriesType,
          xKey: "timestamp",
          openKey: "open",
          highKey: "high",
          lowKey: "low",
          closeKey: "close",
        },
      ];
    case "stacked-bar":
      return [
        { type: "bar", xKey: "timestamp", yKey: "value", stacked: true },
        { type: "bar", xKey: "timestamp", yKey: "value2", stacked: true },
      ];
    case "stacked-area":
      return [
        {
          type: "area",
          xKey: "timestamp",
          yKey: "value",
          stacked: true,
          marker: { enabled: false },
        },
        {
          type: "area",
          xKey: "timestamp",
          yKey: "value2",
          stacked: true,
          marker: { enabled: false },
        },
      ];
    case "range-area":
      return [
        {
          type: "range-area",
          xKey: "timestamp",
          yLowKey: "low",
          yHighKey: "high",
        },
      ];
    case "range-bar":
      return [
        {
          type: "range-bar",
          xKey: "timestamp",
          yLowKey: "low",
          yHighKey: "high",
        },
      ];
    case "area":
      return [
        {
          type: "area",
          xKey: "timestamp",
          yKey: "value",
          marker: { enabled: false },
          strokeWidth: 1,
        },
      ];
    case "bar":
      return [
        {
          type: "bar",
          xKey: "timestamp",
          yKey: "value",
        },
      ];
    case "line":
    default:
      return [
        {
          type: "line",
          xKey: "timestamp",
          yKey: "value",
          marker: { enabled: false },
          strokeWidth: 1,
        },
      ];
  }
}
let isRunning = false;
let animationFrameId: number | undefined;
function updateBatchSize(value: string) {
  batchSize = parseInt(value, 10);
}
function updateDataSize(value: string) {
  const wasRunning = isRunning;
  if (wasRunning) stopUpdates();
  initialPoints = parseInt(value, 10);
  const seedResult = createSeedData(initialPoints, currentSeriesType);
  data = seedResult.data;
  nextIndex = data.length;
  lastBasePrice = seedResult.lastBasePrice;
  options.data = data;
  chart.update(options);
  if (wasRunning) startUpdates();
}
ModuleRegistry.registerModules([
  AreaSeriesModule,
  BarSeriesModule,
  CandlestickSeriesModule,
  CrosshairModule,
  LineSeriesModule,
  NumberAxisModule,
  OhlcSeriesModule,
  RangeAreaSeriesModule,
  RangeBarSeriesModule,
  TimeAxisModule,
  ZoomModule,
]);

const options: AgCartesianChartOptions = {
  data,
  title: { text: "High-Frequency Data Updates" },
  subtitle: { text: "Rolling Window: removing old points, adding new points" },
  animation: { enabled: false },
  zoom: { enabled: true, onDataChange: { strategy: "preserveRatios" } },
  axes: {
    x: {
      type: "time",
      position: "bottom",
      nice: false,
      label: {
        format: "%H:%M:%S",
      },
    },
  },
  series: createSeriesConfig(currentSeriesType),
  legend: { enabled: false },
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);

function runUpdate() {
  if (!isRunning) return;
  switch (currentUpdateMode) {
    case "rolling":
      performRollingUpdate();
      break;
    case "append":
      performAppendUpdate();
      break;
  }
  animationFrameId = requestAnimationFrame(runUpdate);
}

function performRollingUpdate() {
  const newPoints: Datum[] = [];
  for (let i = 0; i < batchSize; i++) {
    const result = generateNextDatum(
      nextIndex++,
      currentSeriesType,
      lastBasePrice,
    );
    newPoints.push(result.datum);
    lastBasePrice = result.lastBasePrice;
  }
  const pointsToRemove = data.slice(0, batchSize);
  chart.applyTransaction({
    remove: pointsToRemove,
    add: newPoints,
  });
  data.splice(0, batchSize);
  data.push(...newPoints);
}

function performAppendUpdate() {
  const newPoints: Datum[] = [];
  for (let i = 0; i < batchSize; i++) {
    const result = generateNextDatum(
      nextIndex++,
      currentSeriesType,
      lastBasePrice,
    );
    newPoints.push(result.datum);
    lastBasePrice = result.lastBasePrice;
  }
  chart.applyTransaction({
    add: newPoints,
  });
  data.push(...newPoints);
}

function startUpdates() {
  if (isRunning) return;
  isRunning = true;
  updateButton();
  animationFrameId = requestAnimationFrame(runUpdate);
}

function stopUpdates() {
  if (!isRunning) return;
  isRunning = false;
  updateButton();
  if (animationFrameId !== undefined) {
    cancelAnimationFrame(animationFrameId);
    animationFrameId = undefined;
  }
}

function updateButton() {
  const button = document.getElementById("toggleBtn");
  if (button) {
    button.textContent = isRunning ? "Stop Updates" : "Start Updates";
  }
}

function toggleUpdates() {
  if (isRunning) {
    stopUpdates();
  } else {
    startUpdates();
  }
}

function updateSeriesType(value: string) {
  const wasRunning = isRunning;
  if (wasRunning) stopUpdates();
  currentSeriesType = value as SeriesType;
  const seedResult = createSeedData(initialPoints, currentSeriesType);
  data = seedResult.data;
  nextIndex = data.length;
  lastBasePrice = seedResult.lastBasePrice;
  options.data = data;
  options.series = createSeriesConfig(currentSeriesType);

  if (wasRunning) startUpdates();
  chart.update(options);
}

function updateMode(value: string) {
  const wasRunning = isRunning;
  if (wasRunning) stopUpdates();
  currentUpdateMode = value as "rolling" | "append";
  const subtitleText =
    currentUpdateMode === "rolling"
      ? "Rolling Window: removing old points, adding new points"
      : "Append Only: continuously adding new points";
  options.subtitle = { text: subtitleText };
  const seedResult = createSeedData(initialPoints, currentSeriesType);
  data = seedResult.data;
  nextIndex = data.length;
  lastBasePrice = seedResult.lastBasePrice;
  options.data = data;

  if (wasRunning) startUpdates();
  chart.update(options);
}

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).toggleUpdates = toggleUpdates;
  (<any>window).updateSeriesType = updateSeriesType;
  (<any>window).updateMode = updateMode;
}
```

[Live example: High-Frequency Updates](https://www.ag-grid.com/charts/typescript/high-frequency-data/examples/high-frequency-showcase)

In the above example:

- Use the controls to select different series types.
- Choose between update modes: **Rolling Window** (remove old, add new) or **Append Only** (continuously add).
- Updates run at `requestAnimationFrame` speed for maximum throughput.
- The zoom functionality is available during the updates.

> **Note**
>
> Performance may vary based on your specific use case, environment and hardware.

## How it works

The `applyTransaction()` API provides efficient incremental updates to chart data. Instead of replacing the entire dataset on each update, transactions specify only the changes: items to add, remove, or update. This dramatically reduces processing overhead for real-time scenarios.

```js
// Rolling window: remove old points, add new ones
chart.applyTransaction({
    remove: oldPoints,
    add: newPoints,
});
```

For detailed information on transaction operations and best practices, see [Transactions](https://www.ag-grid.com/charts/javascript/transactions/).

## Financial Charts

High-frequency updates also work with Financial Charts.

The following example simulates real-time candlestick trading data:

- Starts with 365 days of historical data.
- Every 2 seconds, a new candle is created representing a new trading day.
- Between new candles, price ticks update the current candle's high, low, and close values at `requestAnimationFrame` speed.

#### High-Frequency Financial Chart

```ts
import {
  AgCharts,
  AgFinancialChartOptions,
  FinancialChartModule,
  ModuleRegistry,
} from "ag-charts-enterprise";
import { Candle, MS_PER_DAY, PriceSimulator, getHistoricalData } from "./data";
import { random } from "./seededRandom";

const CANDLE_INTERVAL_MS = 2000;
const TICKS_PER_CANDLE = 100;
const INITIAL_POINTS = 365;
const VISIBLE_POINTS = 25;
const data: Candle[] = getHistoricalData(INITIAL_POINTS);
let currentCandle: Candle | undefined;
let simulator: PriceSimulator | undefined;
let candleIntervalId: ReturnType<typeof setInterval> | undefined;
let animationFrameId: number | undefined;
let isRunning = false;
ModuleRegistry.registerModules([FinancialChartModule]);

const options: AgFinancialChartOptions = {
  title: { text: "High-Frequency Update" },
  data,
  volume: true,
  navigator: false,
  rangeButtons: false,
  statusBar: true,
  toolbar: false,
  zoom: true,
  initialState: {
    zoom: {
      ratioX: { start: 1 - VISIBLE_POINTS / data.length, end: 1 },
    },
  },
};

options.container = document.getElementById("myChart");

const chart = AgCharts.createFinancialChart(options);

function startNewCandle() {
  const lastCandle = data[data.length - 1];
  const newTimestamp = lastCandle.date + MS_PER_DAY;
  const openPrice = lastCandle.close;
  simulator = new PriceSimulator(openPrice, TICKS_PER_CANDLE);
  currentCandle = {
    date: newTimestamp,
    open: openPrice,
    high: openPrice,
    low: openPrice,
    close: openPrice,
    volume: Math.round(1000000 + random() * 500000),
  };
  data.push(currentCandle);
  chart.applyTransaction({ add: [currentCandle] });
}

function processTick() {
  if (!currentCandle || !simulator) return;
  const newPrice = simulator.tick();
  currentCandle.close = newPrice;
  currentCandle.high = Math.max(currentCandle.high, newPrice);
  currentCandle.low = Math.min(currentCandle.low, newPrice);
  chart.applyTransaction({ update: [currentCandle] });
}

function scheduleNextTick() {
  if (isRunning) {
    animationFrameId = requestAnimationFrame(() => {
      processTick();
      scheduleNextTick();
    });
  }
}

function stopAllUpdates() {
  if (candleIntervalId) {
    clearInterval(candleIntervalId);
    candleIntervalId = undefined;
  }
  if (animationFrameId) {
    cancelAnimationFrame(animationFrameId);
    animationFrameId = undefined;
  }
}

function updateButton() {
  const button = document.getElementById("toggleBtn");
  if (button) {
    button.textContent = isRunning ? "Stop" : "Start";
  }
}

function toggleUpdates() {
  if (isRunning) {
    isRunning = false;
    stopAllUpdates();
  } else {
    isRunning = true;
    startNewCandle();
    scheduleNextTick();
    candleIntervalId = setInterval(() => startNewCandle(), CANDLE_INTERVAL_MS);
  }
  updateButton();
}

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).toggleUpdates = toggleUpdates;
}
```

[Live example: High-Frequency Financial Chart](https://www.ag-grid.com/charts/typescript/high-frequency-data/examples/high-frequency-financial-chart-showcase)
