QuantConnect: Adding Other Timeframes

In this tutorial, we shall cover how to add different timeframes to an algorithm. You may have noticed that QuantConnect provides only Tick, Second, Minute, Hourly and Daily data feeds. So how are we supposed to trade a Weekly, 15-minute, 30-minute or a 4-hour timeframe? Stick around to find out! Quant Connect Supported Resolutions Note: This post continues on from our Plotting The First Script post and forms part of the Getting Started Series. If you are completely new to QuantConnect, it is recommended to start at the first tutorial in the series.

Scope

We are going to create a custom “weekly’ timeframe and add it to our algorithm. The weekly timeframe will be used in the strategy for confirming entries with a second RSI Indicator. In addition to this, we shall also refresh our newly acquired plotting skills by adding the upper timeframe confirmation series to our RSI plot. However, the “how and why” of adding the plot will not be covered here as all the details were shared in the last tutorial. At first glance, the scope of this tutorial may sound relatively simple. However, there are quite a few steps we must take in order to create a custom timeframe and add an indicator to it. As such, the post will actually touch upon the following topics:
  • Consolidators: What they are and how to use them.
  • A look at the differences between tradebars and quotebars.
  • Registering an Indicator with our custom timeframe.
  • How to avoid a “Forward Only Indicator” runtime error.

Consolidators

Consolidators answer the question of “how can I use XYZ timeframe if QuantConnect doesn’t provide the data for it?“. Although “consolidator” is unlikely to be the first word that would pop into your head when looking for documentation on “how to access 30-minute data feeds”, the name is actually quite appropriate. A consolidator will consolidate smaller data into larger data. So, for example, we can take 1-minute data and consolidate 30 x 1-minute bars into a single 30-minute bar. Whilst using consolidators may take a bit longer to learn, using them to create custom timeframes has a couple of key advantages:
  • We can create any time-frame we can think of! We don’t need to stick to the usual suspects of 1-minute, 5-minute, 15-minute etc bars. We can create a 25 tick, a 17 second or a 12-minute data feed!
  • It means QuantConnect don’t need to store or process all this data on our behalf.
So in general, we trade a little complexity for added flexibility. For example, instead of simply requesting the resolution we want when adding the instrument (like we do with Tick, Second, Minute and Daily data), we must go through the following steps (which will be covered in our example later):
  1. Create a consolidator class.
  2. Create a handler. This is a method (function) that is called/executed every time a new bar of consolidated data is created.
  3. Add the consolidator via the SubscriptionManager.
Furthermore, in addition to these steps, we need to select the right type of consolidator for the job. There are actually four types of consolidator you can choose from:
  • TickConsolidator
  • TickQuoteBarConsolidator
  • TradeBarConsolidator
  • QuoteBarConsolidator
The four consolidators above fall into two broad categories, TradeBar and QuoteBar consolidators. So what is the difference between them and how do we know which to use?

Trade Bars Vs Quote Bars

Trade bars contain,Openhigh,low,close and volumedata. These are “mid” prices and a mid-price is the average price between bidand askprices. Mid prices are what you are given when you see that Stock XYZ is at $200. Conversely, Quote Bars actually contain the bidand askdata. With bidandaskinformation, we can actually see the spread (the difference between the bid and ask) and get more accurate backtesting results. For more information regarding bidandaskprices see the following link: https://www.investopedia.com/terms/b/bid-and-ask.asp

Selecting the right consolidator

So now we know the difference between the two groups, how do we know which one to use with our data? Luckily QuantConnect’s documentation comes to the rescue:
For each asset class and the data we provide please see the data library. It is a little confusing but here is the summary of QuantConnect provided data:
  • Equities – trade data only. For Resolution.Tick use the TickConsolidator, for all other resolutions use the TradeBarConsolidator.
  • Forex and CFD – quote data only. For Resolution.Tick use the TickQuoteBarConsolidator, for all other resolutions use the QuoteBarConsolidators.
  • Options- trade and quote data, at Resolution.Minute only. For consolidating options data use the TradeBarConsolidator or the QuoteBarConsolidator (depending on if you’re consolidating trades or quotes).
  • Futures – trade and quote data, at tick, second and minute resolutions.
Source: https://www.quantconnect.com/docs/algorithm-reference/consolidating-data#Consolidating-Data-TradeBars-vs-QuoteBars So if you are adding an asset that only has trade data available (such as equities), then we must use the TradeBarConsolidator. Similarly, for assets that only have quote data (like FX) we use the QuoteBarConsolidator.

The Code

Now we have the required background on consolidators, tradebars and quote bars, we can move onto the code.
### <summary>
### Simple RSI Strategy intended to provide a minimal algorithm example using
### one indicator with the most basic plotting
### </summary>
from datetime import timedelta

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(2015,1,1)    # Set Start Date
        self.SetEndDate(2018,1,1)      # Set End Date
        self.SetCash(10000)            # Set Strategy Cash
        
        RSI_Period    = 14                # RSI Look back period 
        self.RSI_OB   = 60                # RSI Overbought level
        self.RSI_OS   = 40                # RSI Oversold level
        self.Allocate = 0.25              # Percentage of captital to allocate
        
        # Find more symbols here: http://quantconnect.com/data
        self.AddEquity("AAPL", Resolution.Daily)
        
        # Create our consolidators
        W1_Con = TradeBarConsolidator(timedelta(days=5))
        
        # Register our Handlers
        W1_Con.DataConsolidated += self.On_W1
        
        # Create our Indicators
        self.RSI_Ind = self.RSI("AAPL", RSI_Period)
        self.W1_RSI = RelativeStrengthIndex("AAPL", RSI_Period)
        
        # Register the indicaors with our stock and consolidator
        self.RegisterIndicator("AAPL", self.W1_RSI, W1_Con)
        
        # Finally add our consolidators to the subscription
        # manager in order to receive updates from the engine
        self.SubscriptionManager.AddConsolidator("AAPL", W1_Con)
        
        # Ensure that the Indicator has enough data before trading,.
        # x5 to get enough weekly data
        self.SetWarmUp(RSI_Period*5)
        
        # Plot the RSI
        RSIChart = Chart("RSI", ChartType.Stacked)
        RSIChart.AddSeries(Series("D1", SeriesType.Line))
        RSIChart.AddSeries(Series("W1", SeriesType.Line))
        self.AddChart(RSIChart)
        
        # Create a custom volume chart
        VolChart = Chart("Volume", ChartType.Stacked)
        VolChart.AddSeries(Series('Buying Volume', SeriesType.Bar))
        VolChart.AddSeries(Series('Selling Volume', SeriesType.Bar))
        self.AddChart(VolChart)
        
    
    def On_W1(self,sender,bar):
        '''
        This method will be called every time a new 30 minute bar is ready. 
   
        bar = The incoming Tradebar. This is different to the data object in OnData()
        '''
        self.Plot('RSI', 'W1', self.W1_RSI.Current.Value)
    
        
    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.

        Arguments:
            data: Slice object keyed by symbol containing the stock data
        '''
        
        self.Plot('RSI', 'D1', self.RSI_Ind.Current.Value)
        
        if data["AAPL"].Close >= data["AAPL"].Open:
            self.Plot('Volume', 'Buying Volume', data["AAPL"].Volume)
        else:
            self.Plot('Volume', 'Selling Volume', data["AAPL"].Volume)
            
        # Make sure we are not warming up 
        if self.IsWarmingUp: return
        
        # Determine our entry and exit conditions
        # Do it here to avoid long lines later
        long_cond1 = self.RSI_Ind.Current.Value < self.RSI_OS
        long_cond2 = self.W1_RSI.Current.Value < self.RSI_OS
        exit_cond1 = self.RSI_Ind.Current.Value > self.RSI_OB
        exit_cond2 = self.W1_RSI.Current.Value > self.RSI_OB    
        
        # Check if we are in the market
        if not self.Portfolio.Invested:
        
            # If not, we check the RSI Indicator
            if all([long_cond1, long_cond2]):
                # Buy Apple
                self.SetHoldings("AAPL", self.Allocate)
        else:
            
            if all([exit_cond1, exit_cond2]):
                # Sell Apple 
                self.Liquidate("AAPL")
                

Code Commentary

Compared to our plotting tutorial, the initialize()method has grown significantly. It is here where we do most of the work to add our custom timeframe and a second indicator. However, before any of that, we need to import a python module at the start of the script: from datetime import timedelta. We then use the imported timedeltafunction to set how much data our consolidator will consolidate with the line. W1_Con = TradeBarConsolidator(timedelta(days=5)) Here, we are creating the W1_Con consolidator using a TradeBarConsolidator (AAPL is an equity and the documentation above says that QuantConnect only has trade data available for equities). Following this, we then pass the TradeBarConsolidatora time delta of 5 days (which is one week in the equity markets). As an example, if you wanted to create a 30-minute timeframe for FX you would need to do 2 things:
  1. Import Forex MINUTE data using AddForex()
  2. Use a QuotebarConsolidatorand pass timedelta(minutes=30) into it.

Creating and Registering Handlers

As mentioned in the consolidators section above, we need to create a handler that will be called whenever a new bar of data is ready. Therefore we have added:
 def On_W1(self,sender,bar):
        '''
        This method will be called every time a new 30 minute bar is ready. 
        
        bar = The incoming Tradebar. This is different to the data object in OnData()
        '''
        self.Plot('RSI', 'W1', self.W1_RSI.Current.Value)
You can give this handler any name you wish. In our example code, it is named On_W1(). You will see that all we actually do in the method is plot our RSI value. Our core entry/exit logic will still be performed on our lower timeframe using On_Data(). Once we have our handler we can register it in the initialize() method with the line: W1_Con.DataConsolidated += self.On_W1 Those familiar with python may be familiar with using +=to add a number to a current variable i.e x += 1. However, as a side note it, +=can do other things. See the great answer on stack overflow for some background reading and an example: https://stackoverflow.com/questions/4841436/what-exactly-does-do-in-python Finally to finish off adding the timeframe we add it to algorithm via the Subscription Manager with: self.SubscriptionManager.AddConsolidator("AAPL", W1_Con) Note: If you didn’t want to add any indicators to the timeframe, these are all the steps you need to add a timeframe to the algorithm. You could stop here if that was the case.

Creating and Registering Our RSI Indicator

Since our objective is to use a second upper timeframe for confirmation, we will need to register an RSI indicator to the timeframe. This is done in a slightly different way to how we added the RSI indicator in our first script and if you do this wrong, you can end up scratching your head for some time.
        # Create our Indicators
        self.RSI_Ind = self.RSI("AAPL", RSI_Period)
        self.W1_RSI = RelativeStrengthIndex("AAPL", RSI_Period)
        
        # Register the indicaors with our stock and consolidator
        self.RegisterIndicator("AAPL", self.W1_RSI, W1_Con)
The first self.RSI_Ind indicator is created in exactly the same way as our first script. However, notice the difference with between self.W1_RSI and self.RSI_Ind?  Why are they using different API calls? Well, if you try to create our weekly RSI indicator with self.RSI() you will run into the following error…

Runtime Error: This is a forward only indicator:

As a new Rookie to the platform, this had me stumped for a good hour or two. I was creating both indicators using self.RSI().  Obviously, I know now that this is NOT the correct way to do it for consolidated data feeds. To ensure that my pain is your pleasure, let’s take a look at why the error appears. When we useself.RSI(), we are actually using one of QuantConnect’s “helper” methods. It is a shortcut that does some stuff behind the scenes to save you lines of code.  In this case, it registers the indicator “under the hood”. As such, If we then register it ourselves to the weekly timeframe, the same indicator will have then been registered twice leading to the error. So to summarize:
  • RSI("AAPL", RSI_Period): Is a helper method.
  • RelativeStrengthIndex("AAPL", RSI_Period): Is what we should use. QuantConnect refer to this as the “constructor”.
See this helpful post for reference: https://www.quantconnect.com/forum/discussion/2217/consolidated-bar-indicators/p1 Therefore, the solution to avoid this error is to ensure that we only create Indicators using the constructor. The same will be true for EMAand other indicators so be on the lookout for them!

Running The Script

After running the script, you will see that we won’t be retiring anytime soon! However, that is not the point of the exercise! You should see a chart that looks like this: Results from adding a timeframe example code And if you take a look at the RSI chart we should see both timeframes are plotted: Dual Timeframe RSI Plotted  

Find This Post Useful?

If this post saved you time and effort, please consider donating a coffee to support the site!  

3PUY12Tgp8xynrMCbBdLE56DShzCbxFG8i

0xb90252f1a0af77a43c499102be8a08ce5e190e01

Le3ykk29k2TjD3ZFoEyWTFzJgUu9Q9v6Fq


Dontate with PayPal using any payment method you are comfortable with.