Does SciChart Support Technical Indicators like MACD, SMA, EMA, RSI?
Posted by Andrew BT on 04 February 2019 08:13 PM
|
|
Q: Does SciChart support Technical Indicators like MACD, SMA, EMA? A: Our charting library lets you plot any data in the formats Line, Scatter, Column, Mountain, Banded-area (plus many more). Also, it provides a Filters API which can be used to implement Technical Indicators. Among those provided out-of-the-box are Moving Average, Linear and Polynomial Trendlines, Spline Interpolation and others. For those that are missing, you can either write your own, or use a third-party technical indicator library such as the open source TA-Lib (C++) or the Trady Library (which is .NET). It then becomes a simple exercise of plugging the outputs of the Technical Indicator library into SciChart's DataSeries. Our demos include some indicators like MACD, SMA to show you how you could integrate these with SciChart. See specifically the code sample in our Create Multipane Stock Charts example. The example of data filters can be found in the Chart Filters API example. The source-code for our examples can be found in your Installation Folder if you have installed the Trial of SciChart. Our SMA code is effectively open source and listed here. We demonstrate how to create a fast, lightweight Simple Moving Average, then to LINQ-ify it! Simple Moving Average Code public class MovingAverage { private readonly int _length; private int _circIndex = -1; private bool _filled; private double _current = double.NaN; private readonly double _oneOverLength; private readonly double[] _circularBuffer; private double _total; public MovingAverage(int length) { _length = length; _oneOverLength = 1.0 / length; _circularBuffer = new double[length]; } public MovingAverage Update(double value) { double lostValue = _circularBuffer[_circIndex]; _circularBuffer[_circIndex] = value; // Maintain totals for Push function _total += value; _total -= lostValue; // If not yet filled, just return. Current value should be double.NaN if (!_filled) { _current = double.NaN; return this; } // Compute the average double average = 0.0; for (int i = 0; i < _circularBuffer.Length; i++) { average += _circularBuffer[i]; } _current = average * _oneOverLength; return this; } public MovingAverage Push(double value) { // Apply the circular buffer if (++_circIndex == _length) { _circIndex = 0; } double lostValue = _circularBuffer[_circIndex]; _circularBuffer[_circIndex] = value; // Compute the average _total += value; _total -= lostValue; // If not yet filled, just return. Current value should be double.NaN if (!_filled && _circIndex != _length - 1) { _current = double.NaN; return this; } else { // Set a flag to indicate this is the first time the buffer has been filled _filled = true; } _current = _total * _oneOverLength; return this; } public int Length { get { return _length; } } public double Current { get { return _current; } } } This class provides a very fast and lightweight implementation of a MovingAverage filter. It creates a circular buffer of Length N and computes one add, one subtract and one multiply per data-point appended, as opposed to the N multiply-adds per point for the brute force implementation. LINQ Extension Method for Simple Moving Average internal static class MovingAverageExtensions { public static IEnumerable<double> MovingAverage<T>(this IEnumerable<T> inputStream, Func<T, double> selector, int period) { var ma = new MovingAverage(period); foreach (var item in inputStream) { ma.Push(selector(item)); yield return ma.Current; } } public static IEnumerable<double> MovingAverage(this IEnumerable<double> inputStream, int period) { var ma = new MovingAverage(period); foreach (var item in inputStream) { ma.Push(item); yield return ma.Current; } } } Now to use it! int period = 50; // Simply filtering a list of doubles IEnumerable<double> inputDoubles; IEnumerable<double> outputDoubles = inputDoubles.MovingAverage(period); // Or, use a selector to filter T into a list of doubles IEnumerable<Point> inputPoints; // assuming you have initialised this IEnumerable<double> smoothedYValues = inputPoints.MovingAverage(pt => pt.Y, period);
Integrating Moving Averages to DataSeriesYou can imagine that if you use the above in conjunction with our DataSeries you can easily push filtered data to the SciChartSurface. Take a look at PricePaneViewModel.cs in the Create MultiPane Stock Charts example. The source-code for the examples is also on disk if you have installed the Trial version of SciChart. // See PricePaneViewModel.cs in the Create Multi-Pane Stock Charts demo // Assumes you have a series of Time, Open, High, Low, Close data PriceSeries prices; // Add the main OHLC chart var stockPrices = new OhlcDataSeries<DateTime, double>(); stockPrices.Append(prices.TimeData, prices.OpenData, prices.HighData, prices.LowData, prices.CloseData); // Add a moving average var maLow = new XyDataSeries<DateTime, double>(); maLow.Append(prices.TimeData, prices.CloseData.MovingAverage(50));
What about the MACD and RSI?We also demonstrate a MACD and RSI implementation in the examples source-code. The source-code to the examples can be found in your Installation Folder if you have installed the SciChart trial. MACD Implementation and Usage // Fast Macd LINQ Extension method provided for example purposes on an AS IS BASIS ONLY. ACCURACY OF CALCULATION NOT GAURANTEED // See MovingAverage.cs in the Examples Suite source-code public static IEnumerable<MacdPoint> Macd(this IEnumerable<double> inputStream, int slow, int fast, int signal) { var maSlow = new MovingAverage(slow); var maFast = new MovingAverage(fast); var maSignal = new MovingAverage(signal); foreach (var item in inputStream) { double macd = maSlow.Push(item).Current - maFast.Push(item).Current; double signalLine = double.IsNaN(macd) ? double.NaN : maSignal.Push(macd).Current; double divergence = double.IsNaN(macd) || double.IsNaN(signalLine) ? double.NaN : macd - signalLine; yield return new MacdPoint() { Macd = macd, Signal = signalLine, Divergence = divergence }; } } // How to use the MACD Extension method // See MacdPaneViewModel.cs in the Examples Suite Source-Code IEnumerable<MacdPoint> macdPoints = prices.CloseData.Macd(12, 26, 9).ToList(); var histogramDataSeries = new XyDataSeries<DateTime, double>(); histogramDataSeries.Append(prices.TimeData, macdPoints.Select(x => x.Divergence)); var macdDataSeries = new XyyDataSeries<DateTime, double>(); macdDataSeries.Append(prices.TimeData, macdPoints.Select(x => x.Macd), macdPoints.Select(x => x.Signal));
What about Other Indicators?We recommend taking a look at the Open-source Trady library for .NET applications, or TA-Lib for C++ applications.
What about Reactive Extensions?We don't a sample just yet to use Reactive Extensions to push data through a moving average, but this would be a natural fit for trading systems. If you don't know about Reactive Extensions, you need to - read here. Further ReadingMore information about the Filters API can be found in dedicated articles in our documentation:
Examples can be found in the SciChart Examples Suite under the Filters API section: Third-party open-source libraries that provide Technical Indicators implementation:
| |
|