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

Request for code review



  • Hello,
    since these are my first lines of code in backtrade(and Python),can anyone run a quick code review and post comments?
    a few words about the logic:

    1. rank the stocks in a list according to volatility(r_squared of last 90 days linear regression)
    2. open the first ones with positive linear regression slope
    3. close the ones with negative linear regression slope

    comments are highly appreciated,go brutal...

    imports ...
    class TestStrategy(bt.Strategy):
        params = (
            ('atr_parameter', 20),
            ('rank_period', 90),
        )
    
        def __init__(self):
            self.atrs = {data._name: bt.indicators.ATR(data, period=self.p.atr_parameter)
                         for data in self.datas}
    
        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                return
    
            dt = self.datas[0].datetime.date(0)
            order_type = None
            order_value = None
            if order.status in [order.Completed]:
                if order.isbuy():
                    order_type= 'buy'
                    order_value = order.executed.value
                else:  # Sell
                    order_type= 'sell'
                    order_value = order.executed.pnl
                print('%s : %s %s, (#%d@%.2f$),%.2f$' %(dt.isoformat(), order_type, order.data._name, order.executed.size, order.executed.price, order_value))
    
            elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                print('Order Canceled/Margin/Rejected')
    
        def next(self):
            open_candiates = []
            close_positions = []
            for ticker in self.getdatanames():
                symbol_quotes = self.getdatabyname(ticker)
                rank_period_quotes=list(symbol_quotes)[-self.p.rank_period:]
                x = np.arange(len(rank_period_quotes))
                slope, intercept, r_value, p_value, std_err = stats.linregress(x, rank_period_quotes)
                if(slope <= 0):
                    close_positions.append(ticker)
                else:
                    ticker_values = {}
                    ticker_values['ticker_r'] = r_value**2
                    ticker_values['position_size'] = 0.001 * self.broker.getvalue()/self.atrs[ticker][0]
                    ticker_values['ticker']=ticker
                    open_candiates.append(ticker_values)
    
            open_candiates.sort(key=itemgetter('ticker_r'), reverse=True)
    
            # close positions
            for ticker in close_positions:
                if(0 != self.broker.getposition(data = self.getdatabyname(ticker)).size):
                    self.close(data=self.getdatabyname(ticker))
    
            # open position
            for ticker in open_candiates:
                ticker_data = self.getdatabyname(ticker['ticker'])
                current_size = self.broker.getposition(data = ticker_data).size
                if(0 == current_size):
                    self.buy(data = ticker_data, size=ticker['position_size'])
    
    if __name__ == '__main__':
        isOptimizing = False    # backtesting and not optimizing
        # Create a cerebro entity
        cerebro = bt.Cerebro()
    
        # load symbol list from file
        with open('ticker_list.txt', 'r') as f:
            reader = csv.reader(f)
            tickers = list(reader)
    
        for ticker in tickers:
            print("Loading .csv for %s" % ticker[0])
            # Create a Data Feed
            data = bt.feeds.QuandlCSV(
                dataname=ticker[0]+'.csv',
                fromdate=datetime.datetime(2013, 1, 1),
                todate=datetime.datetime(2016, 12, 29),
                adjclose=False, reverse=True)
            # Add the Data Feed to Cerebro
            cerebro.adddata(data=data,name=ticker[0])
    
        # Add a strategy
        if(isOptimizing):
            strats = cerebro.optstrategy(TestStrategy,maperiod=range(10, 31))
        else:
            cerebro.addstrategy(TestStrategy)
    
        cerebro.broker.setcash(100000.0)
        cerebro.broker.setcommission(commission=0.0015)
        cerebro.run()
    


  • go brutal...

    You've asked. Why don't you debug your code by yourself?



  • Thank you for the reply,
    I'm not looking to debug the code, I'm looking for reviews, such as :

    • could I have done things differently? using other backtrader (excellent) components?

    • Could the purpose of ranking be done another way?

    • What are the potential pitfalls in the code?

    • Did I use backtrader the way it was supposed to be used?



  • @Trade-Prophet said in Request for code review:

                symbol_quotes = self.getdatabyname(ticker)
                rank_period_quotes=list(symbol_quotes)[-self.p.rank_period:]
    

    Out of curiosity ... what's the intention of that. I imagine:

    • list(symbolquotes) uses the fact that a data feed has a __len__ (one can say len(data)) and __getitem__ (one can use data[x]

    But given that backtrader doesn't use standard indexing (starting and 0 and growing), but the built-in mechanisms in Python don't know it and will construct the list by iterating from 0 -> len(data) ... this probably won't deliver the expected result ... which seems to gather all data and then slice.

    @Trade-Prophet said in Request for code review:

    if(0 == current_size):
    

    That isn't very Pythonic ... if not current_size would be. But each uses his/her own patterns.



  • Thank you very much for your reply
    and you are absolutely right! the lines you have mentioned do not produce the expected result, I'm constructing an indicator to provide linear regression data(slope,r squared) instead of the current code.
    coming from C++ lines like "if(0 == current_size):" make sense to me but I will gladly honor the Pythonic tradition.
    Thanks for the time and the professional review



  • @Paska-Houso said in Request for code review:

    • list(symbolquotes) uses the fact that a data feed has a __len__ (one can say len(data)) and __getitem__ (one can use data[x])

    See this recent thread. You probably want to use data.get(ago=x, size=y). The results will be in the expected order.