---
title: "High-Frequency Update"
enterprise: true
framework: react
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

```tsx
import React, { useState, useRef, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
  AgCartesianChartOptions,
  AgCartesianSeriesOptions,
  AgChartsInstance,
  AreaSeriesModule,
  BarSeriesModule,
  CandlestickSeriesModule,
  CrosshairModule,
  LineSeriesModule,
  ModuleRegistry,
  NumberAxisModule,
  OhlcSeriesModule,
  RangeAreaSeriesModule,
  RangeBarSeriesModule,
  TimeAxisModule,
  ZoomModule,
} from "ag-charts-enterprise";
import { Datum, SeriesType, createSeedData, generateNextDatum } from "./data";
import clone from "clone";

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;
  chartRef.current!.update(options);
  if (wasRunning) startUpdates();
}
ModuleRegistry.registerModules([
  AreaSeriesModule,
  BarSeriesModule,
  CandlestickSeriesModule,
  CrosshairModule,
  LineSeriesModule,
  NumberAxisModule,
  OhlcSeriesModule,
  RangeAreaSeriesModule,
  RangeBarSeriesModule,
  TimeAxisModule,
  ZoomModule,
]);

const ChartExample = () => {
  const chartRef = useRef<AgChartsInstance>(null);
  const [options, setOptions] = useState<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 },
  });

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

  const 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);
    chartRef.current!.applyTransaction({
      remove: pointsToRemove,
      add: newPoints,
    });
    data.splice(0, batchSize);
    data.push(...newPoints);
  };

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

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

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

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

  const toggleUpdates = () => {
    if (isRunning) {
      stopUpdates();
    } else {
      startUpdates();
    }
  };

  const updateSeriesType = (value: string) => {
    const nextOptions = clone(options);

    const wasRunning = isRunning;
    if (wasRunning) stopUpdates();
    currentSeriesType = value as SeriesType;
    const seedResult = createSeedData(initialPoints, currentSeriesType);
    data = seedResult.data;
    nextIndex = data.length;
    lastBasePrice = seedResult.lastBasePrice;
    nextOptions.data = data;
    nextOptions.series = createSeriesConfig(currentSeriesType);

    if (wasRunning) startUpdates();

    setOptions(nextOptions);
  };

  const updateMode = (value: string) => {
    const nextOptions = clone(options);

    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";
    nextOptions.subtitle = { text: subtitleText };
    const seedResult = createSeedData(initialPoints, currentSeriesType);
    data = seedResult.data;
    nextIndex = data.length;
    lastBasePrice = seedResult.lastBasePrice;
    nextOptions.data = data;

    if (wasRunning) startUpdates();

    setOptions(nextOptions);
  };

  return (
    <Fragment>
      <div className="example-controls">
        <div className="controls-row">
          <button id="toggleBtn" onClick={toggleUpdates}>
            Start Updates
          </button>
          <select
            id="seriesTypeSelect"
            onChange={(event) => updateSeriesType(event.target.value)}
          >
            <option value="line">Line</option>
            <option value="area">Area</option>
            <option value="bar">Bar</option>
            <option value="stacked-bar">Stacked Bar</option>
            <option value="stacked-area">Stacked Area</option>
            <option value="range-area">Range Area</option>
            <option value="range-bar">Range Bar</option>
            <option value="candlestick">Candlestick</option>
            <option value="ohlc">OHLC</option>
          </select>
          <select
            id="updateModeSelect"
            onChange={(event) => updateMode(event.target.value)}
          >
            <option value="rolling">Rolling Window</option>
            <option value="append">Append Only</option>
          </select>
        </div>
      </div>
      <AgCharts ref={chartRef} options={options} />
    </Fragment>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
```

[Live example: High-Frequency Updates](https://www.ag-grid.com/charts/reactFunctionalTs/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/react/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

```tsx
import React, { useState, useRef, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgFinancialCharts } from "ag-charts-react";
import {
  AgChartsInstance,
  AgFinancialChartOptions,
  FinancialChartModule,
  ModuleRegistry,
} from "ag-charts-enterprise";
import { Candle, MS_PER_DAY, PriceSimulator, getHistoricalData } from "./data";
import { random } from "./seededRandom";
import clone from "clone";

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 ChartExample = () => {
  const chartRef = useRef<AgChartsInstance>(null);
  const [options, setOptions] = useState<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 },
      },
    },
  });

  const 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);
    chartRef.current!.applyTransaction({ add: [currentCandle] });
  };

  const 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);
    chartRef.current!.applyTransaction({ update: [currentCandle] });
  };

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

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

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

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

  return (
    <Fragment>
      <div className="example-controls">
        <div className="controls-row">
          <button id="toggleBtn" onClick={toggleUpdates}>
            Start
          </button>
        </div>
      </div>
      <AgFinancialCharts ref={chartRef} options={options} />
    </Fragment>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
```

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