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
-
@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 astr
) 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
- Docs - Strategy - See
getdatabyname
But playing with the names is for sure the root of the problem.
- Docs - Strategy - See
-
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.