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

Noob questions on RSI trading strategies from multiple datafeeds and close orders



  • Hi I am a noob on Python and I just started learning about backtrader, I would appreciate some help greatly if someone can take a look at my code and give me some advice.

    I want to backtest a very simple strategy, that is, if there is no open position, then place buy orders when both RSIm30 and RSIm60 are below 30, place sell orders when they are both above 70; while there are open positions, close the long positions when RSIm30 and RSIm60 are above 70, close the short positions when they are below 30.

    I want to realize this simple logic in backtrader so that I can backtest more complicated ones in future, however I am having problems with it now. Below is my code:

    # -*- coding: utf-8 -*-
    """
    Created on Tue Aug 28 21:06:23 2018
    
    @author: lenovo
    """
    
    import backtrader as bt
    import backtrader.feeds as btfeeds
    import os
    import datetime
    import sys  # To find out the script name (in argv[0])
    
    class TestStrategy(bt.Strategy):
    
        def log(self, txt, dt=None):
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))
    
        def __init__(self):
            self.dataclose = self.datas[0].close
            self.order = None
            self.buyprice = None
            self.buycomm = None
    
            self.rsim30=bt.indicators.RSI(self.data.close, period=14)
            self.rsim60=bt.indicators.RSI(self.data1.close, period=14)
    
        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
    
            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:
                    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 notify_trade(self, trade):
            if not trade.isclosed:
                return
    
            self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                     (trade.pnl, trade.pnlcomm))
    
        def next(self): # input trade logid in this function
            self.log('Close, %.2f' % self.dataclose[0])
    
            if self.order:
                return
    
            if not self.position:
    
                if self.rsim30<30 and self.rsim60<30:
    
                    self.log('BUY CREATE, %.2f' % self.dataclose[0])
                    self.order = self.buy()
    
                elif self.rsim30>70 and self.rsim60>70:
                    self.log('SELL CREATE, %.2f' % self.dataclose[0])
                    self.order = self.sell()
            
            if self.position:
                if self.rsim30>70 and self.rsim60>70:
                    self.p.exectype == 'Close'
                    self.buy(exectype=bt.Order.Close)
                    self.log('BUY CREATE, exectype Close, price %.2f' %
                             self.data.close[0])
                if self.rsim30<30 and self.rsim60<30:
                    self.p.exectype == 'Close'
                    self.sell(exectype=bt.Order.Close)
    
                    self.log('SELL CREATE, exectype Close, price %.2f' %
                             self.data.close[0])
    
    
    
    if __name__ == '__main__':
        cerebro = bt.Cerebro()
        cerebro.addstrategy(TestStrategy)
        modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
        datapath = os.path.join(modpath, 'EURUSD30.csv')
        datapath1 = os.path.join(modpath, 'EURUSD60.csv')
        # Create a Data Feed
        data = btfeeds.GenericCSVData(
            dataname=datapath,
            fromdate=datetime.datetime(2018, 1, 1),
            dtformat=('%Y.%m.%d'),
            tmformat=('%H:%M'),
            datetime=0,
            time=1,
            high=3,
            low=4,
            open=2,
            close=5,
            volume=6,
            timeframe= bt.TimeFrame.Minutes,
            compression= 30
        )
        data1 = btfeeds.GenericCSVData(
            dataname=datapath1,
            fromdate=datetime.datetime(2018, 1, 1),
            dtformat=('%Y.%m.%d'),
            tmformat=('%H:%M'),
            datetime=0,
            time=1,
            high=3,
            low=4,
            open=2,
            close=5,
            volume=6,
            timeframe= bt.TimeFrame.Minutes,
            compression= 30
        )
    
        cerebro.adddata(data)
    
        cerebro.broker.setcash(10000.0)
    
        cerebro.addsizer(bt.sizers.FixedSize, stake=0.05)
    
        cerebro.broker.setcommission(commission=0.00)
    
        print('Starting Balance: %.2f' % cerebro.broker.getvalue())
    
        cerebro.run()
    
        print('Final Balance: %.2f' % cerebro.broker.getvalue())
    
        cerebro.plot()
    

    When I run it, needless to say it went terribly wrong, here is the error message I got :

    runfile('C:/Users/lenovo/Desktop/python_work/backtest1.py', wdir='C:/Users/lenovo/Desktop/python_work')
    Starting Balance: 10000.00
    Traceback (most recent call last):
    
      File "<ipython-input-3-140e68c3c26b>", line 1, in <module>
        runfile('C:/Users/lenovo/Desktop/python_work/backtest1.py', wdir='C:/Users/lenovo/Desktop/python_work')
    
      File "D:\Anaconda3\lib\site-packages\spyder\utils\site\sitecustomize.py", line 705, in runfile
        execfile(filename, namespace)
    
      File "D:\Anaconda3\lib\site-packages\spyder\utils\site\sitecustomize.py", line 102, in execfile
        exec(compile(f.read(), filename, 'exec'), namespace)
    
      File "C:/Users/lenovo/Desktop/python_work/backtest1.py", line 144, in <module>
        cerebro.run()
    
      File "D:\Anaconda3\lib\site-packages\backtrader\cerebro.py", line 1127, in run
        runstrat = self.runstrategies(iterstrat)
    
      File "D:\Anaconda3\lib\site-packages\backtrader\cerebro.py", line 1217, in runstrategies
        strat = stratcls(*sargs, **skwargs)
    
      File "D:\Anaconda3\lib\site-packages\backtrader\metabase.py", line 88, in __call__
        _obj, args, kwargs = cls.doinit(_obj, *args, **kwargs)
    
      File "D:\Anaconda3\lib\site-packages\backtrader\metabase.py", line 78, in doinit
        _obj.__init__(*args, **kwargs)
    
      File "C:/Users/lenovo/Desktop/python_work/backtest1.py", line 27, in __init__
        self.rsim60=bt.indicators.RSI(self.data1.close, period=14)
    
      File "D:\Anaconda3\lib\site-packages\backtrader\lineseries.py", line 461, in __getattr__
        return getattr(self.lines, name)
    
    AttributeError: 'Lines_LineSeries_LineIterator_DataAccessor_Strateg' object has no attribute 'data1'
    

    Can someone just take a look at it and let me know how I should change it to make the backtest go through? Thanks immensely!



  • Please format code and outputs. Really hard to read.



  • Hi, sorry about that, new to this community, still trying to get the hang of it, I just edited my question and I think it looks better now.



  • You didin't add data1 into the system but try to get it's values. Need to add one more adddata statement.



  • Thanks a lot! I will try that! Meanwhile, are my RSI definitions and positions-close syntax correct in the code? I am terribly worried that there may be some other mistakes in my code, for instance, I don't think I used this line

        def __init__(self):
            self.dataclose = self.datas[0].close
    

    anywhere in the file (it comes from the template), not even in the RSI definitions, this line seems to have been used in defining the SMA in the original template, I hope I am not making a cock-up here by ignoring it.