For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

Resampling issues + adding multiple timeframes



  • @dasch

    class Trend_Indicator(bt.Indicator):
        lines = ('Trend', 'ohlc')
        params = (
                ('slowma', 21),
                ('fastma', 8),
        )
    
        def __init__(self):
            
            # Add Bollinger bands
            self.BB = bt.indicators.BollingerBands(self.datas[0])
          
            self.ohlc.open = self.datas[0].open[0]
            self.ohlc.high = self.datas[0].high[0]
            self.ohlc.low = self.datas[0].low[0]
            self.ohlc.close = self.datas[0].close[0]
            
            self.SlowEMA= bt.indicators.EMA(self.datas[0],period=self.params.slowma)
            self.FastEMA = bt.indicators.EMA(self.datas[0],period=self.params.fastma)
            
            self.Trend.bollinger_touch = ""
            self.Trend.Status = ""
            self.Trend.Enter = ""
            self.Trend.ExitParameters = {"20BarRule": False, "EMARule": False, "OppositeBollingerTouch": False}
            self.Trend.EMARule = {"Close1":False, "Close2":False}
            self.Trend.Position = 0 
            self.Trend.Test = 0 
            self.addminperiod(21)
            
        def next(self):          
            
            if self.Trend.bollinger_touch == "Top" and self.data.low[0] > self.BB.lines.bot[0] and self.Trend.Enter == "":
            # if the upper bollinger band was touched and the candle we have now makes the highest high and highest close, 
            # we enter in an uptrend
                if self.data.high[0] > self.ohlc.high and self.data.close[0] > self.ohlc.close:
                    self.Trend.Enter = "Uptrend entered"
                    self.ohlc.high = self.data.high[0]
                    self.Trend.Status = "Up"
                    self.Trend.ExitParameters["OppositeBollingerTouch"] = False
                    self.Trend.ExitParameters["20BarRule"] = False
                    self.Trend.ExitParameters["EMARule"] = False
                    self.Trend.Position = len(self.ohlc)
                    self.Trend.Test = len(self.data.high)
                if self.data.high[0] > self.ohlc.high:
                    self.ohlc.high = self.data.high[0]
                if self.data.close[0] > self.ohlc.close:
                    self.ohlc.close = self.data.close[0]                
    
            # we enter in a downtrend
            elif self.Trend.bollinger_touch == 'Bottom' and self.data.high[0] < self.BB.lines.top[0] and self.Trend.Enter == "":
            # if the bottom bollinger band was touched and the candle we have now makes the lowest low and lowest close, 
            # we enter in a downtrend
                if self.data.low[0] < self.ohlc.low and self.data.close[0] < self.ohlc.close:
                    self.Trend.Enter = "Downtrend entered"
                    self.ohlc.low = self.data.low[0]
                    self.Trend.Status = "Down"
                    self.Trend.ExitParameters["OppositeBollingerTouch"] = False
                    self.Trend.ExitParameters["20BarRule"] = False
                    self.Trend.ExitParameters["EMARule"] = False
                    self.Trend.Position = len(self.ohlc)
                    self.Trend.Test = len(self.data.low)
                if self.data.low[0] < self.ohlc.low:
                    self.ohlc.low = self.data.low[0]
                if self.data.close[0] < self.ohlc.close:
                    self.ohlc.close = self.data.close[0]                
    
            elif self.Trend.bollinger_touch == "":
                # look for a top bollinger touch, then store that bar's data
                if self.data.high[0] >= self.BB.lines.top[0] and self.data.low[0] > self.BB.lines.bot[0]:
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
                    self.Trend.bollinger_touch = "Top"
                    
                elif self.data.low[0] <= self.BB.lines.bot[0] and self.data.high[0] < self.BB.lines.top[0]:
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
                    self.Trend.bollinger_touch = "Bottom"
            
            # if the upper bollinger band was touched but now the lower bollinger band got touched, the upper bollinger touch resets
            if self.Trend.bollinger_touch == "Top" and self.data.low[0] <= self.BB.lines.bot[0]:
                    self.Trend.bollinger_touch = "Bottom"
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
                    
            # if the bottom bollinger band was touched but now the top bollinger band got touched, the bottom bollinger touch resets
            elif self.Trend.bollinger_touch == "Bottom" and self.data.high[0] >= self.BB.lines.top[0]:
                    self.Trend.bollinger_touch = "Top"
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
    
            # we are in an uptrend
            if self.Trend.Status == "Up":
                # if the opposite bollinger band was touched, we exit the uptrend
                if self.data.low[0] <= self.BB.lines.bot[0]:
                    self.Trend.Status = ""
                    self.Trend.Enter = ""
                    self.Trend.ExitParameters["OppositeBollingerTouch"] = True
                    self.Trend.bollinger_touch = "Bottom"
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
                # if a fresh high is made, keep track of the position
                if self.data.high[0] > self.ohlc.high:
                    self.ohlc.high = self.data.high[0]
                    self.Trend.Position = len(self.data.high)
                # after 20 bars (excluding the first) no new high is made, we are not in a trend anymore
                elif self.data.high[0] < self.ohlc.high:
                    if len(self.data.high) - self.Trend.Position >= 21:
                        self.Trend.Status = ""
                        self.Trend.Enter = ""
                        self.Trend.ExitParameters["20BarRule"] = True
                        self.Trend.bollinger_touch = "Bottom"
                        self.Trend.EMARule['Close1'] = False
                        self.Trend.EMARule['Close2'] = False
                        self.Trend.ExitParameters['20BarRule'] = True
                        
                # if the top bollinger band was touched, reset the EMA rule
                if self.data.high[0] >= self.BB.lines.top[0]:
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
                    
                if self.data.close[0] < self.SlowEMA[0] and self.data.close[0] < self.FastEMA[0]:
                    self.Trend.EMARule['Close1'] = True
    
                if self.Trend.EMARule['Close1'] == True and self.data.close[0] > self.SlowEMA[0] and self.data.close[0] > self.FastEMA[0]:
                    self.Trend.EMARule['Close2'] = True
                    
                if self.Trend.EMARule['Close1'] == True and self.Trend.EMARule['Close2'] == True and self.data.close[0] < self.SlowEMA[0] and self.data.close[0] < self.FastEMA[0]:
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
                    self.Trend.Status = ""
                    self.Trend.Enter = ""
                    self.Trend.ExitParameters['EMARule'] = True
                    self.Trend.bollinger_touch = ""
                      
            if self.Trend.Enter == "Uptrend entered" and True not in self.Trend.ExitParameters.values():
                self.Trend[0] = 1
                
            # we are in a downtrend
            if self.Trend.Status == "Down":
                # if the opposite bollinger band was touched, we exit the downtrend
                if self.data.high[0] >= self.BB.lines.top[0]:
                    self.Trend.Status = ""
                    self.Trend.Enter = ""
                    self.Trend.ExitParameters["OppositeBollingerTouch"] = True
                    self.Trend.bollinger_touch = "Top"
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
    
                # if a fresh low is made, keep track of the position
                if self.data.low[0] < self.ohlc.high:
                    self.ohlc.low = self.data.low[0]
                    self.Trend.Position = len(self.data.low)
                # after 20 bars (excluding the first) no new low is made, we are not in a trend anymore
                elif self.data.low[0] > self.ohlc.low:
                    if len(self.data.low) - self.Trend.Position >= 21:
                        self.Trend.Status = ""
                        self.Trend.Enter = ""
                        self.Trend.ExitParameters["20BarRule"] = True
                        self.Trend.bollinger_touch = ""
                        self.Trend.EMARule['Close1'] = False
                        self.Trend.EMARule['Close2'] = False
                        self.Trend.ExitParameters['20BarRule'] = True
                        
                # if the bottom bollinger band was touched, reset the EMA rule
                if self.data.low[0] <= self.BB.lines.bot[0]:
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
                    
                if self.data.close[0] > self.SlowEMA[0] and self.data.close[0] > self.FastEMA[0]:
                    self.Trend.EMARule['Close1'] = True
    
                if self.Trend.EMARule['Close1'] == True and self.data.close[0] < self.SlowEMA[0] and self.data.close[0] < self.FastEMA[0]:
                    self.Trend.EMARule['Close2'] = True
                    
                if self.Trend.EMARule['Close1'] == True and self.Trend.EMARule['Close2'] == True and self.data.close[0] > self.SlowEMA[0] and self.data.close[0] > self.FastEMA[0]:
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
                    self.Trend.Status = ""
                    self.Trend.Enter = ""
                    self.Trend.ExitParameters['EMARule'] = True
                    self.Trend.bollinger_touch = ""
                    
            if self.Trend.Enter == "Downtrend entered" and True not in self.Trend.ExitParameters.values():
                self.Trend[0] = -1
    


  • you have some issues with your code:

            self.ohlc.open = self.datas[0].open[0]
            self.ohlc.high = self.datas[0].high[0]
            self.ohlc.low = self.datas[0].low[0]
            self.ohlc.close = self.datas[0].close[0]
    

    so change it to:

            self.ohlc.open = None
            self.ohlc.high = None
            self.ohlc.low = None
            self.ohlc.close = None
    

    you also don't need to add a minperiod, since next will be called, when your indicators are filled.

    if you want to init the attributes, you assign to the lines (?) ex. this way: self.Trend.Status = "", which works but is confusing, you could also set the inital ohlc values in nextstart:

    def nextstart(self):
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
    

    see: https://www.backtrader.com/docu/operating/



  • oh, somehow the way it will not work that way is gone. In init you will not be able to access data that way you do it.

    Check out the Stage 1 - Stage 2 difference in this doc:
    https://www.backtrader.com/docu/concepts/#stage-1-operators-create-objects
    https://www.backtrader.com/docu/concepts/#stage-2-operators-true-to-nature



  • So here is your fixed code, but to say it again, the way you set the values is confusing.

    You create a indicator line ohlc, then you set attributes to it, you never fill it with any data. Also you set attributes to the Trend line to keep track of the trend state.

    Better would be not adding the ohlc line, set all your tracking vars to the indicator.

    import backtrader as bt
    
    
    class Trend_Indicator(bt.Indicator):
        lines = ('Trend', 'ohlc')
        params = (
            ('slowma', 21),
            ('fastma', 8),
        )
    
        def __init__(self):
    
            # Add Bollinger bands
            self.BB = bt.indicators.BollingerBands(self.datas[0])
    
            self.SlowEMA = bt.indicators.EMA(
                self.datas[0], period=self.params.slowma)
            self.FastEMA = bt.indicators.EMA(
                self.datas[0], period=self.params.fastma)
    
            self.Trend.bollinger_touch = ""
            self.Trend.Status = ""
            self.Trend.Enter = ""
            self.Trend.ExitParameters = {
                "20BarRule": False, "EMARule": False, "OppositeBollingerTouch": False}
            self.Trend.EMARule = {"Close1": False, "Close2": False}
            self.Trend.Position = 0
            self.Trend.Test = 0
    
        def nextstart(self):
            self.ohlc.open = self.datas[0].open[0]
            self.ohlc.high = self.datas[0].high[0]
            self.ohlc.low = self.datas[0].low[0]
            self.ohlc.close = self.datas[0].close[0]
    
        def next(self):
    
            if self.Trend.bollinger_touch == "Top" and self.data.low[0] > self.BB.lines.bot[0] and self.Trend.Enter == "":
                # if the upper bollinger band was touched and the candle we have now makes the highest high and highest close,
                # we enter in an uptrend
                if self.data.high[0] > self.ohlc.high and self.data.close[0] > self.ohlc.close:
                    self.Trend.Enter = "Uptrend entered"
                    self.ohlc.high = self.data.high[0]
                    self.Trend.Status = "Up"
                    self.Trend.ExitParameters["OppositeBollingerTouch"] = False
                    self.Trend.ExitParameters["20BarRule"] = False
                    self.Trend.ExitParameters["EMARule"] = False
                    self.Trend.Position = len(self.ohlc)
                    self.Trend.Test = len(self.data.high)
                if self.data.high[0] > self.ohlc.high:
                    self.ohlc.high = self.data.high[0]
                if self.data.close[0] > self.ohlc.close:
                    self.ohlc.close = self.data.close[0]
    
            # we enter in a downtrend
            elif self.Trend.bollinger_touch == 'Bottom' and self.data.high[0] < self.BB.lines.top[0] and self.Trend.Enter == "":
                # if the bottom bollinger band was touched and the candle we have now makes the lowest low and lowest close,
                # we enter in a downtrend
                if self.data.low[0] < self.ohlc.low and self.data.close[0] < self.ohlc.close:
                    self.Trend.Enter = "Downtrend entered"
                    self.ohlc.low = self.data.low[0]
                    self.Trend.Status = "Down"
                    self.Trend.ExitParameters["OppositeBollingerTouch"] = False
                    self.Trend.ExitParameters["20BarRule"] = False
                    self.Trend.ExitParameters["EMARule"] = False
                    self.Trend.Position = len(self.ohlc)
                    self.Trend.Test = len(self.data.low)
                if self.data.low[0] < self.ohlc.low:
                    self.ohlc.low = self.data.low[0]
                if self.data.close[0] < self.ohlc.close:
                    self.ohlc.close = self.data.close[0]
    
            elif self.Trend.bollinger_touch == "":
                # look for a top bollinger touch, then store that bar's data
                if self.data.high[0] >= self.BB.lines.top[0] and self.data.low[0] > self.BB.lines.bot[0]:
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
                    self.Trend.bollinger_touch = "Top"
    
                elif self.data.low[0] <= self.BB.lines.bot[0] and self.data.high[0] < self.BB.lines.top[0]:
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
                    self.Trend.bollinger_touch = "Bottom"
    
            # if the upper bollinger band was touched but now the lower bollinger band got touched, the upper bollinger touch resets
            if self.Trend.bollinger_touch == "Top" and self.data.low[0] <= self.BB.lines.bot[0]:
                    self.Trend.bollinger_touch = "Bottom"
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
    
            # if the bottom bollinger band was touched but now the top bollinger band got touched, the bottom bollinger touch resets
            elif self.Trend.bollinger_touch == "Bottom" and self.data.high[0] >= self.BB.lines.top[0]:
                    self.Trend.bollinger_touch = "Top"
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
    
            # we are in an uptrend
            if self.Trend.Status == "Up":
                # if the opposite bollinger band was touched, we exit the uptrend
                if self.data.low[0] <= self.BB.lines.bot[0]:
                    self.Trend.Status = ""
                    self.Trend.Enter = ""
                    self.Trend.ExitParameters["OppositeBollingerTouch"] = True
                    self.Trend.bollinger_touch = "Bottom"
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
                # if a fresh high is made, keep track of the position
                if self.data.high[0] > self.ohlc.high:
                    self.ohlc.high = self.data.high[0]
                    self.Trend.Position = len(self.data.high)
                # after 20 bars (excluding the first) no new high is made, we are not in a trend anymore
                elif self.data.high[0] < self.ohlc.high:
                    if len(self.data.high) - self.Trend.Position >= 21:
                        self.Trend.Status = ""
                        self.Trend.Enter = ""
                        self.Trend.ExitParameters["20BarRule"] = True
                        self.Trend.bollinger_touch = "Bottom"
                        self.Trend.EMARule['Close1'] = False
                        self.Trend.EMARule['Close2'] = False
                        self.Trend.ExitParameters['20BarRule'] = True
    
                # if the top bollinger band was touched, reset the EMA rule
                if self.data.high[0] >= self.BB.lines.top[0]:
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
    
                if self.data.close[0] < self.SlowEMA[0] and self.data.close[0] < self.FastEMA[0]:
                    self.Trend.EMARule['Close1'] = True
    
                if self.Trend.EMARule['Close1'] == True and self.data.close[0] > self.SlowEMA[0] and self.data.close[0] > self.FastEMA[0]:
                    self.Trend.EMARule['Close2'] = True
    
                if self.Trend.EMARule['Close1'] == True and self.Trend.EMARule['Close2'] == True and self.data.close[0] < self.SlowEMA[0] and self.data.close[0] < self.FastEMA[0]:
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
                    self.Trend.Status = ""
                    self.Trend.Enter = ""
                    self.Trend.ExitParameters['EMARule'] = True
                    self.Trend.bollinger_touch = ""
    
            if self.Trend.Enter == "Uptrend entered" and True not in self.Trend.ExitParameters.values():
                self.Trend[0] = 1
    
            # we are in a downtrend
            if self.Trend.Status == "Down":
                # if the opposite bollinger band was touched, we exit the downtrend
                if self.data.high[0] >= self.BB.lines.top[0]:
                    self.Trend.Status = ""
                    self.Trend.Enter = ""
                    self.Trend.ExitParameters["OppositeBollingerTouch"] = True
                    self.Trend.bollinger_touch = "Top"
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
                    self.ohlc.open = self.data.open[0]
                    self.ohlc.high = self.data.high[0]
                    self.ohlc.low = self.data.low[0]
                    self.ohlc.close = self.data.close[0]
    
                # if a fresh low is made, keep track of the position
                if self.data.low[0] < self.ohlc.high:
                    self.ohlc.low = self.data.low[0]
                    self.Trend.Position = len(self.data.low)
                # after 20 bars (excluding the first) no new low is made, we are not in a trend anymore
                elif self.data.low[0] > self.ohlc.low:
                    if len(self.data.low) - self.Trend.Position >= 21:
                        self.Trend.Status = ""
                        self.Trend.Enter = ""
                        self.Trend.ExitParameters["20BarRule"] = True
                        self.Trend.bollinger_touch = ""
                        self.Trend.EMARule['Close1'] = False
                        self.Trend.EMARule['Close2'] = False
                        self.Trend.ExitParameters['20BarRule'] = True
    
                # if the bottom bollinger band was touched, reset the EMA rule
                if self.data.low[0] <= self.BB.lines.bot[0]:
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
    
                if self.data.close[0] > self.SlowEMA[0] and self.data.close[0] > self.FastEMA[0]:
                    self.Trend.EMARule['Close1'] = True
    
                if self.Trend.EMARule['Close1'] == True and self.data.close[0] < self.SlowEMA[0] and self.data.close[0] < self.FastEMA[0]:
                    self.Trend.EMARule['Close2'] = True
    
                if self.Trend.EMARule['Close1'] == True and self.Trend.EMARule['Close2'] == True and self.data.close[0] > self.SlowEMA[0] and self.data.close[0] > self.FastEMA[0]:
                    self.Trend.EMARule['Close1'] = False
                    self.Trend.EMARule['Close2'] = False
                    self.Trend.Status = ""
                    self.Trend.Enter = ""
                    self.Trend.ExitParameters['EMARule'] = True
                    self.Trend.bollinger_touch = ""
    
            if self.Trend.Enter == "Downtrend entered" and True not in self.Trend.ExitParameters.values():
                self.Trend[0] = -1
    


  • Indicator lines should be calculated as follow

    self.lines.Trend = something
    self.lines.ohlc = something
    

    You may want to read docs on indicator development one more time instead of hitting the wall continuously.



  • @ab_trader it would be good practice, i assume. I prefer also to use self.lines.x . But since backtrader creates these shortcuts, why not use them?



  • here is a cleaned up version of the indicator.

    Trend_Indicator(bt.Indicator):
        lines = ('trend',)
        params = (
            ('slow_ema', 21),
            ('fast_ema', 8),
        )
    
        def __init__(self):
    
            # Add Bollinger bands
            self.bb = bt.indicators.BollingerBands()
    
            self.slow_ema = bt.indicators.EMA(period=self.params.slow_ema)
            self.fast_ema = bt.indicators.EMA(period=self.params.fast_ema)
    
            self.ohlc = {"o": None, "h": None, "l": None, "c": None}
    
            self.trend_bollinger_touch = 0
            self.trend_status = 0
            self.trend_enter = 0
            self.trend_exit_params = {
                "20BarRule": False,
                "EMARule": False,
                "OppositeBollingerTouch": False}
            self.trend_ema_rule = {
                "Close1": False,
                "Close2": False}
            self.trend_position = 0
            self.trend_test = 0
    
        def nextstart(self):
            self._update_ohlc()
    
        def next(self):
            if (self.trend_bollinger_touch == 1
                    and self.data.low[0] > self.bb.lines.bot[0]
                    and self.trend_enter == 0):
                # if the upper bollinger band was touched and the candle
                # we have now makes the highest high and highest close,
                # we enter in an uptrend
                if (self.data.high[0] > self.ohlc["h"]
                        and self.data.close[0] > self.ohlc["c"]):
                    self._update_ohlc(["h"])
                    self.trend_enter = 1
                    self.trend_status = 1
                    self.trend_exit_params["OppositeBollingerTouch"] = False
                    self.trend_exit_params["20BarRule"] = False
                    self.trend_exit_params["EMARule"] = False
                    self.trend_position = len(self.ohlc)
                    self.trend_test = len(self.data.high)
                if self.data.high[0] > self.ohlc["h"]:
                    self._update_ohlc(["h"])
                if self.data.close[0] > self.ohlc["c"]:
                    self._update_ohlc(["c"])
    
            # we enter in a downtrend
            elif (self.trend_bollinger_touch == -1
                    and self.data.high[0] < self.bb.lines.top[0]
                    and self.trend_enter == 0):
                # if the bottom bollinger band was touched and the candle we
                # have now makes the lowest low and lowest close, we enter
                # in a downtrend
                if (self.data.low[0] < self.ohlc["l"]
                        and self.data.close[0] < self.ohlc["c"]):
                    self._update_ohlc(["l"])
                    self.trend_enter = -1
                    self.trend_status = -1
                    self.trend_exit_params["OppositeBollingerTouch"] = False
                    self.trend_exit_params["20BarRule"] = False
                    self.trend_exit_params["EMARule"] = False
                    self.trend_position = len(self.ohlc)
                    self.trend_test = len(self.data.low)
                if self.data.low[0] < self.ohlc["l"]:
                    self._update_ohlc(["l"])
                if self.data.close[0] < self.ohlc["c"]:
                    self._update_ohlc(["c"])
    
            elif self.trend_bollinger_touch == 0:
                # look for a top bollinger touch, then store that bar's data
                if (self.data.high[0] >= self.bb.lines.top[0]
                        and self.data.low[0] > self.bb.lines.bot[0]):
                    self._update_ohlc()
                    self.trend_bollinger_touch = 1
    
                elif (self.data.low[0] <= self.bb.lines.bot[0]
                        and self.data.high[0] < self.bb.lines.top[0]):
                    self._update_ohlc()
                    self.trend_bollinger_touch = -1
    
            # if the upper bollinger band was touched but now the lower bollinger
            # band got touched, the upper bollinger touch resets
            if (self.trend_bollinger_touch == 1
                    and self.data.low[0] <= self.bb.lines.bot[0]):
                self._update_ohlc()
                self.trend_bollinger_touch = -1
    
            # if the bottom bollinger band was touched but now the top bollinger
            # band got touched, the bottom bollinger touch resets
            elif (self.trend_bollinger_touch == -1
                    and self.data.high[0] >= self.bb.lines.top[0]):
                self._update_ohlc()
                self.trend_bollinger_touch = 1
    
            # we are in an uptrend
            if self.trend_status == 1:
                # if the opposite bollinger band was touched, we exit the uptrend
                if self.data.low[0] <= self.bb.lines.bot[0]:
                    self.trend_status = 0
                    self.trend_enter = 0
                    self.trend_exit_params["OppositeBollingerTouch"] = True
                    self.trend_bollinger_touch = -1
                    self.trend_ema_rule['Close1'] = False
                    self.trend_ema_rule['Close2'] = False
                    self._update_ohlc()
                # if a fresh high is made, keep track of the position
                if self.data.high[0] > self.ohlc["h"]:
                    self._update_ohlc(["h"])
                    self.trend_position = len(self.data.high)
                # after 20 bars (excluding the first) no new high is made, we are
                # not in a trend anymore
                elif self.data.high[0] < self.ohlc["h"]:
                    if len(self.data.high) - self.trend_position >= 21:
                        self.trend_status = 0
                        self.trend_enter = 0
                        self.trend_bollinger_touch = -1
                        self.trend_exit_params["20BarRule"] = True
                        self.trend_ema_rule['Close1'] = False
                        self.trend_ema_rule['Close2'] = False
                        self.trend_exit_params['20BarRule'] = True
    
                # if the top bollinger band was touched, reset the EMA rule
                if self.data.high[0] >= self.bb.lines.top[0]:
                    self.trend_ema_rule['Close1'] = False
                    self.trend_ema_rule['Close2'] = False
    
                if (self.data.close[0] < self.slow_ema[0]
                        and self.data.close[0] < self.fast_ema[0]):
                    self.trend_ema_rule['Close1'] = True
    
                if (self.trend_ema_rule['Close1'] is True
                        and self.data.close[0] > self.slow_ema[0]
                        and self.data.close[0] > self.fast_ema[0]):
                    self.trend_ema_rule['Close2'] = True
    
                if (self.trend_ema_rule['Close1'] is True
                        and self.trend_ema_rule['Close2'] is True
                        and self.data.close[0] < self.slow_ema[0]
                        and self.data.close[0] < self.fast_ema[0]):
                    self.trend_ema_rule['Close1'] = False
                    self.trend_ema_rule['Close2'] = False
                    self.trend_exit_params['EMARule'] = True
                    self.trend_enter = 0
                    self.trend_status = 0
                    self.trend_bollinger_touch = 0
    
            # we are in a downtrend
            if self.trend_status == -1:
                # if the opposite bollinger band was touched, we exit the downtrend
                if self.data.high[0] >= self.bb.lines.top[0]:
                    self.trend_status = 0
                    self.trend_enter = 0
                    self.trend_bollinger_touch = 1
                    self.trend_exit_params["OppositeBollingerTouch"] = True
                    self.trend_ema_rule['Close1'] = False
                    self.trend_ema_rule['Close2'] = False
                    self._update_ohlc()
    
                # if a fresh low is made, keep track of the position
                if self.data.low[0] < self.ohlc["h"]:
                    self._update_ohlc(["l"])
                    self.trend_position = len(self.data.low)
                # after 20 bars (excluding the first) no new low is made, we are
                # not in a trend anymore
                elif self.data.low[0] > self.ohlc["l"]:
                    if len(self.data.low) - self.trend_position >= 21:
                        self.trend_status = 0
                        self.trend_enter = 0
                        self.trend_bollinger_touch = 0
                        self.trend_exit_params["20BarRule"] = True
                        self.trend_ema_rule['Close1'] = False
                        self.trend_ema_rule['Close2'] = False
                        self.trend_exit_params['20BarRule'] = True
    
                # if the bottom bollinger band was touched, reset the EMA rule
                if self.data.low[0] <= self.bb.lines.bot[0]:
                    self.trend_ema_rule['Close1'] = False
                    self.trend_ema_rule['Close2'] = False
    
                if (self.data.close[0] > self.slow_ema[0]
                        and self.data.close[0] > self.fast_ema[0]):
                    self.trend_ema_rule['Close1'] = True
    
                if (self.trend_ema_rule['Close1'] is True
                        and self.data.close[0] < self.slow_ema[0]
                        and self.data.close[0] < self.fast_ema[0]):
                    self.trend_ema_rule['Close2'] = True
    
                if (self.trend_ema_rule['Close1'] is True
                        and self.trend_ema_rule['Close2'] is True
                        and self.data.close[0] > self.slow_ema[0]
                        and self.data.close[0] > self.fast_ema[0]):
                    self.trend_status = 0
                    self.trend_enter = 0
                    self.trend_bollinger_touch = 0
                    self.trend_ema_rule['Close1'] = False
                    self.trend_ema_rule['Close2'] = False
                    self.trend_exit_params['EMARule'] = True
    
            # update line
            if (self.trend_enter == -1
                    and True not in self.trend_exit_params.values()):
                self.lines.trend[0] = -1
            elif (self.trend_enter == 1
                    and True not in self.trend_exit_params.values()):
                self.lines.trend[0] = 1
    
        def _update_ohlc(self, values=["o", "h", "l", "c"]):
            if "o" in values:
                self.ohlc["o"] = self.data.open[0]
            if "h" in values:
                self.ohlc["h"] = self.data.high[0]
            if "l" in values:
                self.ohlc["l"] = self.data.low[0]
            if "c" in values:
                self.ohlc["c"] = self.data.close[0]
    


  • @dasch I didn't see indicators made this way in the docs or blog posts. Can you share a link?



  • @ab_trader here: https://www.backtrader.com/docu/inddev/

    Let’s recall that self.lines.dummyline is the long notation and that it can be shortened to:

    self.l.dummyline
    

    and even to:

    self.dummyline
    

    The latter being only possible if the code has not obscured this with a member attribute.



  • @dasch I see, thank you. Looks like I just forgot it since it was not used in the original bt scripts I've used as examples.



  • @ab_trader there are a lot of shortcuts to different lines, which are not used that much in examples, so it would only make sense to use them, when you know what you are doing.

    Thats why your statement is mostly the way to go to use self.lines.x (which also is more readable in code)



  • @dasch thanks a lot. much appreciated
    Yes it probably could be a lot better than I have created it. I'm still quite new to backtrader so learning bit by bit.
    what was then actually the problem which caused the error?



  • @Jens-Halsberghe the actual problem was just the assignment of ohlc values in init:

            self.ohlc.open = self.datas[0].open[0]
            self.ohlc.high = self.datas[0].high[0]
            self.ohlc.low = self.datas[0].low[0]
            self.ohlc.close = self.datas[0].close[0]
    


  • @dasch said in Resampling issues + adding multiple timeframes:

    there are a lot of shortcuts to different lines, which are not used that much in examples, so it would only make sense to use them, when you know what you are doing.

    I totally agree.


Log in to reply
 

});