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

Extended Data Feed: Using Additional Feed Variables as Primary Indicators



  • Regards,

    I successfully managed to load an extended dataset with an indicator that was calculated outside the Backtrader coding pipeline. In my strategy, it is named: "predictions".

    The problem comes when I try to use this variable as a trading indicator: No trades are effected. Can anyone help? My code is below:

    # Define Dynamic Class for Loading Data   
    lines = ('datetime', 'open', 'high', 'low', 'close', 'volume', 'openinterest', 'predictions')
        
    params = (('datetime', -1),
                  ('open', -1),
                  ('high', -1),
                  ('low', -1),
                  ('close', -1),
                  ('volume', -1),
                  ('openinterest', -1),
                  ('predictions', -1))
        
    datafields = btfeeds.PandasData.datafields + (['datetime', 
                                                       'open', 
                                                       'high', 
                                                       'low', 
                                                       'close', 
                                                       'volume', 
                                                       'openinterest', 
                                                       'predictions'])
        
    mydict = dict(lines=tuple(lines), params=params, datafields=bt.feeds.PandasData.datafields + list(lines),)
    
    PandasSPY = type('SPY', (btfeeds.PandasData,), mydict)
    
    
    # Trading Strategy
    class SPYStrategy(bt.Strategy):
         
        def __init__(self):
            self.predictions = self.datas[0].predictions
     
        def next(self):
            if self.data.close[0] < self.data.close[-1] and self.predictions > 0:
                print("Signal Detected")
                self.buy(size=1000)
                    
        
        def notify_trade(self, trade):
            if trade.isclosed:
                dt = self.data.datetime.date()
     
                print('---------------------------- TRADE ---------------------------------')
                print("1: Data Name:                            {}".format(trade.data._name))
                print("2: Bar Num:                              {}".format(len(trade.data)))
                print("3: Current date:                         {}".format(dt))
                print('4: Status:                               Trade Complete')
                print('5: Ref:                                  {}'.format(trade.ref))
                print('6: PnL:                                  {}'.format(round(trade.pnl,2)))
                print('--------------------------------------------------------------------')
    
    
    
        
    # Variable for our starting cash
    startcash = 10000
        
    # Create a cerebro entity
    cerebro = bt.Cerebro(stdstats=False)
        
    # Set Commission - The Proportional Transaction Cost is Set as the Commission 
    cerebro.broker.setcommission(commission=0.004199108990760269)
            
    # Add a strategy
    cerebro.addstrategy(SPYStrategy)
    
    # Get a pandas dataframe
    datapath = ('event_based_backtesting.csv')
    
    # Compile dataframe object
    dataframe = pandas.read_csv(datapath,
                                    parse_dates=True,
                                    index_col=0)
    
    
    # Pass it to the backtrader datafeed and add it to the cerebro
    data = PandasSPY(dataname=dataframe)
    
    cerebro.adddata(data)
    
    # Run over everything
    cerebro.run()
    
    # 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

    1. That's a very complex and wrong way of extending the data feed. Using datafields was deprecated long ago, but that's not the wrong part.

    class MyPandasdata(bt.feeds.PandasData):
    
        lines = ('only_new_lines_go_here',)
        # NOT ALL PARAMS need be declared
        params = dict(
            redefine_param=x,
            param_for_new_line=y,
        )
    

    Note: datetime=-1 is not probably what you want (and it is not the default value in PandasData), but with missing data (see below) it is impossible to know.

    2. In order for anyone to know why your trades are not executing, let me suggest that you

    • Show some sample data
    • Show logging of what's happening with the orders (are the orders getting rejected?)

    It may be something as simple as not having enough cash.



  • Resolved! The issue stemmed from my inclusion of BT pipeline OHLCV parameters in the "lines" object for added variable.

    Works well now.



  • For the benefit of others, I have decided to share the full code of the datafeed extension solution that worked for me:

    # Define Dynamic Class for Loading Data  
    
    class GenericCSV(bt.feeds.GenericCSVData):
        
        lines = ('stock_p_change', 'predictions')
        
        params = (('nullvalue', float('NaN')),
                  ('dtformat', '%m/%d/%Y'),
                  ('stock_p_change', 7),
                  ('predictions', 8))
    
    
    # Trading Strategy
    class SPYStrategy(bt.Strategy):
        
        def __init__(self):
    
            # The indicator autoregisters and will plot even if no obvious
            # reference is kept to it in the class
            pass 
            
        def next(self):
            if self.data.predictions > 0 and self.data.stock_p_change < -0.005:
                self.buy(stake=1000)
            
            else:
                if self.data.predictions < 1 and self.data.stock_p_change > 0:
                    self.close()
    
        
        def notify_trade(self, trade):
            dt = self.data.datetime.date()
            if trade.isclosed:
                print('{} {} Closed: PnL Gross {}, Net {}'.format(
                                                    dt,
                                                    trade.data._name,
                                                    round(trade.pnl,2),
                                                    round(trade.pnlcomm,2)))
    
    # Variable for our starting cash
    startcash = 10000
        
    # Create a cerebro entity
    cerebro = bt.Cerebro(stdstats=False)
        
    # Set Commission - The Proportional Transaction Cost is Set as the Commission 
    cerebro.broker.setcommission(commission=0.005199108990760269)
            
    # Add a strategy
    cerebro.addstrategy(SPYStrategy)
    
    # Get Data
    data = GenericCSV(dataname="event_based_backtesting.csv")
    
    cerebro.adddata(data)
    
    # Run over everything
    cerebro.run()
    
    # 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='line')
    

    To check if the added variables are being read in correctly--sometimes using the parameter "7" for your added variables may accidentally parse "openinterest" as an added variable--you can use this code:

    # Define Dynamic Class for Loading Data  
    
    class GenericCSV(bt.feeds.GenericCSVData):
        
        lines = ('stock_p_change', 'predictions')
        
        params = (('nullvalue', float('NaN')),
                  ('dtformat', '%m/%d/%Y'),
                  ('stock_p_change', 7),
                  ('predictions', 8))
    
    
    # Trading Strategy
    class SPYStrategy(bt.Strategy):
        
        def __init__(self):
    
            # The indicator autoregisters and will plot even if no obvious
            # reference is kept to it in the class
            pass 
            
        def next(self):
            print('%03d %f %f, %f' % (
                len(self),
                self.data.close[0],
                self.data.stock_p_change[0],
                self.data.predictions[0],))
    
    
    # Create a cerebro entity
    cerebro = bt.Cerebro(stdstats=False)
        
    # Add a strategy
    cerebro.addstrategy(SPYStrategy)
    
    # Get Data
    data = GenericCSV(dataname="event_based_backtesting.csv")
    
    cerebro.adddata(data)
    
    # Run over everything
    cerebro.run()
    
    

Log in to reply
 

});