Trendline, Moving Average and Ratio Filters

Demonstrates how use Linear Trend, Moving Average and Ratio Filters with filter chaining, using SciChart.js, High Performance JavaScript Charts

Fullscreen

Edit

 Edit

Docs

drawExample.ts

index.html

vanilla.ts

RandomWalkGenerator.ts

theme.ts

Copy to clipboard
Minimise
Fullscreen
1import { appTheme } from "../../../theme";
2import { RandomWalkGenerator } from "../../../ExampleData/RandomWalkGenerator";
3
4import {
5    EAxisAlignment,
6    ECoordinateMode,
7    EHorizontalAnchorPoint,
8    EVerticalAnchorPoint,
9    ELegendOrientation,
10    ELegendPlacement,
11    NumberRange,
12    TextAnnotation,
13    FastLineRenderableSeries,
14    LegendModifier,
15    MouseWheelZoomModifier,
16    NumericAxis,
17    SciChartSurface,
18    XyDataSeries,
19    XyLinearTrendFilter,
20    XyMovingAverageFilter,
21    XyRatioFilter,
22    XyScaleOffsetFilter,
23    ZoomExtentsModifier,
24    ZoomPanModifier,
25    NativeTextAnnotation,
26    EWrapTo,
27    EMultiLineAlignment,
28} from "scichart";
29
30const RATIO_YAXIS_ID = "RatioYAxisId";
31
32export const drawExample = async (rootElement: string | HTMLDivElement) => {
33    const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement, {
34        theme: appTheme.SciChartJsTheme,
35    });
36
37    sciChartSurface.xAxes.add(
38        new NumericAxis(wasmContext, {
39            axisTitle: "X Axis",
40        })
41    );
42
43    sciChartSurface.yAxes.add(
44        new NumericAxis(wasmContext, {
45            axisAlignment: EAxisAlignment.Left,
46            axisTitle: "Original Data Y Axis",
47            growBy: new NumberRange(0.1, 0.1),
48        })
49    );
50
51    sciChartSurface.yAxes.add(
52        new NumericAxis(wasmContext, {
53            axisAlignment: EAxisAlignment.Right,
54            axisTitle: "Ratio Axis",
55            id: RATIO_YAXIS_ID,
56            growBy: new NumberRange(0.1, 0.1),
57            visibleRange: new NumberRange(-20, 20),
58        })
59    );
60
61    // Create an original Data Series with some X,Y data
62    const data1 = new RandomWalkGenerator().Seed(420).getRandomWalkSeries(500);
63    const originalDataSeries = new XyDataSeries(wasmContext, {
64        xValues: data1.xValues,
65        yValues: data1.yValues,
66        dataSeriesName: "Original",
67    });
68    sciChartSurface.renderableSeries.add(
69        new FastLineRenderableSeries(wasmContext, {
70            strokeThickness: 3,
71            stroke: appTheme.VividSkyBlue,
72            dataSeries: originalDataSeries,
73        })
74    );
75
76    // Compute a moving average using filters API and apply to the chart
77    sciChartSurface.renderableSeries.add(
78        new FastLineRenderableSeries(wasmContext, {
79            stroke: appTheme.VividRed,
80            strokeThickness: 3,
81            dataSeries: new XyMovingAverageFilter(originalDataSeries, {
82                length: 10,
83                dataSeriesName: "Moving Average (10)",
84            }),
85        })
86    );
87
88    // Compute a moving average using filters API and apply to the chart
89    sciChartSurface.renderableSeries.add(
90        new FastLineRenderableSeries(wasmContext, {
91            stroke: appTheme.VividOrange,
92            strokeThickness: 3,
93            dataSeries: new XyMovingAverageFilter(originalDataSeries, {
94                length: 20,
95                dataSeriesName: "Moving Average (20)",
96            }),
97        })
98    );
99
100    // Compute an offset of the original series
101    const offsetSeries = new XyScaleOffsetFilter(originalDataSeries, {
102        offset: -0.5,
103        scale: 2,
104        dataSeriesName: "Offset -0.5 / Scaled x2",
105    });
106    sciChartSurface.renderableSeries.add(
107        new FastLineRenderableSeries(wasmContext, {
108            stroke: appTheme.VividSkyBlue + "33",
109            strokeThickness: 3,
110            dataSeries: offsetSeries,
111        })
112    );
113
114    // Compute a trendline
115    sciChartSurface.renderableSeries.add(
116        new FastLineRenderableSeries(wasmContext, {
117            stroke: appTheme.MutedPurple,
118            strokeDashArray: [3, 3],
119            strokeThickness: 3,
120            dataSeries: new XyLinearTrendFilter(originalDataSeries, { dataSeriesName: "Linear Trendline" }),
121        })
122    );
123
124    // Compute a ratio between the trendline & the original series
125    sciChartSurface.renderableSeries.add(
126        new FastLineRenderableSeries(wasmContext, {
127            strokeThickness: 3,
128            stroke: appTheme.MutedRed,
129            yAxisId: RATIO_YAXIS_ID,
130            dataSeries: new XyRatioFilter(originalDataSeries, {
131                divisorSeries: offsetSeries,
132                dataSeriesName: "Ratio (Original vs. Offset)",
133            }),
134        })
135    );
136
137    // Add a title over the chart with information
138    sciChartSurface.annotations.add(
139        new NativeTextAnnotation({
140            x1: 0.02,
141            y1: 0.02,
142            xCoordinateMode: ECoordinateMode.Relative,
143            yCoordinateMode: ECoordinateMode.Relative,
144            horizontalAnchorPoint: EHorizontalAnchorPoint.Left,
145            verticalAnchorPoint: EVerticalAnchorPoint.Top,
146            fontSize: 18,
147            opacity: 0.55,
148            textColor: appTheme.ForegroundColor,
149            text: "SciChart.js supports dynamic transforms like Moving Averages, Trendlines, Ratios",
150            wrapTo: EWrapTo.ViewRect,
151            multiLineAlignment: EMultiLineAlignment.Left,
152        })
153    );
154
155    // Optional: add some chartmodifiers for interaction and to show the legend
156    sciChartSurface.chartModifiers.add(
157        new MouseWheelZoomModifier(),
158        new ZoomPanModifier({ enableZoom: true }),
159        new ZoomExtentsModifier(),
160        new LegendModifier({ placement: ELegendPlacement.BottomLeft, orientation: ELegendOrientation.Horizontal })
161    );
162
163    return { sciChartSurface, wasmContext };
164};
165

Trendline, Moving Average and Ratio Filters in JavaScript

Overview

This example demonstrates how to integrate SciChart.js with a JavaScript application to create a high-performance financial chart that applies dynamic data transformations. It showcases advanced technical analysis filters including Moving Average, Linear Trendline and Ratio filters, while also configuring dual y-axes for displaying both the original and transformed data.

Technical Implementation

The chart is initialized asynchronously using SciChart.js’s native asynchronous API, where a WebAssembly context is created by calling SciChartSurface.create and loading Wasm. Data is generated using a random walk generator and then processed through several filters, including two distinct Moving Average filters with different lengths, an offset and scale filter, a Linear Trendline filter, and finally, a Ratio filter that computes the ratio between the original data series and its transformed version. The filter chaining demonstrates how multiple data transformations can be applied step-by-step as described in the Transforming Data with Filters documentation.

Features and Capabilities

The implementation includes several advanced features such as:

  • Dual y-axis configuration to clearly distinguish between original data and ratio values, following best practices outlined in the Tutorial 08 - Adding Multiple Axis guide.
  • Interactive chart modifiers like MouseWheelZoomModifier, ZoomPanModifier, ZoomExtentsModifier and a LegendModifier that enhance user interactivity. More details on these can be found in the MouseWheelZoomModifier Documentation.
  • Annotations using the NativeTextAnnotation to overlay descriptive text with relative coordinates, adding additional context to the chart.

Integration and Best Practices

The example demonstrates best practices for integrating SciChart.js into a JavaScript project. Developers can follow the step-by-step asynchronous initialization process as detailed in the Getting Started with SciChart JS guide. Furthermore, resource cleanup is handled through a destructor function that properly disposes of chart objects, ensuring efficient memory management as recommended in the Memory Best Practices | JavaScript Chart Documentation. This implementation not only highlights the usage of dynamic filter chaining and dual-axis configuration, but also shows how to optimize performance when working with WebAssembly-powered charts.

SciChart Ltd, 16 Beaufort Court, Admirals Way, Docklands, London, E14 9XL.