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

AttributeError: 'str' object has no attribute '_name' - The error is clear - but for the life of me I can't see why it's happening



  • It's a multi-data stratergy and I have data objects in the 'next' function cycling as the entries in self.datas - in this function everything seems to work fine in interating with the data objects, in partcular they certainly do have name attributes - this error seems to occur after the cycle of "next's" is complete.

    Apologies if this turns out to be something trivial.

    The stack trace is as follows:

    Traceback (most recent call last):
      File "trader.py", line 472, in <module>
        runstrategy(stocks_of_interest,financial_history, pairs_list)
      File "trader.py", line 346, in runstrategy
        oldsync=args.oldsync)
      File "C:\ProgramData\Anaconda3\envs\finance\lib\site-packages\backtrader\cerebro.py", line 1127, in run
        runstrat = self.runstrategies(iterstrat)
      File "C:\ProgramData\Anaconda3\envs\finance\lib\site-packages\backtrader\cerebro.py", line 1293, in runstrategies
        self._runonce(runstrats)
      File "C:\ProgramData\Anaconda3\envs\finance\lib\site-packages\backtrader\cerebro.py", line 1688, in _runonce
        self._brokernotify()
      File "C:\ProgramData\Anaconda3\envs\finance\lib\site-packages\backtrader\cerebro.py", line 1360, in _brokernotify
        self._broker.next()
      File "C:\ProgramData\Anaconda3\envs\finance\lib\site-packages\backtrader\brokers\bbroker.py", line 1240, in next
        self._get_value()  # update value
      File "C:\ProgramData\Anaconda3\envs\finance\lib\site-packages\backtrader\brokers\bbroker.py", line 440, in _get_value
        comminfo = self.getcommissioninfo(data)
      File "C:\ProgramData\Anaconda3\envs\finance\lib\site-packages\backtrader\broker.py", line 80, in getcommissioninfo
        if data._name in self.comminfo:
    AttributeError: 'str' object has no attribute '_name'
    

    For clarity I can attach the code if that would be helpful but I didn't want to clutter this post unnecessarily?

    Asked for code:

    
    class PairTradingStrategy(bt.Strategy):
        params = dict(
            period=300,
            stake=1,
            qty1=0,
            qty2=0,
            printout=True,
            upper=1,
            lower=-1,
            up_medium=0.75,
            low_medium=-0.75,
            status=0,
            portfolio_value=50,
        )
    
        def log(self, txt, dt=None):
            if self.p.printout:
                dt = dt or self.data.datetime[0]
                dt = bt.num2date(dt)
                print('%s, %s' % (dt.isoformat(), txt))
    
        def notify_order(self, order):
            if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
                return  # Await further notifications
    
            if order.status == order.Completed:
                if order.isbuy():
                    buytxt = 'BUY COMPLETE, %.2f' % order.executed.price
                    self.log(buytxt, order.executed.dt)
                else:
                    selltxt = 'SELL COMPLETE, %.2f' % order.executed.price
                    self.log(selltxt, order.executed.dt)
    
            elif order.status in [order.Expired, order.Canceled, order.Margin]:
                self.log('%s ,' % order.Status[order.status])
                pass  # Simply log
    
            # Allow new orders
            self.orderid = None
    
        def __init__(self,pairs_list):
            # To control operation entries
            self.pairs_list = pairs_list
            self.orderid = None
            self.qty1 = self.p.qty1
            self.qty2 = self.p.qty2
            self.upper_limit = self.p.upper
            self.lower_limit = self.p.lower
            self.up_medium = self.p.up_medium
            self.low_medium = self.p.low_medium
            self.status = self.p.status
            self.portfolio_value = self.p.portfolio_value
    
    
            #kalman filter stuff -- curently not sure what its about, just want that beta
            self.delta = 0.0001
            self.Vw = self.delta / (1 - self.delta) * np.eye(2)
            self.Ve = 0.001
            self.beta = np.zeros(2)
            self.P = np.zeros((2, 2))
            self.R = np.zeros((2, 2))
    
    
            # Signals performed with PD.OLS :
            self.transform = btind.OLS_TransformationN(self.data0, self.data1,
                                                       period=self.p.period)
            self.zscore = self.transform.zscore
            #self.beta = pd.ols(self.data0, self.data1, window_type=self.p.period)
    
    
            x = np.asarray([self.data0[0], 1.0]).reshape((1, 2))
            y = self.data1[0]
    
            self.R = self.P + self.Vw  # state covariance prediction
            yhat = x.dot(self.beta)  # measurement prediction
    
            Q = x.dot(self.R).dot(x.T) + self.Ve  # measurement variance
    
            e = y - yhat  # measurement prediction error
    
            K = self.R.dot(x.T) / Q  # Kalman gain
    
            self.beta += K.flatten() * e  # State update
    
            self.P = self.R - K * x.dot(self.R)
    
            sqrt_Q = np.sqrt(Q)
    
            self.lower_limit = -1.5*sqrt_Q
            self.upper_limit = 1.5*sqrt_Q
    
            self.up_medium = sqrt_Q
            self.low_medium = -sqrt_Q
    
            # Checking signals built with StatsModel.API :
            # self.ols_transfo = btind.OLS_Transformation(self.data0, self.data1,
            #                                             period=self.p.period,
            #                                             plot=True)
    
        def next(self):
    
            ########PRETTTYYYYYYYYYY SURE SHORT SELLS ARENT WORKING AS THEY SHOULDDDD......
    
            # data is in self.datas
    
            # for i, d in enumerate(self.datas):
            #     dt, dn = self.datetime.date(), d._name
            #     pos = self.getposition(d).size
    
            #     for p in pairs_list:
            #         if p[0] == d._name:
            #             #no current market orders
            #             if pos == None
    
                # if self.orderid:
                #     return  # if an order is active, no new orders are allowed
    
            def get_data_by_name(name):
                output = None
                for data in self.datas:
                    #print(data)
                    if data._name == name:
                        output = data
    
    
                if output is None:
                    #shoulf probably create a log
                    print('Warning: The data with name: '+ name+ ' could not be found.')
    
                return(output)
    
            print(self.pairs_list)
    
            for pair in self.pairs_list:
                p0_data = get_data_by_name(pair[0])
                p1_data = get_data_by_name(pair[1])
                print(pair)
    
    
                #if data isnt available just skip it
                if p0_data is not None and p1_data is not None :
                    print(p0_data)
                    print(p1_data)
                    print(p0_data._name)
                    print(p1_data._name)
    
                    print('positions')
                    print(self.getposition(pair[0]) )
                    print(self.getposition(pair[1]) )
    
                    if self.getposition(pair[0]).size ==0 and self.getposition(pair[1]).size == 0:
    
                        print('hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh')
                        if self.p.printout:
                            print('Self  len:', len(self))
                            print('Data0 len:', len(self.data0))
                            print('Data1 len:', len(self.data1))
                            print('Data0 len == Data1 len:',
                                  len(p0_data) == len(p1_data))
    
                            print('Data0 dt:', p0_data.datetime.datetime())
                            print('Data1 dt:', p1_data.datetime.datetime())
    
                        print('status is', self.status)
                        print('zscore is', self.zscore[0])
    
                        # Step 2: Check conditions for SHORT & place the order
                        # Checking the condition for SHORT
                        if (self.zscore[0] > self.upper_limit) and (self.status != 1):
    
                            # Calculating the number of shares for each stock
                            value = 0.5 * self.portfolio_value  # Divide the cash equally
                            x = int(value / (p0_data.close))  # Find the number of shares for Stock1
                            y = int(value / (p1_data.close))  # Find the number of shares for Stock2
                            print('x + self.qty1 is', x + self.qty1)
                            print('y + self.qty2 is', y + self.qty2)
    
                            # Placing the order
                            self.log('SELL CREATE %s, price = %.2f, qty = %d' % (p0_data._name, p0_data.close[0], x + self.qty1))
                            self.sell(data=p0_data, size=(x + self.qty1))  # Place an order for buying y + qty2 shares
                            self.log('BUY CREATE %s, price = %.2f, qty = %d' % (p1_data._name, p1_data.close[0], y + self.qty2*self.beta[0]))
                            self.buy(data=p1_data, size=(y + self.qty2*self.beta[0]))  # Place an order for selling x + qty1 shares
    
                            # Updating the counters with new value
                            self.qty1 = x  # The new open position quantity for Stock1 is x shares
                            self.qty2 = y  # The new open position quantity for Stock2 is y shares
    
                            # doesnt make sense for multiple feeds self.status = 1  # The current status is "short the spread"
    
                            # Step 3: Check conditions for LONG & place the order
                            # Checking the condition for LONG
                        elif (self.zscore[0] < self.lower_limit) and (self.status != 2):
    
                            # Calculating the number of shares for each stock
                            value = 0.5 * self.portfolio_value  # Divide the cash equally
                            x = int(value / (self.p0_data.close))  # Find the number of shares for Stock1
                            y = int(value / (self.data1.close))  # Find the number of shares for Stock2
                            print('x + self.qty1 is', x + self.qty1)
                            print('y + self.qty2 is', y + self.qty2)
    
                            # Place the order
                            self.log('BUY CREATE %s, price = %.2f, qty = %d' % (p0_data._name, p0_data.close[0], x + self.qty1))
                            self.buy(data=p0_data, size=(x + self.qty1))  # Place an order for buying x + qty1 shares
                            self.log('SELL CREATE %s, price = %.2f, qty = %d' % (p1_data._name, p1_data.close[0], y + self.qty2*self.beta[0]))
                            self.sell(data=p1_data, size=(y + self.qty2*self.beta[0]))  # Place an order for selling y + qty2 shares
    
                            # Updating the counters with new value
                            self.qty1 = x  # The new open position quantity for Stock1 is x shares
                            self.qty2 = y  # The new open position quantity for Stock2 is y shares
                            self.status = 2  # The current status is "long the spread"
    
    
                            # Step 4: Check conditions for No Trade
                            # If the z-score is within the two bounds, close all
                    
                    elif (self.zscore[0] < self.up_medium and self.zscore[0] > self.low_medium):
                        self.log('CLOSE LONG %s, price = %.2f' % (p0_data._name, p0_data.close[0]))
                        self.close(p0_data)
                        self.log('CLOSE LONG %s, price = %.2f' % (p1_data._name, p1_data.close[0]))
                        self.close(p1_data)
                    print('yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy')
    
                print('got to the ennnnnnnnnndddddddddddddddddd')    
    
        def stop(self):
            print('==================================================')
            print('Starting Value - %.2f' % self.broker.startingcash)
            print('Ending   Value - %.2f' % self.broker.getvalue())
            print('==================================================')
    
    
    
    
    
    def runstrategy(stocks_of_interest,financial_history_dict, pairs_list):
    
        args = parse_args()
    
        # Create a cerebro
        cerebro = bt.Cerebro()
    
        # Get the dates from the args
        fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
        todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')
    
        # # Create the 1st data
        # data0 = btfeeds.YahooFinanceCSVData(
        #     dataname=args.data0,
        #     fromdate=fromdate,
        #     todate=todate)
    
        # # Add the 1st data to cerebro
        # cerebro.adddata(data0)
    
        # # Create the 2nd data
        # data1 = btfeeds.YahooFinanceCSVData(
        #     dataname=args.data1,
        #     fromdate=fromdate,
        #     todate=todate)
    
        # # Add the 2nd data to cerebro
        # cerebro.adddata(data1)
    
        ############################# data other 
    
    
        # # Simulate the header row isn't there if noheaders requested
        # skiprows = 1 if args.noheaders else 0
        # header = None if args.noheaders else 0
    
        #must be a better way to do this
        stocks_in_order_used = []
        skipped = []
        for stock in stocks_of_interest:
            try:
                dataframe = financial_history_dict[stock]
                data = bt.feeds.PandasData(dataname=dataframe)
                cerebro.adddata(data, name=stock)
            except KeyError:
                    print('skipping ' + stock)
                    skipped += [stock]
            
            #print(dataframe)
    
    
        #bcause doing it here prevents things fucking up
        adj_pairs_list=[]
        print(pairs_list)
        for pair in pairs_list:
            dont_skip = True
            for skip_stock in skipped:
                if skip_stock in pair:
                    dont_skip = False
    
    
            if dont_skip:
                adj_pairs_list.append(pair) 
    
        print('adjjjjjjjjjjjjjjj')
        print(adj_pairs_list)
    
        # Add the strategy
        cerebro.addstrategy(PairTradingStrategy,pairs_list = adj_pairs_list,
                            period=args.period,
                            stake=args.stake)
    
        # Add the commission - only stocks like a for each operation
        cerebro.broker.setcash(args.cash)
    
        # Add the commission - only stocks like a for each operation
        cerebro.broker.setcommission(commission=args.commperc)
    
        # And run it
        #cerebro.run()
    
    
        cerebro.run(runonce= not args.runnext,
                     preload= not args.nopreload,
                     oldsync=args.oldsync)
    
        # Plot if requested
        if args.plot:
            cerebro.plot(numfigs=args.numfigs, volume=False, zdown=False)
    
    
    def parse_args():
        parser = argparse.ArgumentParser(description='MultiData Strategy')
    
        parser.add_argument('--data0', '-d0',
                            default='data/csvs/CMP.csv',
                            help='1st data into the system')
    
        parser.add_argument('--data1', '-d1',
                            default='data/csvs/SLCA.csv',
                            help='2nd data into the system')
    
        parser.add_argument('--fromdate', '-f',
                            default='2015-01-01',
                            help='Starting date in YYYY-MM-DD format')
    
        parser.add_argument('--todate', '-t',
                            default='2019-06-01',
                            help='Starting date in YYYY-MM-DD format')
    
        parser.add_argument('--period', default=60, type=int,
                            help='Period to apply to the Simple Moving Average')
    
        parser.add_argument('--cash', default=100, type=int,
                            help='Starting Cash')
    
        parser.add_argument('--runnext', action='store_true',
                            help='Use next by next instead of runonce')
    
        parser.add_argument('--nopreload', action='store_true',
                            help='Do not preload the data')
    
        parser.add_argument('--oldsync', action='store_true',
                            help='Use old data synchronization method')
    
        parser.add_argument('--commperc', default=0.005, type=float,
                            help='Percentage commission (0.005 is 0.5%%')
    
        parser.add_argument('--stake', default=1, type=int,
                            help='Stake to apply in each operation')
    
        parser.add_argument('--plot', '-p', default=True, action='store_true',
                            help='Plot the read data')
    
        parser.add_argument('--numfigs', '-n', default=1,
                            help='Plot using numfigs figures')
    
        return parser.parse_args()
    
    
    
    
    def find_history(auto_save=None ):
        # Note finaical history is a pandas dataframe
        stocks_of_interest = None
        failed_symbols = []
        financial_history = {}
        pairs_list = []
        if not os.path.isfile('co_integration_stock_history.pkl'):
    
            start_date = pd.Timestamp(dt.strptime("2005-06-16", '%Y-%m-%d'))
            end_date = pd.Timestamp(dt.strptime("2019-06-01", '%Y-%m-%d'))
    
            with open('stocks_of_interest.pkl', 'rb') as handle: 
                print('loading_from_file')
                stocks_of_interest = pickle.load(handle)
    
    
            for stock in stocks_of_interest:
                print('Getting ', stock)
                try:
                    financial_history[stock] = pdr.get_data_yahoo(stock,
                                                                      start_date,
                                                                      end_date)
    
                    print('Loaded ', len(financial_history[stock]), ' histories. For company ',stock,'.')
                except (ValueError) as e:
                    print('ERROR: Failed to get history for', stock)  
                    failed_symbols.append(stock)
    
                    time.sleep(1)
    
    
            for i, l in enumerate(failed_symbols):
                if i == 0:
                    print('Failed to load: ')
                print('\t',l)
    
            with open('co_integration_stock_history.pkl', 'wb') as handle:
                pickle.dump(financial_history , handle, protocol=pickle.HIGHEST_PROTOCOL)
    
    
        else:
            with open('co_integration_stock_history.pkl', 'rb') as handle: 
                print('loading_from_file')
                financial_history = pickle.load(handle)
    
            with open('stocks_of_interest.pkl', 'rb') as handle: 
                print('loading_from_file')
                stocks_of_interest = pickle.load(handle)   
    
            with open('stock_pairs_of_interest.pkl', 'rb') as handle: 
                print('loading_from_file')
                pairs_list = pickle.load(handle)   
                print(pairs_list)
    
    
        return stocks_of_interest,financial_history, pairs_list
    
    
    if __name__ == '__main__':
        stocks_of_interest,financial_history, pairs_list = find_history(auto_save=None)
        runstrategy(stocks_of_interest,financial_history, pairs_list)
    '''


  • @east1992 said in AttributeError: 'str' object has no attribute '_name' - The error is clear - but for the life of me I can't see why it's happening:

    I can attach the code if that would be helpful

    Everybody here can read other people minds, sure no script is needed.



  • @ab_trader As you like


  • administrators

    @ab_trader said in AttributeError: 'str' object has no attribute '_name' - The error is clear - but for the life of me I can't see why it's happening:

    I can attach the code if that would be helpful

    Everybody here can read other people minds, sure no script is needed.

    @east1992 said in AttributeError: 'str' object has no attribute '_name' - The error is clear - but for the life of me I can't see why it's happening:

    @ab_trader As you like

    I think @ab_trader was just trying a prank ...

    In any case ... let me wonder how you write such a script, yet miss the top of the forum

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

    Without the three backticks your code is basically rendered in a form which cannot be properly read.

    @east1992 said in AttributeError: 'str' object has no attribute '_name' - The error is clear - but for the life of me I can't see why it's happening:

      File "C:\ProgramData\Anaconda3\envs\finance\lib\site-packages\backtrader\broker.py", line 80, in getcommissioninfo
        if data._name in self.comminfo:
    AttributeError: 'str' object has no attribute '_name'
    

    The problem is clear: you have created a position for something which is a string and not a data feed. Hence data (which is a str) has no attribute _name

    From what one can read, it is unclear why you need to define a `get_data_by_name', given that a method to retrieve data feeds by name does already exist

    But playing with the names is for sure the root of the problem.



  • Thank you for taking the time to answer my question; unfortunately the proposed answer is incorrect. This makes sense if you consider that a custom getter function devised in the manner above shouldn't functionally alter the rest of the code. Specfically I've implimented the suggested change from:

                       p0_data = get_data_by_name(pair[0])
                       p1_data = get_data_by_name(pair[1])
    

    which uses my custom getter function and changed this to

               p0_data = self.getdatabyname(pair[0])
               p1_data = self.getdatabyname(pair[1])
    

    This alteration resulted in an identical error, so indeed is unlikely to have functionally altered anything.