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



  • 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


  • administrators

    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.



  • 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?


  • administrators

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



  • 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')
    

  • administrators

    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.



  • 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
    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!!!


  • administrators

    @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.


Log in to reply
 

});