everyone!
I´m trying to replicate one of the strategies with backtrader for practice purposes, but I get this message when I try to run it: 'Lines_LineSeries_LineIterator_DataAccessor_Strateg' object has no attribute 'self'.
I want to buy when RSI is above 30 and SMA(14) crosses over SMA (50) upwards and sell when RSI is less than 70 and SMA(14) crosses over SMA (50) downwards.
Could you please help me to find where´s the error and how do I fix it? Thank you very much!
Please see below my code:
´´´
class RSI_SMA_Strategy(bt.Strategy):
params = dict(rsi_periods=21, rsi_upper=70,
rsi_lower=30, rsi_mid=50,
sma_periods=14, sma_periods2=50)
def __init__(self):
# keep track of close price in the series
self.data_close = self.datas[0].close
self.data_open = self.datas[0].open
# keep track of pending orders/buy price/buy commission
self.order = None
self.price = None
self.comm = None
# initializing rsi and sma
self.rsi = bt.indicators.RSI(self.datas[0], period=self.p.rsi_periods)
self.sma14=bt.ind.SMA(self.datas[0], period=self.params.sma_periods)
self.sma50=bt.ind.SMA(self.datas[0], period=self.params.sma_periods2)
self.rsi_signal_long_buy = bt.ind.CrossUp(self.rsi, self.p.rsi_lower)
self.rsi_signal_long_exit = bt.ind.CrossUp(self.rsi, self.p.rsi_mid)
self.rsi_signal_short = bt.ind.CrossDown(self.rsi, self.p.rsi_upper)
def log(self, txt):
'''Logging function'''
dt = self.datas[0].datetime.date(0).isoformat()
print(f'{dt}, {txt}')
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# order already submitted/accepted - no action required
return
# report executed order
if order.status in [order.Completed]:
if order.isbuy():
self.log(
f'BUY EXECUTED --- Price: {order.executed.price:.2f}, Cost: {order.executed.value:.2f}, Commission: {order.executed.comm:.2f}'
)
self.price = order.executed.price
self.comm = order.executed.comm
else:
self.log(
f'SELL EXECUTED --- Price: {order.executed.price:.2f}, Cost: {order.executed.value:.2f}, Commission: {order.executed.comm:.2f}'
)
# report failed order
elif order.status in [order.Canceled, order.Margin,
order.Rejected]:
self.log('Order Failed')
# set no pending order
self.order = None
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log(f'OPERATION RESULT --- Gross: {trade.pnl:.2f}, Net: {trade.pnlcomm:.2f}')
def next_open(self):
if not self.position:
if self.rsi > 30 and self.self.sma14 > self.self.sma50:
# calculate the max number of shares ('all-in')
size = int(self.broker.getcash() / self.datas[0].open)
# buy condition
self.log(f'BUY CREATED --- Size: {size}, Cash: {self.broker.getcash():.2f}, Open: {self.data_open[0]}, Close: {self.data_close[0]}')
self.buy(size=size)
else:
if self.rsi < 70 and self.self.sma14 < self.self.sma50:
# sell order
self.log(f'SELL CREATED --- Size: {self.position.size}')
self.sell(size=self.position.size)
data = bt.feeds.PandasData(dataname=yf.download('MSFT', '2018-01-01', '2018-12-31'))
cerebro = bt.Cerebro(stdstats = False, cheat_on_open=True)
cerebro.addstrategy(RSI_SMA_Strategy)
cerebro.adddata(data)
cerebro.broker.setcash(10000.0)
cerebro.broker.setcommission(commission=0.002)
cerebro.addobserver(bt.observers.BuySell)
cerebro.addobserver(bt.observers.Value)
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='time_return')
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
backtest_result = cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
´´´
Your issue comes from the
self.self.sma14
andself.self.sma50
lines, at the end of the class definition. They should beself.sma14
andself.sma50
instead.