Navigation

    Backtrader Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

    Strategy execution on different timeframes and multi-data

    General Code/Help
    6
    8
    4118
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • RandyT
      RandyT last edited by

      I'm attempting to complete a system that generates indicators and signals based on a daily timeframe and execute trades against a data feed running on a minute timeframe. As I have written in other threads, I am looking at minute data for the ES and daily data for the SPY. I have set sessionend=dt.time(16, 00) for the daily timeframe resample of the SPY data so I would only expect to see the daily bar for SPY at 16:00 EST.

      Note: I am also making an assumption that the sessionend= variable is set to the time in the timezone of that instrument and not my local timezone.

      In Strategy next(), how do I ignore the ticks from the minute timeframe data (data0) and only evaluate conditions based on the daily closing bar (data1) that the indicators are processing? As it stands, it appears I am hitting next() for every tick of data0 (ES).

      1 Reply Last reply Reply Quote 0
      • RandyT
        RandyT last edited by RandyT

        I think part of what I am seeing here is a replay of all backfilled data from my IB feed on ES data0.

        Here is how I am sourcing data:

                # ES Futures Live data timeframe resampled to 1 Minute
                data0 = ibstore.getdata(dataname=args.live_es, fromdate=fetchfrom,
                                        timeframe=bt.TimeFrame.Minutes, compression=1)
                cerebro.resampledata(data0, name="ES", timeframe=bt.TimeFrame.Minutes, compression=1)
        
                # SPY Live data timeframe resampled to 1 Day
                data1 = ibstore.getdata(dataname=args.live_spy, backfill_from=bfdata0,
                                        timeframe=bt.TimeFrame.Days, compression=1,
                                        sessionend=dt.time(16, 00))
                cerebro.resampledata(data1, name="SPY", timeframe=bt.TimeFrame.Days, compression=1)
        
        

        After a full replay of backfilled minute data0 (ES), I now see a tick per minute playing through Strategy next()

        I'd like to ignore these ticks since the only purpose for the ES feed is to execute a trade on that instrument.

        1 Reply Last reply Reply Quote 0
        • B
          backtrader administrators last edited by

          next is called for any tick which any data produces. Concentrating on a specific data is a matter of looking at the len of the data.

          When the len of the data changes (it will always go up) you know you got a tick of that data

          def start(self):
              self.lendata1 = 0
          def next(self):
              if len(self.data1) > self.lendata1:
                  self.lendata1 += 1
                  do_something_here()
          
          S 1 Reply Last reply Reply Quote 2
          • RandyT
            RandyT last edited by

            @backtrader thank you sir

            1 Reply Last reply Reply Quote 0
            • S
              Systematica last edited by

              this is very helpful - I typically backtest over same time series with 1min resolution for executions. Thanks for posting.

              1 Reply Last reply Reply Quote -1
              • H
                hbf last edited by hbf

                @backtrader @RandyT @Systematica

                I'm trying to implement this with the following live code:

                from __future__ import (absolute_import, division, print_function,
                                        unicode_literals)
                
                
                import datetime
                
                import backtrader as bt
                
                class St(bt.Strategy):
                
                    def __init__(self):
                        self.lendata1 = 0
                
                    data_live = False
                
                    def notify_data(self, data, status, *args, **kwargs):
                        print('*' * 5, data._name ,'DATA NOTIF:', data._getstatusname(status),
                              *args)
                        if status == data.LIVE:
                            self.data_live = True
                
                    def next(self):
                        txt = list()
                        txt.append('Data0')
                        txt.append('%04d' % len(self.data0))
                        dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
                        txt.append('{}'.format(self.data.datetime[0]))
                        txt.append('%s' % self.data.datetime.datetime(0).strftime(dtfmt))
                        txt.append('{}'.format(self.data.open[0]))
                        txt.append('{}'.format(self.data.high[0]))
                        txt.append('{}'.format(self.data.low[0]))
                        txt.append('{}'.format(self.data.close[0]))
                        txt.append('{}'.format(self.data.volume[0]))
                        txt.append('{}'.format(self.data.openinterest[0]))
                        print(', '.join(txt))
                
                        if len(self.datas) > 1 and len(self.data1):
                            txt = list()
                            txt.append('Data1')
                            txt.append('%04d' % len(self.data1))
                            dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
                            txt.append('{}'.format(self.data1.datetime[0]))
                            txt.append('%s' % self.data1.datetime.datetime(0).strftime(dtfmt))
                            txt.append('{}'.format(self.data1.open[0]))
                            txt.append('{}'.format(self.data1.high[0]))
                            txt.append('{}'.format(self.data1.low[0]))
                            txt.append('{}'.format(self.data1.close[0]))
                            txt.append('{}'.format(self.data1.volume[0]))
                            txt.append('{}'.format(self.data1.openinterest[0]))
                            print(', '.join(txt))
                
                        if len(self.data1) > self.lendata1:
                            print(len(self.data1))
                            self.lendata1 += 1
                            print(self.lendata1)
                
                def runstrat(args=None):
                
                    cerebro = bt.Cerebro()
                
                    # Store
                    store = bt.stores.IBStore(port=7497, host='127.0.0.1')
                
                    # Data feed
                    data = store.getdata(dataname='AUD.CAD-CASH-IDEALPRO')
                    data1 = store.getdata(dataname='AUD.CAD-CASH-IDEALPRO')
                    tf0n = str('1m')
                    tf1n = str('5m')
                    cerebro.resampledata(data, timeframe=bt.TimeFrame.Minutes,
                                         compression=1, name=tf0n)
                    cerebro.resampledata(data1, timeframe=bt.TimeFrame.Minutes,
                                         compression=5, name=tf1n)
                    # Broker
                    cerebro.broker = store.getbroker()
                
                    # Strategy
                    cerebro.addstrategy(St)
                
                    # Execute
                    cerebro.run()
                
                if __name__ == '__main__':
                    runstrat()
                

                The problem I'm having is that even though I have initiated lendata1 on init, it doesn't seem to be updating on next. The line printed 1 should be 1180, 2 should be 1181 and 282 should be 1197.

                Server Version: 76
                TWS Time at connection:20181210 13:50:51 AEST
                ***** 1m DATA NOTIF: DELAYED
                ***** 5m DATA NOTIF: DELAYED
                1340,2018-12-10T01:34:00,1m,0.95982,0.95984,0.95971,0.95975,-1.00000
                1180
                1
                1341,2018-12-10T01:35:00,1m,0.95975,0.95983,0.95963,0.95964,-1.00000
                1181
                2
                ...
                ***** 1m DATA NOTIF: LIVE
                ***** 5m DATA NOTIF: LIVE
                Data0, 0282, 737038.122222, 2018-12-09T21:56:00.000000, 0.9598, 0.95981, 0.95979, 0.95979, 0.0, 0.0
                Data1, 1197, 737038.121528, 2018-12-09T21:55:00.000000, 0.95981, 0.95982, 0.95978, 0.95981, -1.0, 0.0
                1197
                282
                

                I assume that it is because of the backfill. I was wondering if you had any suggestions? Thank you.

                Weirdly, it has been working when testing on different timeframes. The following code modification stores the correct values for self.lendata1

                    # Data feed
                    data = store.getdata(dataname='AUD.CAD-CASH-IDEALPRO')
                    data1 = store.getdata(dataname='AUD.CAD-CASH-IDEALPRO')
                    tf0n = str('5m')
                    tf1n = str('1h')
                    cerebro.resampledata(data, timeframe=bt.TimeFrame.Minutes,
                                         compression=5, name=tf0n)
                    cerebro.resampledata(data1, timeframe=bt.TimeFrame.Minutes,
                                         compression=60, name=tf1n)
                

                UPDATE: I have used the following:

                def nextstart(self):
                     self.lendata1 = len(self.data1)
                

                Updated code:

                from __future__ import (absolute_import, division, print_function,
                                        unicode_literals)
                
                
                import datetime
                
                import backtrader as bt
                
                class St(bt.Strategy):
                
                    def __init__(self):
                        self.lendata1 = 0
                
                    data_live = False
                
                    def notify_data(self, data, status, *args, **kwargs):
                        print('*' * 5, data._name ,'DATA NOTIF:', data._getstatusname(status),
                              *args)
                        if status == data.LIVE:
                            self.data_live = True
                
                    def nextstart(self):
                        self.lendata1 = len(self.data1)
                
                    def next(self):
                        txt = list()
                        txt.append('Data0')
                        txt.append('{}'.format(len(self.data0)))
                        txt.append('{}'.format(self.data.datetime.datetime(0).isoformat()))
                        txt.append('{}'.format(self.data.open[0]))
                        txt.append('{}'.format(self.data.high[0]))
                        txt.append('{}'.format(self.data.low[0]))
                        txt.append('{}'.format(self.data.close[0]))
                        txt.append('{}'.format(self.data.volume[0]))
                        txt.append('{}'.format(self.data.openinterest[0]))
                        print(', '.join(txt))
                
                        if len(self.datas) > 1 and len(self.data1):
                            txt = list()
                            txt.append('Data1')
                            txt.append('{}'.format(len(self.data1)))
                            txt.append('{}'.format(self.data1.datetime.datetime(0).isoformat()))
                            txt.append('{}'.format(self.data1.open[0]))
                            txt.append('{}'.format(self.data1.high[0]))
                            txt.append('{}'.format(self.data1.low[0]))
                            txt.append('{}'.format(self.data1.close[0]))
                            txt.append('{}'.format(self.data1.volume[0]))
                            txt.append('{}'.format(self.data1.openinterest[0]))
                            print(', '.join(txt))
                
                        if len(self.data1) > self.lendata1:
                            print(len(self.data1))
                            self.lendata1 += 1
                            print(self.lendata1)
                            print('new higher tf bar')
                
                def runstrat(args=None):
                
                    cerebro = bt.Cerebro()
                
                    # Store
                    store = bt.stores.IBStore(port=7497, host='127.0.0.1')
                
                    # Data feed
                    data = store.getdata(dataname='AUD.CAD-CASH-IDEALPRO',
                                         timeframe=bt.TimeFrame.Ticks)
                    
                    tf0n = str('30s')
                    tf1n = str('2m')
                
                    cerebro.resampledata(data, timeframe=bt.TimeFrame.Seconds,
                                         compression=30, name=tf0n)
                
                    cerebro.resampledata(data, timeframe=bt.TimeFrame.Minutes,
                                         compression=2, name=tf1n)
                
                    # Broker
                    cerebro.broker = store.getbroker()
                
                    # Strategy
                    cerebro.addstrategy(St)
                
                    # Execute
                    cerebro.run()
                
                if __name__ == '__main__':
                    runstrat()
                
                
                1 Reply Last reply Reply Quote 0
                • S
                  stochastick @backtrader last edited by

                  I am revisiting this old thread as it seems to be a good jumping off point for a problem I'm stuck on. How might we extend this code to incorporate multiple unique symbols?

                  @backtrader said in Strategy execution on different timeframes and multi-data:

                  next is called for any tick which any data produces. Concentrating on a specific data is a matter of looking at the len of the data.

                  When the len of the data changes (it will always go up) you know you got a tick of that data

                  def start(self):
                      self.lendata1 = 0
                  def next(self):
                      if len(self.data1) > self.lendata1:
                          self.lendata1 += 1
                          do_something_here()
                  

                  I am trying to run a test across multiple symbols by evaluating trade signals on one time frame and completing executions on another.

                  When using the aforementioned code within an enumeration, only trades for the first added data feed are acted upon.

                  This is a snippet of the code I am working with, which parallel's that @backtrader outlines above:

                  '''python

                  def start(self):
                      self.datas[1].len = 0
                  
                  def next(self):
                      for i, d in enumerate(self.datas):
                          if len(self.datas[1]) > self.datas[1].len:
                              self.datas[1].len += 1
                              TRADE_LOGIC_HERE
                  

                  '''

                  It seems like something else needs to be done other than just wrapping this in enumerate(). I have tried many different versions and have been stuck on this problem for the last several weeks, so any help is appreciated. If needed, I can create a unique thread for this problem.

                  B 1 Reply Last reply Reply Quote 0
                  • B
                    bigdavediode @stochastick last edited by

                    @stochastick

                    Just enumerate through the datas in a pythonic way by referencing d. if len(d) > self.lenofdata....

                    If you reference self.datas[1] you'll only ever access self.datas[1]. Each timeframe and datafeed will have its own data in datas. datas[0] datas[1] datas[2]... datas[n]. Also self.len has the potential for a keyword conflict. I suggest following backtraders example above.

                    Have you reviewed the multiple datas example? You might want to search that out.

                    1 Reply Last reply Reply Quote 0
                    • 1 / 1
                    • First post
                      Last post
                    Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors