The CategoryAxis treats values a little differently. This is a special axis type which uses the X-Index not the X-Value to measure chart series.
Difference between CategoryAxis and NumericAxis
Imagine you want to plot the data:
Age (XAxis) | Cats | Dogs | Fish |
1 | 7 | 7 | 8 |
9 | 6 | 5 | 7 |
10 | 5 | 4 | 3 |
20 | 4 | 3 | 2 |
A standard NumericAxis (which is a value-axis) and the CategoryAxis would display the data differently.
<div class="container"> <div id="scichart0" class="box"></div> <div id="scichart1" class="box"></div> </div>
body { margin:0; padding:0; } .container { width: 100%; height: 100vh; background-color:#fff; display:grid; grid-template-columns: 50% 50%; grid-row: auto auto; .box{ background-color:#333; margin: 20px 0px 0px 20px; border: 1px solid red; color:#fff; display:flex; align-items:center; justify-content:center; font-size:40px; font-family:sans-serif; } } .scichart__legend-item { font-family: Arial; }
async function categoryAxisVsValueAxis() { // Compares CategoryAxis vs. Value Axis in SciChart.js const { SciChartSurface, CategoryAxis, SciChartJsNavyTheme, NumericAxis, TextAnnotation, ECoordinateMode, EHorizontalAnchorPoint, EVerticalAnchorPoint, FastLineRenderableSeries, XyDataSeries, EllipsePointMarker, NumberRange, ENumericFormat, LegendModifier, ELegendOrientation, ELegendPlacement } = SciChart; // or, for npm, import { SciChartSurface, ... } from "scichart" const addChartTitle = (sciChartSurface, titleText, subTitleText) => { // Note: we will be improving this shortly in scichart.js v3.1 sciChartSurface.annotations.add(new TextAnnotation({ text: titleText, x1: 0.5, y1: 0, yCoordShift: 10, xCoordinateMode: ECoordinateMode.Relative, yCoordinateMode: ECoordinateMode.Relative, horizontalAnchorPoint: EHorizontalAnchorPoint.Center, verticalAnchorPoint: EVerticalAnchorPoint.Top, opacity: 0.77, fontSize: 28, fontWeight: "Bold", textColor: "White", })); sciChartSurface.annotations.add(new TextAnnotation({ text: subTitleText, x1: 0.5, y1: 0, yCoordShift: 50, xCoordinateMode: ECoordinateMode.Relative, yCoordinateMode: ECoordinateMode.Relative, horizontalAnchorPoint: EHorizontalAnchorPoint.Center, verticalAnchorPoint: EVerticalAnchorPoint.Top, opacity: 0.77, fontSize: 14, textColor: "White", })); }; const addLineSeries = (sciChartSurface, xValues, yValues, stroke, dataSeriesName) => { sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(sciChartSurface.webAssemblyContext2D, { dataSeries: new XyDataSeries(sciChartSurface.webAssemblyContext2D, { xValues, yValues, dataSeriesName}), stroke, strokeThickness: 2, pointMarker: new EllipsePointMarker(sciChartSurface.webAssemblyContext2D, { width: 7, height: 7, fill: stroke, stroke}), })); }; const addLegend = (sciChartSurface) => { sciChartSurface.chartModifiers.add(new LegendModifier({ orientation: ELegendOrientation.Horizontal, placement: ELegendPlacement.BottomLeft })); }; const createCategoryAxisChart = async (divElementId) => { // #region ExampleA // With the following data const xValues = [1, 9, 10, 20]; const cats = [7, 6, 5, 4]; const dogs = [7, 5, 4, 3]; const fish = [8, 7, 3, 2]; // create a chart const { wasmContext, sciChartSurface } = await SciChartSurface.create(divElementId, { theme: new SciChartJsNavyTheme() }); // Add a Category XAxis with numeric formatting sciChartSurface.xAxes.add(new CategoryAxis(wasmContext, { axisTitle: "Category Axis", labelFormat: ENumericFormat.Decimal })); sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { growBy: new NumberRange(0.2, 0.2)})); // #endregion // Add titles + series addChartTitle(sciChartSurface, "Category XAxis", "Index is used to measure X-Distance"); addLineSeries(sciChartSurface, xValues, cats, "#50C7E0", "Cats"); addLineSeries(sciChartSurface, xValues, dogs, "#F48420", "Dogs"); addLineSeries(sciChartSurface, xValues, fish, "#C52E60", "Fish"); addLegend(sciChartSurface); }; const createValueAxisChart = async (divElementId) => { // #region ExampleB // With the following data const xValues = [1, 9, 10, 20]; const cats = [7, 6, 5, 4]; const dogs = [7, 5, 4, 3]; const fish = [8, 7, 3, 2]; const { wasmContext, sciChartSurface } = await SciChartSurface.create(divElementId, { theme: new SciChartJsNavyTheme() }); sciChartSurface.xAxes.add(new NumericAxis(wasmContext, { axisTitle: "Numeric Axis" })); sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { growBy: new NumberRange(0.2, 0.2) })); addChartTitle(sciChartSurface, "Value XAxis", "X Values are used to measure X-Distance"); addLineSeries(sciChartSurface, xValues, cats, "#50C7E0", "Cats"); addLineSeries(sciChartSurface, xValues, dogs, "#F48420", "Dogs"); addLineSeries(sciChartSurface, xValues, fish, "#C52E60", "Fish"); addLegend(sciChartSurface); // #endregion }; createCategoryAxisChart("scichart0"); createValueAxisChart("scichart1"); }; categoryAxisVsValueAxis(); // // Uncomment this to use the builder example // builderExample();
Here's the code to setup the chart.
Why is this important? In the case where you are plotting stock market data, you want to use a CategoryAxis.
This is because stock market data has gaps in it (consider the stock market has data on Monday, Tuesday, Wednesday, Thursday, Friday but not weekends). The CategoryAxis collapses the gaps and treats each datapoint as equidistant, ignoring the X-Value.
Using and Configuring a CategoryAxis
The Category Axis won't draw without either:
- At least one series with some data on the chart,
- or, CategoryAxis.defaultXStart and defaultXStep (allows a chart without any series)
- or CategoryAxis.defaultXValues (allows a chart without any series)
Creating a CategoryAxis (without Data) using Defaults
The first example we're going to show uses the default properties on the CategoryAxis to display a chart without data.
This results in the following:
<div id="scichart-root" ></div>
body { margin: 0; } #scichart-root { width: 100%; height: 100vh; }
async function chartWithCategoryAxis(divElementId) { // Demonstrates how to configure a DateTimeNumericAxis in SciChart.js const { SciChartSurface, CategoryAxis, SciChartJsNavyTheme, EAxisAlignment, NumericAxis, ZoomPanModifier, MouseWheelZoomModifier, CursorModifier, TextAnnotation, ECoordinateMode, EHorizontalAnchorPoint, EVerticalAnchorPoint, ENumericFormat } = SciChart; // or, for npm, import { SciChartSurface, ... } from "scichart" // #region ExampleA const { wasmContext, sciChartSurface } = await SciChartSurface.create(divElementId, { theme: new SciChartJsNavyTheme() }); // Unix Epoch for March 1st 2023 & March 2nd 2023 const march1st2023 = new Date("2023-03-1").getTime(); // = 1677628800000 ms since 1/1/1970 const march2nd2023 = new Date("2023-03-2").getTime(); // = 1677715200000 ms since 1/1/1970 const oneDay = march2nd2023 - march1st2023; // = 86400000 milliseconds in one day // Creating a CategoryAxis as an XAxis on the bottom sciChartSurface.xAxes.add(new CategoryAxis(wasmContext, { // set Defaults so that category axis can draw. Once you add series and data these will be overridden // All Linux Timestamp properties in scichart.js must be divided by 1,000 to go from milliseconds to seconds defaultXStart: march1st2023 / 1000, defaultXStep: oneDay / 1000, // set other properties drawMajorGridLines: true, drawMinorGridLines: true, axisTitle: "Category X Axis", axisAlignment: EAxisAlignment.Bottom, // set a date format for labels labelFormat: ENumericFormat.Date_DDMMYY })); // Create a YAxis on the left sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { axisTitle: "Numeric Y Axis", axisAlignment: EAxisAlignment.Left, })); // #endregion // For the example, we add zooming, panning and an annotation so you can see how dates react on zoom. sciChartSurface.chartModifiers.add(new ZoomPanModifier(), new MouseWheelZoomModifier(), new CursorModifier()); // Add annotations to tell the user what to do sciChartSurface.annotations.add(new TextAnnotation({ text: "CategoryAxis Demo", x1: 0.5, y1: 0.5, yCoordShift: 0, xCoordinateMode: ECoordinateMode.Relative, yCoordinateMode: ECoordinateMode.Relative, horizontalAnchorPoint: EHorizontalAnchorPoint.Center, verticalAnchorPoint: EVerticalAnchorPoint.Center, opacity: 0.33, fontSize: 36, fontWeight: "Bold" })); sciChartSurface.annotations.add(new TextAnnotation({ text: "Try mouse-wheel, left/right mouse drag and notice the dynamic X-Axis Labels", x1: 0.5, y1: 0.5, yCoordShift: 50, xCoordinateMode: ECoordinateMode.Relative, yCoordinateMode: ECoordinateMode.Relative, horizontalAnchorPoint: EHorizontalAnchorPoint.Center, verticalAnchorPoint: EVerticalAnchorPoint.Center, opacity: 0.45, fontSize: 17, })); }; chartWithCategoryAxis("scichart-root"); async function builderExample(divElementId) { // Demonstrates how to create a line chart with SciChart.js using the Builder API const { chartBuilder, EThemeProviderType, NumberRange, EAxisAlignment, EAxisType, } = SciChart; // or, for npm, import { chartBuilder, ... } from "scichart" // #region ExampleB // Unix Epoch for March 1st 2023 & March 2nd 2023 const march1st2023 = new Date("2023-03-1").getTime(); // = 1677628800000 ms since 1/1/1970 const march2nd2023 = new Date("2023-03-2").getTime(); // = 1677715200000 ms since 1/1/1970 const oneDay = march2nd2023 - march1st2023; // = 86400000 milliseconds in one day const { wasmContext, sciChartSurface } = await chartBuilder.build2DChart(divElementId, { surface: { theme: { type: EThemeProviderType.Dark } }, xAxes: { type: EAxisType.CategoryAxis, options: { // set Defaults so that category axis can draw. Once you add series and data these will be overridden // All Linux Timestamp properties in scichart.js must be divided by 1,000 to go from milliseconds to seconds defaultXStart: march1st2023 / 1000, defaultXStep: oneDay / 1000, // set other properties drawMajorGridLines: true, drawMinorGridLines: true, axisTitle: "Category X Axis", axisAlignment: EAxisAlignment.Bottom, // set a date format for labels labelFormat: ENumericFormat.Date_DDMMYY } }, yAxes: { type: EAxisType.NumericAxis, options: { axisTitle: "Numeric Y Axis", axisAlignment: EAxisAlignment.Left, } }, }); // #endregion }; // Uncomment this to use the builder example //builderExample("scichart-root");
In the above code sample we set the CategoryAxis.defaultXStart = unix timestamp for March 1st 2023, and defaultXStep = number of seconds in one day. This tells SciChart.js to default the index calculation to 1-datapoint = 1 day and to start the xAxis from 1st March.
Once you apply data to the CategoryAxis these properties will be ignored. They are only required to create and show a chart using CategoryAxis without data.
Creating a CategoryAxis with Financial Data
Let's create a chart with CategoryAxis by supplying some data below:
This results in the following output:
<div id="scichart-root" ></div>
body { margin: 0; } #scichart-root { width: 100%; height: 100vh; }
// Helper function to fetch candlestick data from Binance via Rest API const getCandles = async ( symbol, interval, limit = 300 ) => { let url = `https://api.binance.com/api/v3/klines?symbol=${symbol}&interval=${interval}`; if (limit) { url += `&limit=${limit}`; } try { console.log(`SimpleBinanceClient: Fetching ${limit} candles of ${symbol} ${interval}`); const response = await fetch(url); // Returned data format is [ { date, open, high, low, close, volume }, ... ] const data = await response.json(); // Map to { dateValues[], openValues[], highValues[], lowValues[], closeValues[] } expected by scichart.js const dateValues = []; const openValues = []; const highValues = []; const lowValues = []; const closeValues = []; const volumeValues = []; data.forEach(candle => { const [timestamp, open, high, low, close, volume] = candle; dateValues.push(timestamp / 1000); // SciChart expects Unix Timestamp / 1000 openValues.push(parseFloat(open)); highValues.push(parseFloat(high)); lowValues.push(parseFloat(low)); closeValues.push(parseFloat(close)); volumeValues.push(parseFloat(volume)); }); return { dateValues, openValues, highValues, lowValues, closeValues, volumeValues }; } catch (err) { console.error(err); return []; } }; async function chartWithCategoryAxis(divElementId) { // #region ExampleA // Demonstrates how to create a chart with a CategoryAxis in SciChart.js const { SciChartSurface, CategoryAxis, SciChartJsNavyTheme, EAxisAlignment, NumericAxis, ZoomPanModifier, MouseWheelZoomModifier, TextAnnotation, ECoordinateMode, EHorizontalAnchorPoint, EVerticalAnchorPoint, ENumericFormat, FastCandlestickRenderableSeries, OhlcDataSeries, SmartDateLabelProvider, NumberRange, EAutoRange } = SciChart; // or, for npm, import { SciChartSurface, ... } from "scichart" const { wasmContext, sciChartSurface } = await SciChartSurface.create(divElementId, { theme: new SciChartJsNavyTheme() }); // Creating a CategoryAxis as an XAxis on the bottom sciChartSurface.xAxes.add(new CategoryAxis(wasmContext, { // set other properties drawMajorGridLines: true, drawMinorGridLines: true, axisTitle: "Category X Axis", axisAlignment: EAxisAlignment.Bottom, // set a date format for labels labelProvider: new SmartDateLabelProvider() })); // Create a YAxis on the left sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { axisTitle: "Numeric Y Axis", labelPrefix: "$", labelPrecision: 2, labelFormat: ENumericFormat.Decimal, axisAlignment: EAxisAlignment.Right, autoRange: EAutoRange.Always, growBy: new NumberRange(0.1, 0.1) })); // The category axis requires some data to work (unless you set defaultStartX/StepX properties) // so, lets add some data with dates to the chart // Data format is { dateValues[], openValues[], highValues[], lowValues[], closeValues[] } const { dateValues, openValues, highValues, lowValues, closeValues, volumeValues } = await getCandles("BTCUSDT", "1h", 100); // Create and add the Candlestick series const candlestickSeries = new FastCandlestickRenderableSeries(wasmContext, { strokeThickness: 1, dataSeries: new OhlcDataSeries(wasmContext, { xValues: dateValues, openValues, highValues, lowValues, closeValues, }), dataPointWidth: 0.7, brushUp: "#33ff3377", brushDown: "#ff333377", strokeUp: "#77ff77", strokeDown: "#ff7777", }); sciChartSurface.renderableSeries.add(candlestickSeries); // #endregion // For the example, we add zooming, panning and an annotation so you can see how dates react on zoom. sciChartSurface.chartModifiers.add(new ZoomPanModifier(), new MouseWheelZoomModifier()); // Add annotations to tell the user what to do sciChartSurface.annotations.add(new TextAnnotation({ text: "CategoryAxis Demo", x1: 0.5, y1: 0.5, yCoordShift: 0, xCoordinateMode: ECoordinateMode.Relative, yCoordinateMode: ECoordinateMode.Relative, horizontalAnchorPoint: EHorizontalAnchorPoint.Center, verticalAnchorPoint: EVerticalAnchorPoint.Center, opacity: 0.33, fontSize: 36, fontWeight: "Bold" })); sciChartSurface.annotations.add(new TextAnnotation({ text: "Try mouse-wheel, left/right mouse drag and notice the dynamic X-Axis Labels", x1: 0.5, y1: 0.5, yCoordShift: 50, xCoordinateMode: ECoordinateMode.Relative, yCoordinateMode: ECoordinateMode.Relative, horizontalAnchorPoint: EHorizontalAnchorPoint.Center, verticalAnchorPoint: EVerticalAnchorPoint.Center, opacity: 0.45, fontSize: 17, })); // For the example, add a cursor sciChartSurface.chartModifiers.add(new SciChart.CursorModifier()); }; chartWithCategoryAxis("scichart-root"); // Uncomment this to use the builder example //builderExample("scichart-root");