Does SciChart Support Technical Indicators like MACD, SMA, EMA, RSI?
Posted by Andrew BT on 17 October 2014 08:10 AM

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). We don't provide technical indicators to generate the data. For this, you can either write your own, or use a third-party technical indicator library such as the open source TA-Lib

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 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 DataSeries

You 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 TA-Lib library.

 

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

 

 

 

 

(0 vote(s))
Helpful
Not helpful

CONTACT US

Not sure where to start? Contact us, we are happy to help!


CONTACT US

SciChart Ltd, 16 Beaufort Court, Admirals Way, Docklands, London, E14 9XL. Email: Legal Company Number: 07430048, VAT Number: 101957725