lines.sma30 and lines.sma50 cause problem

25 Views Asked by At
import backtrader as bt
import yfinance as yf

# Download data from yfinance
data = yf.download("AAPL", period="max")
# Convert the data to a Pandas DataFrame
data = pd.DataFrame(data)
# Convert the DataFrame to a PandasData object
data = bt.feeds.PandasData(dataname=data)

# Instantiate Cerebro with the downloaded data
cerebro = bt.Cerebro()
cerebro.adddata(data)

# Add the SMA indicators to Cerebro
cerebro.addindicator(bt.indicators.SimpleMovingAverage, period=30, plotname="sma30")


cerebro.addindicator(bt.indicators.SimpleMovingAverage, period=50, plotname="sma50")


# Create the strategy
class MyStrategy(bt.Strategy):
    def start(self):
        # Access the indicator values using the `lines` attribute

        if self.lines["sma30"][0] > self.lines["sma50"][0]:

            # Buy if not already in a position

            if not self.position:
                self.buy()
        else:
            # Sell if in a position
            if self.position:
                self.sell()


# Add the strategy to Cerebro
cerebro.addstrategy(MyStrategy)

# Run the backtest
cerebro.run()

TypeError: list indices must be integers or slices, not str

I tried dropping the plot labels from the indicators but the lines.sma30 and lines.sma50 are always trouble. I fear backtrader doesn't have a good tutorial online as YahooFinanceCSVData doesn't work actually, so I used yfinance.

1

There are 1 best solutions below

0
robert bach On
  1. The method YahooFinanceCSVData for download data from Yahoo Finance! is depreciated. You can download the data and save it to a csv file and use the method to load the data to cerebro.
  2. The indicator should be declared within the strategy. Basically your strategy() method is the main logic that contain everything related to your trade. Example:
class MyStrategy(bt.Strategy):
    def __init__(self):
        self.dataclose = self.datas[0].close
        # Define indicators in the __init__ funcition
        self.sma30 = bt.indicators.SimpleMovingAverage(period = 30)
        self.sma50 = bt.indicators.SimpleMovingAverage(period = 50)

    def next(self):
        # Return the current value and decide what you want to do in the next bar
        if self.sma30[0] > self.sma50[0]:

            # Buy if not already in a position
            if not self.position:
                self.buy()
        else:
            # Sell if in a position
            if self.position:
            # After buy, you should use `close()` to close your position
                self.close()

This will help you to access the indicator data.

Here is my example strategy in full form:

class SimpleRSI(bt.Strategy):
    params = (
        ('printlog', True),
        ('period', 14),
        ('upperband', 60.0),
        ('lowerband', 40.0),
    )

    def __init__(self):
        self.dataclose = self.datas[0].close
        # To keep track of pending orders and buy price/commission
        self.order = None
        self.buyprice = None
        self.buycomm = None
        # Add a RSI indicator
        self.rsi = btind.RelativeStrengthIndex(period=self.params.period, upperband=self.params.upperband, lowerband=self.params.lowerband)

    def log(self, txt, dt=None, doprint=False):
        if self.params.printlog or doprint:
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))

    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            return

        # Check if an order has been completed
        # Attention: broker could reject order if not enough cash
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))

                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
            else:  # Sell
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))

            self.bar_executed = len(self)

        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')

        self.order = None

    def next(self):
        # Simply log the closing price of the series from the reference
        self.log('Close, %.2f' % self.dataclose[0])

        # Check for open orders
        if self.order:
            return

        # Check if we are not in the market
        if not self.position:
            # Not yet ... we MIGHT BUY if ...
            if self.rsi[0] > 60:
                # BUY, BUY, BUY!!! (with all possible default parameters)
                self.log('BUY CREATE, %.2f' % self.dataclose[0])
                # Keep track of the created order to avoid a 2nd order
                size = self.broker.getvalue() / self.dataclose[0]
                self.order = self.buy(size = size)
                self.stoploss = self.dataclose[0]*0.99
                self.takeprofit = self.dataclose[0]*1.03
        else:
            if self.dataclose[0] < self.stoploss:
                self.log('STOP LOSS, %.2f' % self.dataclose[0])
                self.order = self.sell(size=self.position.size)
            elif self.dataclose[0] > self.takeprofit:
                self.log('TAKE PROFIT, %.2f' % self.dataclose[0])
                self.order = self.sell(size=self.position.size)

Feel free to use this as a blueprint to write your strategies.