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

storing the candle data which touched a bollinger band



  • Hi,

    I'm trying to do the following:

    a) if a candle touches or breaks the level of the lower bollinger band, store its data
    b) if any candle after (unless the top of the bollinger band gets hit), breaks the candle from a)'s low, then replace this candle

    It seems the way that I am storing the candle's data is incorrect

    # Create a Stratey
    class TestStrategy(bt.Strategy):
    
    
        def log(self, txt, dt=None):
            ''' Logging function fot this strategy'''
            dt = dt or self.datas[0].datetime.datetime(0)
            print('%s, %s' % (dt.strftime("%Y-%m-%d %H:%M"), txt))
    
        def __init__(self):
            # Keep a reference to the "close" line in the data[0] dataseries
            self.dataclose = self.datas[0].close
    
            # Add Bollinger bands
            self.BB = bt.indicators.BollingerBands(self.datas[0])
          
        def next(self):
            
            # The candle touches the lower bollinger band
            if self.data.low[0] <= self.BB.lines.bot[0]:
                self.BBlowtouch = self.data   
    
            # if there is a touch of the lower bollinger and the low is lower than the previous candle which touched the lower bollinger, it resets
                if self.data.low[0] < self.BBlowtouch.low[0] and self.data.low[0] <= self.BB.lines.bot[0]:
                    self.BBlowtouch = self.data
                    self.log('BB Low broken touched')                    
                    
    if __name__ == '__main__':
        cerebro = bt.Cerebro()
        
        # Add a strategy
        cerebro.addstrategy(TestStrategy)
        
        cerebro.addsizer(bt.sizers.FixedSize, stake=1000)
            
        # Create a Data Feed
        minute_data = bt.feeds.PandasData(dataname=df2020)
    
        # Add the Data Feed to Cerebro
        cerebro.adddata(minute_data)
    
        print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
        cerebro.run()
         print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    

    I thought I might have to add self.BBlowtouch to the init function but then I'm not sure what to instantiate it with.
    It just seem that when I do self.BBlowtouch = self.data I'm not actually storing self.data's values at the time of that bar but it will update the values for each bar.



  • There's a bit of confusion between lines and static values. When setting equal to self.data you need to set it at the current bar.

    self.BBlowtouch = self.data[0]
    

    And later when using self.BBlowtouch to compare, don't use .low[0] since this is a single variable.

    if self.data.low[0] < self.BBlowtouch  ...etc
    


  • thanks so much again run-out. this is what I thought what was happening. I will try this out later today!



  • One other thing I need to know is how to declare self.BBlowtouch with empty data. I can only have this information once I have had the first bar which has touched the lower bollinger band. It's causing me later issues when I'm running conditionals as

        def __init__(self):
            # Keep a reference to the "close" line in the data[0] dataseries
            self.dataclose = self.datas[0].close
    
            # Add Bollinger bands
            self.BB = bt.indicators.BollingerBands(self.datas[0])
            
            self.BBlowtouchbool = False
          
        def next(self): 
            
            # Mark if the candle touched or broke the bottom bollinger band
            # Record that candle's information
            if self.data.low[0] <= self.BB.lines.bot[0]:
                self.BBlowtouch = self.data[0] 
                self.BBlowtouchbool = True
                self.log('BB low touched')
    
            if self.data.low[0] < self.BBlowtouch.low[0]: # this will give me an error as self.BBlowtouch wasn't created yet
                self.BBlowtouch = self.data[0]
                self.log('BB low broken')
                self.log(BBlowtouch.low[0])
    
    # Error:
    AttributeError: 'Lines_LineSeries_LineIterator_DataAccessor_Strateg' object has no attribute 'BBlowtouch'
    


  • You can set up a dummy line. Read here.



  • I'm still stuck with this.

    I have added a line and then added 0 for the OHLCV data.

    # Create a Stratey
    class TestStrategy(bt.Strategy):
    
        lines = ('LowestBB',)
        params = (('Open', 0),('High',0),('Low',0),('Close',0),('Volume',0))
    
        def log(self, txt, dt=None):
            ''' Logging function for this strategy'''
            dt = dt or self.datas[0].datetime.datetime(0)
            print('%s, %s' % (dt.strftime("%Y-%m-%d %H:%M"), txt))
    
        def __init__(self):
            # Keep a reference to the "close" line in the data[0] dataseries
            self.dataclose = self.datas[0].close
            
            # Addy dummy to initialize the bblowtouchdata
            
    #         self.lines.BBlowtouch = self.params.OHLCV
    
            # Add Bollinger bands
            self.BB = bt.indicators.BollingerBands(self.datas[0])
            
            self.LowestBB.Open = 0 
            self.LowestBB.High = 0
            self.LowestBB.Low = 0
            self.LowestBB.Close = 0 
            self.LowestBB.Volume = 0
          
        def next(self): 
            
            
    #         # Mark if the candle touched or broke the bottom bollinger band
    #         # Record that candle's information
            if self.data.low[0] <= self.BB.lines.bot[0] and self.LowestBB.Low < self.data.low[0]:
                self.BBlowtouch = self.data[0] 
                self.LowestBB = self.data.Low[0]
                
                    
    if __name__ == '__main__':
        cerebro = bt.Cerebro()
        
        # Add a strategy
        cerebro.addstrategy(TestStrategy)
        
        cerebro.addsizer(bt.sizers.FixedSize, stake=1000)
            
        # Create a Data Feed
        minute_data = bt.feeds.PandasData(dataname=df2020)
    
        # Add the Data Feed to Cerebro
        cerebro.adddata(minute_data)
    
        print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
        cerebro.run()
        print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    
    
    TypeError: 'int' object is not subscriptable
    

    I'm getting an error. It makes sense to me as I have just assigned zeros to the different attributes. I'm sure I have to assign a data structure to BBlowtouch similar to "datas" which I think is a list. I'm just not sure how to proceed



  • I'm still not sure I follow what you are doing but it seems to me that you are trying to store one bar of data, and then change that stored bar over time. If this is the case, then it's pretty simple.

    Just set up the variables individually in init.

    def __init__(self):
        self.open = 0
        self.high = 0
        self.low = 0
        self.close = 0
        self.volume = 0
    

    If you like, you can set their values at the first bar.

    def next(self):
        if len(self) == 0:
            self.open = self.datas[0].open[0]
            self.high = self.datas[0].high[0]
            self.low = self.datas[0].low[0]
            self.close = self.datas[0].close[0]
            self.volume = self.datas[0].volume[0]
        else:
            pass
    

    Then your condition:

        # The candle touches the lower bollinger band
        if self.datas[0].low[0] <= self.BB.lines.bot[0]:
            self.open = self.datas[0].open[0]
            self.high = self.datas[0].high[0]
            self.low = self.datas[0].low[0]
            self.close = self.datas[0].close[0]
            self.volume = self.datas[0].volume[0]
    

    You might want to set this up in a funtion:

    
    def track_ohlcv(self, data):
        """  Resets the candle tracking bar.   """
        self.open = self.datas[0].open[0]    
        self.high = self.datas[0].high[0]
        self.low = self.datas[0].low[0]    
        self.close = self.datas[0].close[0]
        self.volume = self.datas[0].volume[0]
    
        return None
    


  • @run-out

    thanks again, you've been very helpful. yes, that was what I was trying to do, to store the bar data. I've been able to apply all of this to my code and it works.

    Just wondering if there is not a more optimal way to assign the current bar's data to the - let's call it the track_ohlcv bar- instead of doing self.open =, self.high= etc

    would there be no way to do something like
    self.ohlcv.data = self.data (not adding the zero as I want the static data)



  • there doesn't seem to be a way to edit my post.

    Just want to state that at the moment it seems an issue to me when I want to create an empty ohlcv bar at the start (where all values are 0)
    and after, I want to do self.ohlcv.data = self.data, that it doesn't work that way. I might have missed something. It's more about writing cleaner code than utility as I can make it work the way I have it now.



  • You may take a look at the _Bar class as an example (it is an internal class in Backtrader) and create something similar for your needs.


Log in to reply
 

});