Strategy inheritance



  • I made 3 simple strategies, now I want to move shared code into BaseStrategy with 3 subclasses.

    class BaseStrategy(bt.Strategy):
    	def __init__(self):
    		self.xz = "test"
    		self.data = "test"
    ...
    class RSISimple(BaseStrategy):
    	def next(self):
    		print(self.xz)
    

    Error appeared:

    Traceback (most recent call last):
      File "D:/Projects/trading-bot/main/lab/backtrader/netflix.py", line 74, in <module>
    	strategies = back_trader.run()
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\cerebro.py", line 809, in run
    	runstrat = self.runstrategies(iterstrat)
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\cerebro.py", line 926, in runstrategies
    	self._runonce(runstrats)
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\cerebro.py", line 1276, in _runonce
    	strat._oncepost(dt0)
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\strategy.py", line 269, in _oncepost
    	self.nextstart()  # only called for the 1st value
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\lineiterator.py", line 324, in nextstart
    	self.next()
      File "D:\Projects\trading-bot\main\lab\strategy\rsi_simple.py", line 54, in next
    	print(self.xz)
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\lineseries.py", line 429, in __getattr__
    	return getattr(self.lines, name)
    AttributeError: 'Lines_LineSeries_LineIterator_DataAccessor_Strateg' object has no attribute 'xz'
    

    Indeed debugger shows that there is no xz or data attributes in RSISimple instance. Even more

    print(self.__class__)
    

    shows MetaStrategy, not my BaseStrategy.

    How to inherit strategies right?


  • administrators

    Your code must be a lot more complicated than the snippet shown above which has been for sure typed manually, because inheritance for strategies is not being intercepted.

    The following code works 100% and is just like what you in theory have done above.

    class MACrossOver1(bt.Strategy):
        params = (
            # period for the fast Moving Average
            ('fast', 10),
            # period for the slow moving average
            ('slow', 30),
            # moving average to use
            ('_movav', bt.ind.MovAv.SMA)
        )
    
        def __init__(self):
            sma_fast = self.p._movav(period=self.p.fast)
            sma_slow = self.p._movav(period=self.p.slow)
    
            self.buysig = bt.ind.CrossOver(sma_fast, sma_slow)
            self.xz = 1
    
        def next(self):
            if self.position.size:
                if self.buysig < 0:
                    self.sell()
    
            elif self.buysig > 0:
                self.buy()
    
    
    class MACrossOver(MACrossOver1):
        def next(self):
            if self.xz:
                pass
    
            super(MACrossOver, self).next()
    


  • Seems like the problem appears when two init methods we have - in base and subclassed strategy:

    import backtrader as bt
    
    
    class MACrossOver1(bt.Strategy):
    	params = (
    		# period for the fast Moving Average
    		('fast', 10),
    		# period for the slow moving average
    		('slow', 30),
    		# moving average to use
    		('_movav', bt.ind.MovAv.SMA)
    	)
    
    	def __init__(self):
    		self.xz = 1
    
    	def next(self):
    		if self.position.size:
    			if self.buysig < 0:
    				self.sell()
    
    		elif self.buysig > 0:
    			self.buy()
    
    
    class MACrossOver(MACrossOver1):
    
    	def __init__(self):
    		sma_fast = self.p._movav(period=self.p.fast)
    		sma_slow = self.p._movav(period=self.p.slow)
    
    		self.buysig = bt.ind.CrossOver(sma_fast, sma_slow)
    
    	def next(self):
    		if self.xz:
    			pass
    
    		super(MACrossOver, self).next()
    
    
    if __name__ == '__main__':
    	back_trader = bt.Cerebro()
    	back_trader.adddata(
    		bt.feeds.YahooFinanceData(dataname='NFLX', period='h', reverse=True,
    								  fromdate=bt.datetime.datetime(2016, 1, 1), todate=bt.datetime.datetime(2016, 12, 31)
    								  )
    	)
    	back_trader.addstrategy(MACrossOver)
    	back_trader.run()
    

    Traceback:

    Traceback (most recent call last):
      File "D:/Projects/trading-bot/main/lab/strategy/xz_test.py", line 53, in <module>
    	back_trader.run()
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\cerebro.py", line 809, in run
    	runstrat = self.runstrategies(iterstrat)
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\cerebro.py", line 928, in runstrategies
    	self._runonce(runstrats)
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\cerebro.py", line 1278, in _runonce
    	strat._oncepost(dt0)
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\strategy.py", line 269, in _oncepost
    	self.nextstart()  # only called for the 1st value
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\lineiterator.py", line 324, in nextstart
    	self.next()
      File "D:/Projects/trading-bot/main/lab/strategy/xz_test.py", line 39, in next
    	if self.xz:
      File "C:\Users\Home\AppData\Local\Programs\Python\Python35-32\lib\site-packages\backtrader\lineseries.py", line 429, in __getattr__
    	return getattr(self.lines, name)
    AttributeError: 'Lines_LineSeries_LineIterator_DataAccessor_Strateg' object has no attribute 'xz'


  • Sorry, I forgot to init base class:

    ...
    class MACrossOver(MACrossOver1):

    	def __init__(self):
    		MACrossOver1.__init__(self)
    		sma_fast = self.p._movav(period=self.p.fast)
    		sma_slow = self.p._movav(period=self.p.slow)
    
    		self.buysig = bt.ind.CrossOver(sma_fast, sma_slow)
    

    ...

    Same for the first example:

    BaseStrategy.__init__(self)

  • administrators

    It's good to know there is no problem.

    The base hierarchy in backtrader is designed to avoid having to call __init__ from the subclasses. That means that subclassing from Strategy doesn't need to call any base class __init__.

    Background processing is done by means of controlling the __call__ method of metaclasses.

    You may want to have a look at this python package:

    If your own hierarchy defines __init__ there is no workaround to avoid calling it from subclasses.

    Note: there actually is by scanning the classes and automagically invoking the methods without user intervention, but that would also require user cooperation, because the user must not call it. And it breaks some patterns like when some work has to be done before invoking the base class __init__



  • Now I have to implement abstract strategy using ABC module:

    According to some recepies in Python to define Abstract class we should declare that it's abcmeta like this:

    class BaseStrategy(metaclass=ABCMeta):
    

    But what if this class is already subclassed from other class?

    class BaseStrategy(bt.Strategy):
    

    Use other options (interfaces, exception in base class) instead?

    Note that I tried:

    class BaseStrategy(bt.Strategy, metaclass=ABCMeta):
    

    With such traceback:

    Traceback (most recent call last):
      File "D:/Projects/trading-bot/main/lab/backtrader/netflix.py", line 13, in <module>
    	from main.lab.strategy import RSISimple, RSIBuySell, SMACross, SMA_RSI, HolyGrail, MACD_ADX, BBands
      File "D:\Projects\trading-bot\main\lab\strategy\__init__.py", line 1, in <module>
    	from .rsi_simple import *
      File "D:\Projects\trading-bot\main\lab\strategy\rsi_simple.py", line 3, in <module>
    	from main.lab.strategy.base_strategy import BaseStrategy
      File "D:\Projects\trading-bot\main\lab\strategy\base_strategy.py", line 6, in <module>
    	class BaseStrategy(bt.Strategy, metaclass=ABCMeta):
    TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
    

    Possible that bt.Strategy uses abstract classes too.

    Asked on SO too: http://stackoverflow.com/questions/41659630/abstract-class-which-is-subclass-of-something-else/41659690#41659690


  • administrators

    backtrader doesn't use abstract classes but it relies heavily on metaclasses and metaprogramming

    You are trying to simply replace the entire metaclass hierarchy and python is complaining you cannot replace an existing metaclass hierarchy suddenly at will.



  • Roger. Any way to solve it?


  • administrators

    Don't apply abstract classes.



  • Hopefully solved from the python side, not backtrader library. See answer in SO about one more stub class.
    Overall abstract classes makes me a little bit of pain in python. Maybe sometimes I'll master metaclasses :)
    Thanks!


Log in to reply
 

Looks like your connection to Backtrader Community was lost, please wait while we try to reconnect.