React Market Depth Chart

How to create a Market Depth (Order Book) React Chart using Mountain Series and a Custom Modifier

Fullscreen

Edit

 Edit

Docs

src/drawExample.ts
import { appTheme } from "./appTheme";

import {
    SciChartSurface,
    MouseWheelZoomModifier,
    ZoomExtentsModifier,
    XyDataSeries,
    NumericAxis,
    FastMountainRenderableSeries,
    NumberRange,
    EAutoRange,
    EXyDirection,
    EAxisAlignment,
} from "scichart";
import { DepthCursorModifier } from "./DepthCursorModifier";

// SCICHART EXAMPLE

export const drawExample = async (rootElement: string | HTMLDivElement) => {
    // Create a SciChartSurface
    const { wasmContext, sciChartSurface } = await SciChartSurface.create(rootElement, {
        theme: appTheme.SciChartJsTheme,
    });

    const xAxis = new NumericAxis(wasmContext, {
        axisAlignment: EAxisAlignment.Top,
        labelPrecision: 4,
        rotation: 90,
    });

    sciChartSurface.xAxes.add(xAxis);

    const yAxis = new NumericAxis(wasmContext, {
        autoRange: EAutoRange.Always,
        growBy: new NumberRange(0, 0.05),
    });
    sciChartSurface.yAxes.add(yAxis);

    const AAPL_data = {
        buy: [
            { price: 132.79743, volume: 339 },
            { price: 132.79742, volume: 713 },
            { price: 132.79741, volume: 421 },
            { price: 132.7974, volume: 853 },
            { price: 132.79739, volume: 152 },
            { price: 132.79738, volume: 243 },
            { price: 132.79737, volume: 296 },
            { price: 132.79736, volume: 123 },
            { price: 132.79735, volume: 158 },
            { price: 132.79734, volume: 238 },
            { price: 132.79733, volume: 164 },
            { price: 132.79732, volume: 273 },
            { price: 132.79731, volume: 35 },
            { price: 132.79729, volume: 30 },
            { price: 132.79726, volume: 29 },
            { price: 132.79722, volume: 484 },
            { price: 132.79721, volume: 458 },
            { price: 132.7972, volume: 244 },
            { price: 132.79719, volume: 10 },
            { price: 132.79698, volume: 124 },
        ],
        sell: [
            { price: 132.79744, volume: 847 },
            { price: 132.79745, volume: 2412 },
            { price: 132.79746, volume: 635 },
            { price: 132.79747, volume: 323 },
            { price: 132.79748, volume: 828 },
            { price: 132.79749, volume: 322 },
            { price: 132.7975, volume: 268 },
            { price: 132.79751, volume: 92 },
            { price: 132.79752, volume: 249 },
            { price: 132.79753, volume: 189 },
            { price: 132.79754, volume: 179 },
            { price: 132.79755, volume: 122 },
            { price: 132.79756, volume: 28 },
            { price: 132.7976, volume: 114 },
            { price: 132.79764, volume: 27 },
            { price: 132.79767, volume: 10 },
            { price: 132.79772, volume: 31 },
            { price: 132.79785, volume: 484 },
            { price: 132.79786, volume: 364 },
            { price: 132.79787, volume: 244 },
        ],
    };

    const buyValues: number[] = [];
    let totalVol = 0;
    for (const v of AAPL_data.buy) {
        totalVol += v.volume;
        buyValues.push(totalVol);
    }
    const sellValues: number[] = [];
    totalVol = 0;
    for (const v of AAPL_data.sell) {
        totalVol += v.volume;
        sellValues.push(totalVol);
    }

    const buySeries = new FastMountainRenderableSeries(wasmContext, {
        dataSeries: new XyDataSeries(wasmContext, { xValues: AAPL_data.buy.map((v) => v.price), yValues: buyValues }),
        stroke: "green",
        fill: "00890033",
        strokeThickness: 2,
        isDigitalLine: true,
    });
    const sellSeries = new FastMountainRenderableSeries(wasmContext, {
        dataSeries: new XyDataSeries(wasmContext, { xValues: AAPL_data.sell.map((v) => v.price), yValues: sellValues }),
        stroke: "red",
        fill: "89000033",
        strokeThickness: 2,
        isDigitalLine: true,
    });
    sciChartSurface.renderableSeries.add(buySeries, sellSeries);

    xAxis.tickProvider.getMajorTicks = (minor, major, visibleRange) => {
        const ticks: number[] = [];
        const threshold = 400;
        const buyYs = buySeries.dataSeries.getNativeYValues();
        const buyXs = buySeries.dataSeries.getNativeXValues();
        let lastY = 0;
        for (let i = 0; i < buySeries.dataSeries.count(); i++) {
            const y = buyYs.get(i);
            if (y - lastY > threshold) {
                ticks.push(buyXs.get(i));
            }
            lastY = y;
        }
        const sellYs = sellSeries.dataSeries.getNativeYValues();
        const sellXs = sellSeries.dataSeries.getNativeXValues();
        lastY = 0;
        for (let i = 0; i < sellSeries.dataSeries.count(); i++) {
            const y = sellYs.get(i);
            if (y - lastY > threshold) {
                ticks.push(sellXs.get(i));
            }
            lastY = y;
        }
        return ticks.sort((a, b) => a - b);
    };

    const depthModifier = new DepthCursorModifier({
        buySeries,
        sellSeries,
        crosshairStrokeDashArray: [3, 2],
        crosshairStrokeThickness: 3,
        axisLabelFill: "transparent",
    });
    depthModifier.highlightColor = appTheme.DarkIndigo;
    // Optional: Add some interactivity to the chart
    sciChartSurface.chartModifiers.add(
        new ZoomExtentsModifier(),
        new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }),
        depthModifier
    );

    sciChartSurface.zoomExtents();
    xAxis.visibleRangeLimit = xAxis.visibleRange;
    yAxis.visibleRangeLimit = yAxis.visibleRange;
    return { sciChartSurface };
};

React Market Depth Chart

Overview

This React Charts example demonstrates a high performance Market Depth (Order Book) Chart built with SciChart.js in a React environment.

Technical Implementation

The chart is initialized using the <SciChartReact/> component with an initChart prop that assigns the drawExample function. Within this function, two mountain series are created to represent cumulative buy and sell volumes, and a custom modifier (the DepthCursorModifier) is implemented for enhanced mouse interaction, hit testing, and annotation management. The custom modifier updates crosshair lines, text labels, and marker annotations dynamically based on the mouse position. Developers can explore the detailed implementation of custom modifiers in the Custom Chart Modifier API documentation.

Features and Capabilities

The example showcases real-time update capabilities as the custom modifier responds to mouse movements over the chart, dynamically highlighting corresponding data points on both series. Advanced features include coordinate translation, DPI scaling, and custom annotations for enhanced visualization of buy and sell data.

Integration and Best Practices

This implementation follows best practices for integrating SciChart.js within a React application. The use of the <SciChartReact/> component simplifies initialization and cleanup of the SciChartSurface, ensuring optimal performance and resource management. Developers are encouraged to review guidelines for initializing and cleaning up the chart as described in Creating a SciChart React Component from the Ground Up and to apply the performance optimization techniques detailed in Performance Optimisation of JavaScript Applications & Charts. Additionally, advanced mouse event handling for custom modifiers, including hit testing and dynamic annotations, is implemented, as discussed in the Detecting Clicks On Chart Parts with a Custom Modifier resource.