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/

    Possible bug when mixing timeframes

    Indicators/Strategies/Analyzers
    3
    10
    461
    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.
    • Mariano Bengeldorf
      Mariano Bengeldorf last edited by

      Hi there... it's me again...
      I am mixing datafeeds of different timeframes, following this documentation: https://www.backtrader.com/docu/data-multitimeframe/data-multitimeframe/

      It works well, but I found some inconsistencies.
      Basically what I am doing is entering variables by args using the parser.

      parser.add_argument('-t', '--timeframe',
                              type=str,
                              nargs='+',
                              default='1w',
                              choices=['1m', '5m', '15m', '30m', '1h', '2h', '3h', '4h', '6h', '12h', '1d', '1M', '1w', '1y'],)
      

      Then I am using a dict to interprete the choices:

      tframes = {
          '1m': [bt.TimeFrame.Minutes, 1],
          '5m': [bt.TimeFrame.Minutes, 5],
          '15m': [bt.TimeFrame.Minutes, 15],
          '30m': [bt.TimeFrame.Minutes, 30],
          '1h': [bt.TimeFrame.Minutes, 60],
          '2h': [bt.TimeFrame.Minutes, 120],
          '3h': [bt.TimeFrame.Minutes, 180],
          '4h': [bt.TimeFrame.Minutes, 240],
          '6h': [bt.TimeFrame.Minutes, 360],
          '12h': [bt.TimeFrame.Minutes, 720], 
          '1d': [bt.TimeFrame.Days, 1],
          '1w': [bt.TimeFrame.Weeks, 1],
          '1M': [bt.TimeFrame.Months, 1],
          '1y': [bt.TimeFrame.Years, 1]
      
      }
      

      then I add the data from a CVS file (in minutes) to cerebro like this:

      timeframes = args.timeframe
      timeframeslen = len(timeframes)
      cerebro.resampledata(data, timeframe=tframes[timeframes[i]][0], compression=tframes[timeframes[i]][1])
      

      Finally I call a custom indicator like this (in this example I am mixing three different timeframes):

      self.mresistance0 = SandR(self.data0)
      self.mresistance0 = SandR(self.data1, plot = False)
      self.mresistance0 = SandR(self.data2, plot = False)
      

      Then in the terminal I can call the scrip and add arguments like "1d 1w 1M" and it will plot something like this(which is ok):

      1D-1W-1M.png

      Or I can use "4h 1w 1M" and I get the following (which is also ok):

      4H-1W-1M.png

      It is also ok what I get when I use "1h 1w 1M":

      1H-1W-1M.png

      The issue shows up when I mix hourly datafeeds with daily datafeeds. What happens is that the second datafeed just disappears.
      For example, when I use 1h 1d 1W, I get this:

      1H-1D-1W x.png

      In the chart above you can check that the 1d data is just not there, neither in the calculations

      Another example, when I use 4H 1D 1M, the same issue:

      4H-1D-1M x.png

      Please, help! I am suffering so much!!

      Thanks in advance

      1 Reply Last reply Reply Quote 0
      • Mariano Bengeldorf
        Mariano Bengeldorf last edited by

        One correction:
        In the third code that I posted above I made a mistake, this is what I should have posted:

        args = parse_args()
            timeframes = args.timeframe
            timeframeslen = len(timeframes)
         data = readCVS()
                for i in range(timeframeslen):
                    cerebro.resampledata(data, timeframe=tframes[timeframes[i]][0], compression=tframes[timeframes[i]][1])
        
        

        Sorry for that...

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

          And the data?

          In any case, I fail to understand what is your problem and your suffering. There seems to be a matplotlib thing with a specific combination of things, probably due to incompatible lengths along the axis.

          The data is for sure being fed to next and as stated several times, "plotting is only a visual aid and not the target of the framework"

          1 Reply Last reply Reply Quote 0
          • Mariano Bengeldorf
            Mariano Bengeldorf last edited by

            Hi, thanks for answering.
            I am suffering because I've been trying to figure out why is this happing for hours with no luck at all...

            Answering your question about the data: The same problem happens with the data, it is not just a plotting issue.

            I entered print(self.data.close[0], self.data1.close[0], self.data2.close[0]) at the end of the Next method within the strategy to se what comes out.And I can confirm that issue is the same at a data level.
            When I use something like "4h 1w 1M" as an argument I get the right output. But, when I enter an argument mixing hours with days (like "4H 1D 1M"), I just don't get anything printed at all. It looks like is just skipping the Next method of the strategy.
            Any ideas?

            Many thanks for your kind help...

            B 1 Reply Last reply Reply Quote 0
            • A
              ab_trader last edited by

              Don't have much experience with the intraday data. But for me it seems something is wrong with sessionstart and sessionend.

              • If my answer helped, hit reputation up arrow at lower right corner of the post.
              • Python Debugging With Pdb
              • New to python and bt - check this out
              1 Reply Last reply Reply Quote 0
              • B
                backtrader administrators @Mariano Bengeldorf last edited by

                @Mariano-Bengeldorf said in Possible bug when mixing timeframes:

                Answering your question about the data: The same problem happens with the data, it is not just a plotting issue.

                Your next statements contradict this.

                @Mariano-Bengeldorf said in Possible bug when mixing timeframes:

                When I use something like "4h 1w 1M" as an argument I get the right output. But, when I enter an argument mixing hours with days (like "4H 1D 1M"), I just don't get anything printed at all. It looks like is just skipping the Next method of the strategy.

                But the 4H data gets plotted, so the data is there ... contradicting your previous statement.

                Any ideas?

                I know it seems obvious to you, but you only show how you resample data (a redacted version, hence the errors) and not the rest of things you do, like adding indicators.

                Mariano Bengeldorf 1 Reply Last reply Reply Quote 0
                • Mariano Bengeldorf
                  Mariano Bengeldorf @backtrader last edited by

                  @backtrader I see your point now.
                  I am sorry for not being as clear as I am supposed to be.
                  What I am trying to explain is that when I use as an argument "4h 1d 1M"
                  the 4H and 1M do get plotted but the 1D does not (check the previous charts). That is the issue.
                  At the same time, when I use "4h 1d 1M" and create a print() in the next method within the strategy (the way I explained in my previous post) the data values do not get printed in the terminal because it looks like it is skipping the next method. The data is there (at least for the 4h and 1M, not for 1D) but the next method is skipped.
                  None of all this happens when I use as an argument like 4h 1w 1M, which is not mixing hours with days.
                  I hope I was clear enough this time. Sorry for the misunderstanding.
                  Thanks

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

                    It's all clear ... you still fail to understand that posting some charts with some "redacted" code is good for nothing.

                    As you may see in the chart below ... it works, hence: "bug in your code".

                    ac61e0ee-3bcf-446f-abad-8d1343809048-image.png

                    Mariano Bengeldorf 1 Reply Last reply Reply Quote 0
                    • Mariano Bengeldorf
                      Mariano Bengeldorf @backtrader last edited by

                      Hi @backtrader
                      Thanks for your answer. I was just trying to make things simpler for you, I am sorry if it turned out to be the other way. Not my intention.
                      BTW, I love what your are doing and thanks for sharing with all of us.
                      I took a bit to answer because I was trying to figure it out by my self, but no luck at all.
                      Here goes the code, thanks for any advise you can provide:

                      from __future__ import (absolute_import, division, print_function, unicode_literals)
                      import backtrader as bt
                      from collections import OrderedDict
                      import pandas as pd
                      import argparse
                      
                      
                      def parse_args():
                          parser = argparse.ArgumentParser(
                              description='RSI strategy')
                      
                          parser.add_argument('-nh', '--noheaders',
                                              action='store_true',
                                              default=False,
                                              required=False,
                                              help='Do not use header rows')
                      
                          parser.add_argument('-t', '--timeframe',
                                              type=str,
                                              nargs='+',
                                              default='1w',
                                              choices=['1m', '5m', '15m', '30m', '1h', '2h', '3h', '4h', '6h', '12h', '1d', '1M', '1w', '1y'],
                                              help='The timeframe for second indicator')
                      
                          parser.add_argument('-fp', '--filepath',
                                              type=str,
                                              default='binance-BTCUSDT-1m-2019-01-01.csv',
                                              required=False,
                                              help='File path (../../filename.csv')
                      
                          parser.add_argument('-hi', '--history',
                                              type=int,
                                              default=30,
                                              required=False,
                                              help='Number of days to retrieve')
                      
                          parser.add_argument('-c', '--cash',
                                              type=int,
                                              default=10000,
                                              required=False,
                                              help='Starting Cash')
                      
                          parser.add_argument('-st', '--stats',
                                              action='store_true',
                                              default=False,
                                              required=False,
                                              help='Print the stats')
                      
                          return parser.parse_args()
                      
                      
                      def readCVS():
                          # Get a pandas dataframe
                          datapath = args.filepath
                      
                          # Simulate the header row isn't there if noheaders requested
                          skiprows = 1 if args.noheaders else 0
                          header = None if args.noheaders else 0
                      
                          dataframe = pd.read_csv(datapath,
                                                  skiprows=skiprows,
                                                  header=header,
                                                  parse_dates=True,
                                                  index_col=0)
                      
                          dataframe.index = pd.to_datetime(dataframe.index, unit='ms')
                      
                          # Pass it to the backtrader datafeed and add it to the cerebro
                          return bt.feeds.PandasData(dataname=dataframe, timeframe=bt.TimeFrame.Minutes, openinterest=None)
                      
                      
                      def resismet(maxima,resistances, resNum=15, tolPercRes=2):
                          # creates a list of resistances with length resNum and elminates already broken resistances
                          # within a tolerance of tolerancePerc
                          toleranceResist = maxima * tolPercRes / 100 #todo mejorar este calculo
                          if maxima > 0:
                              if all(abs(maxima - i) > toleranceResist for i in resistances):
                                  resistances.append(maxima)
                              else:
                                  removeResist = [i for i in resistances if maxima - i > 0 and maxima - i <= toleranceResist]
                                  if len(removeResist) != 0:
                                      for i in removeResist:
                                          resistances.remove(i)
                                      resistances.append(maxima)
                      
                          lenR = len(resistances)
                      
                          if resNum != None and lenR > resNum:
                              resistances[0:lenR - resNum] = []
                          return (resistances)
                      
                      
                      class SandR(bt.Indicator):
                      
                          params = OrderedDict([
                              ('nresist', 20),
                              ('tolerance', 2),
                              ('tolerance2', 2),
                              ('period', 1),
                              ('barplot', False),
                              ('timeframeslen', 4)
                          ])
                      
                          nresist = params['nresist']
                      
                          mlinelist = list()
                      
                          for i in range( nresist * 2):
                              mlinelist.append('mresistance' + str(i))
                      
                          lines = tuple(mlinelist)  # creates the lines based on the nresist parameter
                      
                          plotinfo = dict(subplot=False,
                                           )
                          resdic = OrderedDict()
                      
                          for i in range(params['timeframeslen']-1,-1,-1):
                              j = 'resistances' + str(i)
                              resdic[j] = list()
                      
                          def __init__(self):
                              #look for local maxima
                              higherthanlast = bt.And(self.data.high(-1) > self.data.high(-2), self.data.high(-1) > self.data.high(0))
                              self.maxima = higherthanlast * self.data.high(-1)
                      
                      
                          def next(self, resdic = resdic):
                              dataid = self.data._id-1
                              resistancesid = 'resistances'+str(dataid)
                              resdic[resistancesid] = resismet(self.maxima[0], resdic[resistancesid], self.params.nresist,
                                                       self.params.tolerance)
                              tolRestoSupPer = self.params.tolerance2/100
                      
                              if dataid == 0:
                                  inputhigh = self.data.high[-1]
                                  for j in resdic.values():
                                      ResToSupport = [i for i in j if inputhigh - i > i*tolRestoSupPer]
                                      if len(ResToSupport) != 0:
                                          for k in ResToSupport:
                                              j.remove(k)
                      
                              mresistances = [i for j in resdic.values() for i in j]
                      
                              for i in (range(len(mresistances)-1,-1,-1)):
                                  if mresistances.count(mresistances[i]) > 1:
                                      del mresistances[i]
                      
                              if dataid == 0:
                                  if len(mresistances) > 0:
                                      n = 0
                                      for i in self.mlinelist:
                                          if len(mresistances) > n:
                                              exec('self.lines.' + i + '[0]' '= mresistances[n]')
                                              n = n + 1
                      
                      
                      class secondStrategy(bt.Strategy):
                      
                          def __init__(self):
                      
                              self.mresistance0 = SandR(self.data0, plotname='Resistance', plotvaluetags=False, timeframeslen = 2)
                      
                          def next(self):
                              print('=========')
                      
                      
                      tframes = {
                          '1m': [bt.TimeFrame.Minutes, 1],
                          '5m': [bt.TimeFrame.Minutes, 5],
                          '15m': [bt.TimeFrame.Minutes, 15],
                          '30m': [bt.TimeFrame.Minutes, 30],
                          '1h': [bt.TimeFrame.Minutes, 60],
                          '2h': [bt.TimeFrame.Minutes, 120],
                          '3h': [bt.TimeFrame.Minutes, 180],
                          '4h': [bt.TimeFrame.Minutes, 240],
                          '6h': [bt.TimeFrame.Minutes, 360],
                          '12h': [bt.TimeFrame.Minutes, 720],
                          '1d': [bt.TimeFrame.Days, 1],
                          '1w': [bt.TimeFrame.Weeks, 1],
                          '1M': [bt.TimeFrame.Months, 1],
                          '1y': [bt.TimeFrame.Years, 1]
                      
                      }
                      
                      # Variable for our starting cash
                      startcash = 100000
                      
                      if __name__ == '__main__':
                          cerebro = bt.Cerebro()
                          args = parse_args()
                          timeframes = args.timeframe
                      
                          if not args.filepath:
                              data = loadExchange(timeframes[0])
                              cerebro.adddata(data)
                              for i in range(1,len(timeframes)):
                                  cerebro.resampledata(data, timeframe=tframes[timeframes[i]][0], compression=tframes[timeframes[i]][1])
                          else:
                              data = readCVS()
                              for i in range(len(timeframes)):
                                  cerebro.resampledata(data, timeframe=tframes[timeframes[i]][0], compression=tframes[timeframes[i]][1])
                      
                          cerebro.broker.setcash(args.cash)
                          cerebro.addstrategy(secondStrategy)
                          cerebro.run()
                      
                          # Get final portfolio Value
                          portvalue = cerebro.broker.getvalue()
                          pnl = portvalue - startcash
                      
                          # Print out the final result
                          print('Final Portfolio Value: ${}'.format(portvalue))
                          print('P/L: ${}'.format(pnl))
                      
                          # Finally plot the end results
                          cerebro.plot(style='candlestick')
                      

                      Again, thanks

                      Mariano Bengeldorf 1 Reply Last reply Reply Quote 0
                      • Mariano Bengeldorf
                        Mariano Bengeldorf @Mariano Bengeldorf last edited by

                        @backtrader BTW, do you know how can I replace the following line?:

                        exec('self.lines.' + i + '[0] = DfMresistances.value.take([n])')
                        

                        I tried to use thes two alternatives, but they don't work:

                        #option 1: 
                        setattr(self, 'lines.' + i + '[0]', DfMresistances.value.take([n]))
                        
                        #option 2:
                        setattr(self.lines, + i + '[0]', DfMresistances.value.take([n]))
                        
                        1 Reply Last reply Reply Quote 0
                        • 1 / 1
                        • First post
                          Last post
                        Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors