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?
-
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)
-
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 fromStrategy
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 usingABC
module:According to some recepies in
Python
to defineAbstract
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
-
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?
-
Don't apply abstract classes.
-
Hopefully solved from the
python
side, notbacktrader
library. See answer in SO about one more stub class.
Overall abstract classes makes me a little bit of pain inpython
. Maybe sometimes I'll master metaclasses :)
Thanks!