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/

    Coupler indicator issue with timeframe mixing

    Indicators/Strategies/Analyzers
    2
    9
    524
    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,
      I've been working with an indicator to identify weekly resistances to use them over daily data.
      The idea is that when the price goes above the resistance the indicator line should disappear since the resistance is broken.
      The indicator works perfectly fine over the weekly chart but not on the daily chart. It prolongs the resistance one more week than what it should.
      To build this I have followed the documentation here: https://www.backtrader.com/docu/mixing-timeframes/indicators-mixing-timeframes/
      and here:
      https://www.backtrader.com/docu/data-multitimeframe/data-multitimeframe/

      Below is the chart of the coupled indicator. In violet you can see the marks where the problem occurs

      indicator.jpg

      Has anybody faced this sort of problem already?

      Thanks in advance for any help you could provide

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

        We don't know what you are doing but it would seem obvious that you calculate the resistance/support at the end of the week, hence the first days of next week, do still follow the previous value.

        But again: we don't know what you are doing.

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

          Thanks for the answer. I am not posting the whole code because it would be too much.
          Basically, I created an indicator that calculates resistances for each candle. Then I input in the indicator weekly data and used daily data to check if the resistance was broken or not. For all these I used the information provided in the links I posted before.
          My question is related to the fact that the same indicator is plotting something different in the daily chart (the one at the top) than in the weekly chart (the one at the bottom).
          In the daily chart is prolonging the resistances one whole week more than in the weekly.

          Is this the normal behavior?

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

            Once again. If you calculate the resistances weekly and apply then daily, you probably carry the values yourself.

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

              I don't fully understand what you mean.

              This is the code, you might give me some advice?:

              Many thanks!

              from __future__ import (absolute_import, division, print_function, unicode_literals)
              from datetime import datetime, timedelta
              import backtrader as bt
              from collections import OrderedDict
              from backtrader import cerebro
              import time
              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('-np', '--noprint',
                                      action='store_true',
                                      default=False,
                                      required=False,
                                      help='Print the dataframe')
              
                  parser.add_argument('-e', '--exchange',
                                      default='binance',
                                      type=str,
                                      required=False,
                                      help='Exchange')
              
                  parser.add_argument('-s', '--symbol',
                                      default='BTC/USDT',
                                      type=str,
                                      required=False,
                                      help='Symbol')
              
                  parser.add_argument('-t', '--timeframe',
                                      type=str,
                                      default='1d',
                                      choices=['1m', '5m', '15m', '30m', '1h', '2h', '3h', '4h', '6h', '12h', '1d', '1M', '1w', '1y'],
                                      help='The timeframe to download')
              
                  parser.add_argument('-t2', '--timeframe2',
                                      type=str,
                                      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')
              
              
                  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')
              
                  if not args.noprint:
                      print('--------------------------------------------------')
                      print(dataframe)
                      print('--------------------------------------------------')
              
                  # 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, high ,resistances, resNum=15, tolPercRes=2, tolRestoSupPer=2 ):
                  # creates a list of resistances with length resNum and elminates already broken resistances
                  # within a tolerance of tolerancePerc
              
                  toleranceResist = maxima * tolPercRes / 100
                  toleranceResSup = maxima * tolRestoSupPer / 100
                  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)
              
                  ResToSupport = [i for i in resistances if high - i > toleranceResSup] #this is reduntant
                  if len(ResToSupport) != 0:
                      for i in ResToSupport:
                          resistances.remove(i)
              
                  lenR = len(resistances)
              
                  if resNum != None and lenR > resNum:
                      resistances[0:lenR - resNum] = []
                  return (resistances)
              
              
              class SandR(bt.Indicator):
                  params = (
                      ('nresist', 5),
                      ('tolerance', 2),
                      ('tolerance2',2),
                      ('period', 1),
                      ('barplot', False),
                  )
              
                  nresist = params[0][1]
                  linelist = list()
              
                  for i in range(nresist):
                      linelist.append('resistance' + str(i))
              
                  lines = tuple(linelist)  # creates the lines based on the nresist parameter
              
                  plotinfo = dict(subplot=False,
                                   )
              
              
                  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=OrderedDict(resistances0 = list() , resistances1 = list())): # todo this only works for two timeframes, will edit to support more
                      dataid = self.data._id-1
                      resistanceid = 'resistances'+str(dataid)
                      resdic[resistanceid] = resismet(self.maxima[0], self.data0.high[0], resdic[resistanceid], self.params.nresist,
                                               self.params.tolerance, self.params.tolerance2)
              
                      for j in resdic.values():  #to make sure that broken resistances get deteled as soon as they are broken on the daily chart
                          ResToSupport = [i for i in j if self.data0.high[0] - i > 0]
                          if len(ResToSupport) != 0:
                              for k in ResToSupport:
                                  j.remove(k)
              
                      resistances = resdic[resistanceid]
                      if len(resistances) > 0:
              
                          n = 0
                          for i in self.linelist:
                              if len(resistances) > n:
                                  exec('self.lines.' + i + '[0]' '= resistances[-1 - n]')
                                  n = n + 1
              
              
              class secondStrategy(bt.Strategy):
              
                  def __init__(self):
              
                      #resistance0 = self.resistance0 = SandR(self.data0, plotname='Resistencia 1D', plotvaluetags=False)
                      resistance1 = self.resistance0 = SandR(self.data1, plotname='Resistencia 1W', plotvaluetags=False)
                      resistance11=resistance1()
              
                      self.buysig = self.data0.close > resistance11.resistance0
                      self.sellsig = self.data0.close < resistance11.resistance0
              
                  def next(self):   #dum strategy, don't pay attention to it, only care about indicator
              
                      if not self.position:
                          if self.buysig[0]:
                              self.buy(size=0.1)
                              #pass
                      else:
                          if self.sellsig[0]:
                              self.sell(size=0.1)
              
              
              msec = 1000
              minute = 60 * msec
              hold = 30
              
              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()
                  if not args.filepath:
                      data = loadExchange()
                      cerebro.adddata(data)
                  else:
                      data = readCVS()
                      cerebro.resampledata(data, timeframe=tframes[args.timeframe][0], compression=tframes[args.timeframe][1])
              
                  cerebro.resampledata(data, timeframe=tframes[args.timeframe2][0], compression=tframes[args.timeframe2][1])
              
              
                  cerebro.broker.setcash(args.cash)
                  cerebro.addstrategy(secondStrategy)
                  cerebro.run(runonce=False)
              
                  # 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')
              
              1 Reply Last reply Reply Quote 0
              • B
                backtrader administrators last edited by

                The larger timeframe weeks moves only once (1) for each five (5) ticks of the daily timeframe (let's simplify and forget that sometimes there are trading holidays)

                The indicator is calculated on the weekly timeframe, i.e.: when the week is complete and the 5 ticks of the week (5 days) have also been seen.

                When the next week kicks in, you coupling puts the latest calculated values from the weekly timeframe into the daily timeframe, until the end of the week is reached and new values can be calculated.

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

                  Many thanks for your answer.
                  I do see what you are explaining, but what I don't understand is why is plotting something different in the two charts.
                  BTW, is there a way to change the weekly count to 7 instead of 5? since I am working with crypto.
                  Thanks

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

                    @Mariano-Bengeldorf
                    I got it. You don't need to answer. Thanks

                    I still would like to know if it is possible to change the 5 count to 7.

                    Thanks!!!

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

                      @Mariano-Bengeldorf said in Coupler indicator issue with timeframe mixing:

                      I still would like to know if it is possible to change the 5 count to 7.

                      The platform doesn't carry any count, the count is dictated by the data itself. If your daily data is made up of 7 bars, it will tick 7 times per week.

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