Navigation

    Backtrader Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    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

    Indicators/Strategies/Analyzers
    2
    4
    1057
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Craig Chirinda
      Craig Chirinda last edited by

      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')
      
      1 Reply Last reply Reply Quote 0
      • B
        backtrader administrators last edited by backtrader

        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.

        1 Reply Last reply Reply Quote 0
        • Craig Chirinda
          Craig Chirinda last edited by

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

          Works well now.

          1 Reply Last reply Reply Quote 0
          • Craig Chirinda
            Craig Chirinda last edited by

            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()
            
            
            1 Reply Last reply Reply Quote 0
            • 1 / 1
            • First post
              Last post
            Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors