QuantConnect: Working with Heikin Ashi Data

This week we return to QuantConnect for an article on working with Heiken Ashi data. We will cover how to use Heikin Ashi data in your strategy, take a look at some plotting options and also provide a simple strategy that can be used as a base for your own projects. This is not the first time we have touched this topic. Previously, we have covered working with Heikin Ashi charts on Backtrader. In that article, we also covered a little background/introduction to the Heikin Ashi candlestick and how to interpret it. Those introductory parts of the article are not really platform specific. As such, any readers who are new to Heiken Ashi candles would benefit from reading the first few paragraphs.

Heiken Ashi on QuantConnect

QuantConnect’s approach to handling Heikin Ashi data differs from other platforms such as Tradingview and Backtrader. On QuantConnect we access Heikinashi data via an Indicator. This design decision makes a whole lot of sense. After all, Heiken Ashi candles are a derivative of real prices just like any other indicator. By contrast, on other platforms, Heiken Ashi data is delivered as a data feed. In other words, the OHLCdata is converted to the Heikin Ashi version of OHLC. When OHLCdata is converted, often beginners will end up with incorrect results and unrealistic expectations. This is because when Heikin Ashi prices are delivered as a data feed, the backtesting engine only has these prices for reference. In turn, this means that order fill levels and profit/loss calculations are based on prices which never existed. Note that this does not mean the other platforms are wrong or have issues. It just means that users must be aware of it and adjust their code appropriately. On QuantConnect the beginner does not even need to worry about this. The data will come through as a set of indicator values. All order fills and PnL calculations etc will always be calculated from the data feed using real prices!

Adding Heikinashi Indicators

As we discussed, in order to use Heiken Ashi data on QuantConnect, we need to create an indicator. We do this in the same way as we created the RSI indicator in our first script tutorial. Fire up the API tree and you will find the HeikinAshi() Indicator is available right at the root of the tree.

Plotting Heikinashi Candles

Users trying to plot Heikin Ashi candlestick charts might find themselves arriving at this article courtesy of Google. If that is you, we can at least save you some time from looking for a solution that does not exist. At the time of writing there is no support for plotting Heikin Ashi candles that look like this: Heikin Although QuantConnect does support creating custom charts, it is not a charting platform by trade. As such, users cannot expect to have Tradingview levels of charting ability. Having said all that, there are some things we can do to plot our Heikinashi data and four suggestions will follow. These are not the only options in the world but should provide a range of styles to consider.

Try Logging!

Whilst this is not one of the plotting suggestions, wherever possible, try to use the logging function (self.Log()). Unless you looking for visual patterns, you can verify everything you need to through logging. As such, this is a good time to start getting comfortable in the logs. Further to this, since we already know from our plotting tutorial that QuantConnect enforces limits on plotting, it is not practical to plot every data point in our algorithms. Our charts should be selected wisely and plotted only when needed.Plotting Limit - Terminal output If you are a visual person and like to see charts in order to generate development ideas, then there is no harm in using a charting platform like Tradingview in conjunction with QuantConnect. Use the charting platform as your notepad and then come back to QuantConnect when you are ready to put an idea into practice! So without further ado, let’s move onto our suggestions. Note: All the suggestions shown below will be contained in the final strategy code.

Suggestion 1 – Line Charts

First, we can plot each data point from the indicator on a simple line chart. This will result in a chart which looks like this: Personally, I find that a little hard to read and would rather look at a log file.

Suggestion 2 – Candle Charts

The next, option is to create a custom chart and set up a series on the chart that uses SeriesType.Candle. It should be noted though that this does not produce a OHLCcandlestick that you might be accustomed to on charting platforms. Instead, it produces something which more closely represents the equity chart. As you can see, each candle represents the change in price between 2 days. We can see this on the x-axis. It moves in 2-day steps.

Suggestion 3 – Scatter Charts

The third suggestion has been recommended in the community forums on several occasions and so it is worth mentioning here! With this method, we use scatter markers to plot the OHLCdata.

Suggestion 4 – Mixed

In the final chart, we mix line and scatter plots to get a nice mix of information. Circles on the Price line denote whether we closed up or down. I.e whether the Heiken Ashi candle is green or red. In addition to this, we add two scatter plots to show the relative highs and lows. This results in a chart that looks like this:

The Code

### <summary>
### Simple RSI Strategy intended to provide a minimal algorithm example using
### one indicator
### </summary>
from System.Drawing import Color

class RSIAlgorithm(QCAlgorithm):

    def Initialize(self):
        '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
        # Set our main strategy parameters
        self.SetStartDate(2014,12, 1)   # Set Start Date
        self.SetEndDate(2015,1,1)       # Set End Date
        self.SetCash(10000)             # Set Strategy Cash
        self.Equities = ["AAPL"]
        self.HK = dict() # Create dict to hold all our HK Indicators
        for Equity in self.Equities:
            # Find more symbols here: http://quantconnect.com/data
            self.AddEquity(Equity, Resolution.Daily)
            self.HK[Equity] = self.HeikinAshi(Equity, Resolution.Daily)
            # Plotting Suggestion 1: Line chart
            # --------------------------------------
            # Note: This has been commented out as
            #       we are limited to 10 series per
            #       backtest. To test, uncomment the
            #       lines below and then comment out
            #       either suggestion 3 or 4.
            # ---------------------------------------
            #    Equity + " - Line",
            #    self.HK[Equity].Open,
            #    self.HK[Equity].High,
            #    self.HK[Equity].Low,
            #    self.HK[Equity].Close
            #    )
            # Plotting Suggestion 2: Candle Chart
            # ---------------------------------------
            CandChart = Chart(Equity + "- Candle", ChartType.Stacked)
            CandChart.AddSeries(Series('Heikinashi', SeriesType.Candle))
            # Plotting Suggestion 3: Scatter Chart
            # ---------------------------------------
            ScatPlot = Chart(Equity + "- X", ChartType.Stacked)
            ScatPlot.AddSeries(Series('Open',  SeriesType.Scatter, '$', Color.Black, ScatterMarkerSymbol.Circle))
            ScatPlot.AddSeries(Series('High',  SeriesType.Scatter, '$', Color.Green, ScatterMarkerSymbol.Triangle))
            ScatPlot.AddSeries(Series('Low',   SeriesType.Scatter, '$', Color.Red,   ScatterMarkerSymbol.TriangleDown))
            ScatPlot.AddSeries(Series('Close', SeriesType.Scatter, '$', Color.Black, ScatterMarkerSymbol.Square))
            # Plotting Suggestion 4: Mixed
            # ---------------------------------------
            SAPlot = Chart(Equity + "- Mix", ChartType.Stacked)
            SAPlot.AddSeries(Series('Price', SeriesType.Line, "$", Color.Black))
            SAPlot.AddSeries(Series('Bullish', SeriesType.Scatter, "$", Color.Green, ScatterMarkerSymbol.Circle))
            SAPlot.AddSeries(Series('Bearish', SeriesType.Scatter, "$", Color.Red, ScatterMarkerSymbol.Circle))
            SAPlot.AddSeries(Series('Neutral', SeriesType.Scatter, "$", Color.Black, ScatterMarkerSymbol.Circle))
            SAPlot.AddSeries(Series('High',  SeriesType.Scatter, '$', Color.Black, ScatterMarkerSymbol.Triangle))
            SAPlot.AddSeries(Series('Low',   SeriesType.Scatter, '$', Color.Black,   ScatterMarkerSymbol.TriangleDown))
    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.

            data: Slice object keyed by symbol containing the stock data
        for Equity in self.Equities:
            # Aliases
            # -----------------------------------------------------------------
            # Heikin
            HK_O = self.HK[Equity].Open.Current.Value
            HK_H = self.HK[Equity].High.Current.Value
            HK_L = self.HK[Equity].Low.Current.Value
            HK_C = self.HK[Equity].Close.Current.Value
            HK_P = self.HK[Equity].Current.Price
            # OHLC
            O = data[Equity].Open
            H = data[Equity].High
            L = data[Equity].Low
            C = data[Equity].Close
            P = data[Equity].Price
            # -----------------------------------------------------------------
            # Work out Heikin Sentiment
            # ---------------------------------------
            if HK_O < HK_C:
                HK_S = "Bull" 
            elif HK_O > HK_C:
                HK_S = "Bear"
                HK_S = "Neut"
            # Plotting Option 2: Candle Chart
            # ---------------------------------------
            self.Plot(Equity + "- Candle", 'Heikinashi',  HK_P)
            # Plotting Option 3: Scatter Chart
            # ---------------------------------------
            self.Plot(Equity + "- X", 'Open', HK_O)
            self.Plot(Equity + "- X", 'High', HK_H)
            self.Plot(Equity + "- X", 'Low',  HK_L)
            self.Plot(Equity + "- X", 'Close', HK_C)
            # Plotting Option 4: Mixed
            # ---------------------------------------
            self.Plot(Equity + "- Mix", 'Price', HK_P)
            self.Plot(Equity + "- Mix", 'High', HK_H)
            self.Plot(Equity + "- Mix", 'Low',  HK_L)
            if HK_S == "Bull":
                self.Plot(Equity + "- Mix", 'Bullish', HK_P)
            elif HK_S == "Bear":
                self.Plot(Equity + "- Mix", 'Bearish', HK_P)
                self.Plot(Equity + "- Mix", 'Neutral', HK_P)
            # Logging
            # -----------------------------------------------------------------
            self.Log("{0} OHLC   >> O: {1} H: {2} L:{3}, C:{4} | Price: {5}".format(Equity, O,H,L,C,P))
            self.Log("{0} Heikin >> O: {1} H: {2} L:{3}, C:{4} | Price: {5} | Sentiment: {6}".format(Equity, HK_O,HK_H,HK_L,HK_C,HK_P,HK_S))
            # Entry / Exit Criteria
            # -----------------------------------------------------------------
            # Check if we are in the market
            if not self.Portfolio.Invested:
                # If not, we check HK sentiment is bullish
                if HK_S == "Bull":
                    self.SetHoldings(Equity, 0.5)
               if HK_S == "Bear":

Code Commentary

The strategy is very, very basic. It simply buys when the Heikin candles are bullish (green) and sells when they turn bearish (red). As mentioned in the introduction, the code is intended to be used as a simple base to build upon. One general note that is applicable to the complete example code is that the Initialize()and OnData()methods (functions) have been coded in a way to support multiple assets. As such, if you add a second symbol into the line: self.Equities = ["AAPL"] Then the strategy will cycle through both symbols and create plots for each. It should be noted that due to the number of plots used in the examples, plotting limits will likely be reached. We are limited to 10 series plots per backtest. It is also for this reason that suggestion 1 is fully commented out.


To practice what one preaches, some basic logging has been added to the example. Here we simply log the real OHLC, Heikin OHLC and the candle sentiment. This is enough to compare the differences in prices and validate that our strategy is opening and closing positions in the correct places.

Plotting – Identity Objects!

One potential Gotcha when working with Heiken Ashi Indicators is that it is also possible to access the latest indicator values like so:
HK_O = self.HK[Equity].Open
HK_H = self.HK[Equity].High
HK_L = self.HK[Equity].Low
HK_C = self.HK[Equity].Close
HK_P = self.HK[Equity].Current
However, these appear to be “Identity” type objects and cannot be plotted. If you try to plot them you will receive the following error:
Runtime Error: Trying to dynamically access a method that does not exist throws a TypeError exception. To prevent the exception, ensure each parameter type matches those required by the Plot method. Please checkout the API documentation. at OnData in main.py:line 110 TypeError : No method matches given arguments for Plot (Open Stacktrace)
It can be confusing at first because if you try to print the values using self.Log(), the values will be converted to a string and you will see them in the log. As such, you can be left wondering what the issue is. Save yourself some time and access the latest values through Current.Valueas shown in the example code.
HK_O = self.HK[Equity].Open.Current.Value
HK_H = self.HK[Equity].High.Current.Value
HK_L = self.HK[Equity].Low.Current.Value
HK_C = self.HK[Equity].Close.Current.Value
HK_P = self.HK[Equity].Current.Price

Verifying Our Output

After creating any algorithm we should perform some checks to make sure that it is performing as expected. As simple as the strategy might be, we should still perform testing. Silly typos can easily creep in and completely invalidate the test. Besides, it is a good habit to get into for when your algorithms become more complex. The two key items we shall verify are:
  1. Whether we are buying/selling at the correct time.
  2. Double checking the earlier claim here that the LEAN engine always uses real prices for order fills.


To check whether we are buying and selling in the correct places, we can study the logs and compare to the trade list: In the image above, we verify two things. First, that the sentiment is correct and secondly that only when sentiment changes we send buy/sell orders.

Order Fills

Finally, we will verify our orders are being filled with real prices. Again we can do this by comparing our Log to the trades list. Here we can see that the trade is filled at the open of the following bar as expected. What’s more is that it is filled against real OHLC prices rather than the Heikin Ashi prices.

Find This Post Useful?

If this post saved you time and effort, please consider support the site! There are many ways to support us and some won’t even cost you a penny.