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

Candlestick pattern short strategy - array index out of range



  • I am new to backtrader and it would be great if someone could help me on this question(sorry if this is too naive to be asked). I am building a candlestick pattern short only strategy. The logics is simple, when the candlestick pattern signal is matched and also the previous bar just met the intra temporary day high, it will go short for one unit of futures. However, there is an error.

    IndexError                                Traceback (most recent call last)
    <ipython-input-1-371efe2286f7> in <module>
        106 
        107     # 4. Run
    --> 108     cerebro.run()
        109 
        110     pos = cerebro.broker.getposition(min_bar)
    
    ~\Anaconda3\lib\site-packages\backtrader\cerebro.py in run(self, **kwargs)
       1125             # let's skip process "spawning"
       1126             for iterstrat in iterstrats:
    -> 1127                 runstrat = self.runstrategies(iterstrat)
       1128                 self.runstrats.append(runstrat)
       1129                 if self._dooptimize:
    
    ~\Anaconda3\lib\site-packages\backtrader\cerebro.py in runstrategies(self, iterstrat, predata)
       1291                     self._runonce_old(runstrats)
       1292                 else:
    -> 1293                     self._runonce(runstrats)
       1294             else:
       1295                 if self.p.oldsync:
    
    ~\Anaconda3\lib\site-packages\backtrader\cerebro.py in _runonce(self, runstrats)
       1693 
       1694             for strat in runstrats:
    -> 1695                 strat._oncepost(dt0)
       1696                 if self._event_stop:  # stop if requested
       1697                     return
    
    ~\Anaconda3\lib\site-packages\backtrader\strategy.py in _oncepost(self, dt)
        307         minperstatus = self._getminperstatus()
        308         if minperstatus < 0:
    --> 309             self.next()
        310         elif minperstatus == 0:
        311             self.nextstart()  # only called for the 1st value
    
    <ipython-input-1-371efe2286f7> in next(self)
         73 
         74 
    ---> 75             if not self.position and self.sell_signal[0] == 1 and self.data.high[0] == self.current_high[0]:
         76                  self.order = self.sell_bracket(stopprice=self.data.close[0]-30, limitprice=self.data.close[0]+30)
         77 
    
    ~\Anaconda3\lib\site-packages\backtrader\linebuffer.py in __getitem__(self, ago)
        161 
        162     def __getitem__(self, ago):
    --> 163         return self.array[self.idx + ago]
        164 
        165     def get(self, ago=0, size=1):
    
    IndexError: array index out of range
    

    The full codes are here:

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    
    import datetime  # For datetime objects
    import backtrader as bt
    import backtrader.feeds as btfeeds
    import backtrader.indicators as btind
    import pandas as pd
    import numpy as np
    
    
    
    class candlestick(bt.Indicator):
        lines = ('s',)
        
        def __init__(self):
            pass
        
        def next(self):
            if self.data.open[-1] < self.data.close[-1]: # ensure the previous bar is an up bar
                if (self.data.high[-1] - self.data.close[-1]) / (self.data.close[-1] - self.data.open[-1]) <= 0.4: # ensure the previous up bar body to be large enough
                    if (self.data.open[-1] - self.data.low[-1]) / (self.data.close[-1] - self.data.open[-1]) <= 0.4: # ensure the previous up bar body to be large enough
                        if self.data.open > self.data.close: # ensure the current bar is a down bar
                            if (self.data.high - self.data.open) / (self.data.open - self.data.close) <= 0.4: # ensure the current down bar body to be large enough
                                if (self.data.close - self.data.low) / (self.data.open - self.data.close) <= 0.4: # ensure the current down bar body to be large enough
                                    if (self.data.close < self.data.open[-1]): # ensure the current down bar body to be large enough
                                        self.lines.s[0] = 1 # indicator signal equals 1 if all the above situations are correct
            
            else: self.lines.s[0] = 0
                                
                        
    
    class MyStrategy(bt.Strategy):
    
    
        def __init__(self):
            self.candlestick = candlestick(self.data) # put the candlestick indicator into the main data feed
            self.sell_signal = self.candlestick.s
            self.current_high = 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, txt))
    
    
        def print_signal(self):
            """Print to termianl ohlcv and the buy/sell signals."""
            self.log(
                "o {:.2f} \th {:.2f} \tl {:.2f} \tc {:.2f}\tv {:9.0f}\t".format(  # \tbuy {}\tsell{}".format(
                    self.data.open[0],
                    self.data.high[0],
                    self.data.low[0],
                    self.data.close[0],
                    self.data.volume[0],
                )
            )
    
        def next(self):
            if self.data.datetime == datetime.time(9,30):  
                self.current_high = self.data.high   # To reset the current day high value at the opening of each day
                        
            
            if self.data.datetime.time() > datetime.time(9,30) and self.data.datetime.time() < datetime.time(10,30):
                self.print_signal()
                    
                 # keep track of current day high
                self.current_high = bt.If(self.current_high > self.data.high[-1], self.data.high, self.current_high) 
                
    
                if not self.position and self.sell_signal[0] == 1 and self.data.high[0] == self.current_high[0]:
                     self.order = self.sell_bracket(stopprice=self.data.close[0]-30, limitprice=self.data.close[0]+30)
    
                if self.getposition().size > 0 and self.sell_signal[0] == 1 and self.data.high == self.current_high:
                     self.order = self.close()
                     self.order = self.sell_bracket(stopprice=self.data.close[0]-30, limitprice=self.data.close[0]+30)
    
    if __name__ == '__main__':
        # 1. Create a cerebro
        cerebro = bt.Cerebro(stdstats=True)
    
        # 2. Add data feed
        # 2.1 Creat a data feed
    
        # load dataframe
    
        data = pd.read_csv('futures.csv')
        data['datetime'] = pd.to_datetime(data['datetime'])
        data.set_index('datetime', inplace=True)
        min_bar = btfeeds.PandasData(dataname=data,
                                fromdate=datetime.datetime(2017, 2, 15),
                                todate=datetime.datetime(2017, 2, 18)
                                )
    
        # 2.2Add the Data Feed to Cerebro
        cerebro.adddata(min_bar)
        # 3 Add strategy
        cerebro.addstrategy(MyStrategy)
    
        # broker setting
        cerebro.broker.setcash(100000)
    
        # 4. Run
        cerebro.run()
    
        pos = cerebro.broker.getposition(min_bar)
        print('size:', pos.size)
        print('price:', pos.price)
    
        print('value:', cerebro.broker.get_value())
        print('cash:', cerebro.broker.get_cash())
    
        # 5.Plot result
        cerebro.plot(style='candle')
    


  • Would someone be able to give a hand here?



  • your code breaks at this point:

    # keep track of current day high
                self.current_high = bt.If(self.current_high > self.data.high[-1], self.data.high, self.current_high) 
                
    
                if not self.position and self.sell_signal[0] == 1 and self.data.high[0] == self.current_high[0]:
    

    i suppose you want to do this:

     # keep track of current day high
                if self.current_high > self.data.high[-1]:
                    self.current_high = self.data.high
                
    
                if not self.position and self.sell_signal[0] == 1 and self.data.high[0] == self.current_high:
    


  • Thank you very much dasch. I change your codes a bit to keep track of the daily temporary high:

    if self.data.high > self.data.high[-1]:
                    self.current_high = self.data.high
    

    But I don't know why my codes won't work. Would you mind to give me some hints on that?



  • at the begin of next method you check for a time to set current_high (it is initialized with 0)

    later you try to update it with a indicator (bt.ind.If) -> first of, i am not sure, what will happen, when you set it up here, second, you overwrite the float value current_high with that line.

    After that you try to access the current_high[0] from the previously created line current_high which has no values.

    What you could try (but i am not sure if this will work) to set the self.current_high in init this way:

    self.current_high = bt.ind.If(self.current_high > self.data.high(-1), self.data.high(), self.current_high) 
    

    and in next:

            if self.data.datetime == datetime.time(9,30):  
                self.current_high[0] = self.data.high[0]   # To reset the current day high value at the opening of each day
               
    


  • if the above code works, you should consider setting an init value for current_high in nextstart

    def nextstart(self):
        self.current_high[0] = self.data.high[0]
    

Log in to reply
 

});