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

Multi-Data Indicator implementation help.



  • Hi there,

    As I've slowly been piecing together my bt set-up, the time has come to try get some indicators working. I'm having some trouble understanding how the indicator ecosystem works, and just how I can achieve what I would like.

    Here are the requirements (for each asset):

    1. calculate the change each day
    2. compute an average of this change over 'x' periods
    3. compute a standard deviation of this change of 'x' periods
    4. compute the square root of 'x' periods
    5. calculate (#4*(#2/#3))
      Optional: 6) calculate SMA of #5
    6. Plot #5 (and #6) (I know that I don't have a plotlines() just yet, trying to get it at least working before dealing with that)

    Here is the current implementation of my code, note, it does not work. I'm a fairly new programmer, so perhaps something glaringly obvious is wrong. My main suspect is that it may have something to do with the fact I am doing computations for each asset in self.datas (in next()), but only having one lines output? Also, I realise I'm doing 'nothing' with the dict() entries, I was trying that as a way to handle the problem, but it yielded the same error as the current iteration.

    import backtrader as bt
    import numpy as np
    
    
    class MQN(bt.Indicator):
        lines = ('mqn', 'ma')
    
        params = (
            ('len', 35),
            ('ma_len', 20),
        )
    
        def __init__(self):
            self.changes = dict()
            self.avg_chng = dict()
    
        def next(self):
            for i, d in enumerate(self.datas):
                cur = d.close[0]
                prev = d.close[-1]
    
                change = cur - prev
                self.changes[d] = change
    
                average = bt.indicators.MovingAverageSimple(change, period=self.p.len)
                self.avg_chng[d] = average
    
                stdev = bt.indicators.StandardDeviation(change, period=self.p.len)
                sqroot = np.sqrt(self.p.len)
    
                mqn = sqroot * (average/stdev)
                ma = bt.indicators.MovingAverageSimple(mqn, period=self.p.ma_len)
    
                self.lines.mqn[0] = mqn
                self.lines.ma[0] = ma
    

    If someone could help me understand what can be done in the bt.indicator ecosystem (as I have other indicators that do similar things to this), and help me to achieve the indicator outcome, that would be simply amazing!

    Much appreciated in advance for any help/answers!



  • I've also now tried this approach, still hanging an error.

    import backtrader as bt
    
    
    class MQN2(bt.Indicator):
        lines = ('mqn', 'ma')
    
        params = (
            ('len', 35),
            ('ma_len', 20),
        )
    
        def __init__(self):
            self.cur = self.data.close[0]
            self.prev = self.data.close[-1]
            self.change = self.cur - self.prev
            self.avg_chng = bt.indicators.MovingAverageSimple(self.change, period=self.p.len)
            self.stdev = bt.indicators.StandardDeviation(self.change, period=self.p.len)
            self.sqroot = self.p.len ** 0.5
    
        def next(self):
    
            mqn = self.sqroot[0] * (self.avg_chng[0]/self.stdev[0])
            # ma = bt.indicators.MovingAverageSimple(mqn, period=self.p.ma_len)
    
            self.lines.mqn[0] = mqn
            # self.lines.ma[0] = ma


  • Yet another iteration. Getting closer I feel, but this one hangs on:

    self.l.mqn[0] = mqn
    

    with:

    TypeError: must be real number, not LinesOperation
    
    import backtrader as bt
    import numpy as np
    
    class MQN3(bt.Indicator):
        lines = ('mqn',)
    
        params = (
            ('len', 35),
            ('ma_len', 20),
        )
    
        def __init__(self):
            self.addminperiod(self.p.len)
    
        def next(self):
            d = self.data.get(size=self.p.len)
    
            print(len(d))
            print(d)
    
            avg_chng = bt.ind.SMA(d, period=self.p.len)
            stdev = bt.ind.StdDev(d, period=self.p.len)
            sqroot = (self.p.len ** 0.5)
    
            print("{} {} {}".format(avg_chng, stdev, sqroot))
    
            mqn = (sqroot * (avg_chng/stdev))
            print(mqn)
            # ma = bt.indicators.MovingAverageSimple(mqn, period=self.p.ma_len)
    
            self.l.mqn[0] = mqn
            # self.lines.ma[0] = ma
    

    'd' is collecting the most recent 35 closes, as it should.
    'sqroot' is giving the correct multiplier.
    With the print string, I can't see the values for 'avg_chng' and 'stdev', just the objects themselves. So potentially this is where i'm getting something wrong?

    I'm also unsure of how to turn 'd' into a list of 'changes' (d[i] - d[i-1]) and pass this to SMA and StdDev.

    Can anyone help me understand where i'm going wrong? :)



  • Managed to get something running!

    import backtrader as bt
    import numpy as np
    
    class MQN4(bt.Indicator):
        lines = ('mqn', 'ma',)
    
        params = (
            ('len', 35),
            ('ma_len', 20),
        )
    
        def __init__(self):
            self.addminperiod(self.p.len+1)
    
        def next(self):
            d = self.data.get(size=self.p.len+1)
    
            changes = []
    
            for i in range(1, len(d)):
                changes.append(d[-i] - d[-i-1])
    
            avg_chng = sum(changes) / self.p.len
            stdev = np.std(changes)
            sqroot = (self.p.len ** 0.5)
    
            mqn = sqroot * (avg_chng/stdev)
    
            self.l.mqn[0] = mqn
    
            # ma = bt.ind.SMA(self.l.mqn, period=self.p.ma_len)
            # self.l.ma[0] = ma
    

    This gives me what I would like! For one asset. How could I modify (or utilise!) 'lines' so that I can have a separate output on each asset? i.e. N separately computed indicators for N assets.

    Furthermore, is there any way to remove the "gap" at the beginning, i.e. remove the buffering of the indicator? Is it possible to feed through (t-x) data but begin the backtest at (t)? Or any other way?



  • Sorry for the relentless stream of replies, haha. I have solved the problem, with multiple data (assets).

    import backtrader as bt
    import numpy as np
    
    
    class MQN(bt.Indicator):
        lines = ('mqn', 'ma',)
    
        params = (
            ('len', 35),
            ('ma_len', 20),
        )
    
        def __init__(self):
            self.addminperiod(self.p.len+1)
    
        def next(self):
            for i, d in enumerate(self.datas):
    
                data = d.get(size=self.p.len+1)
                changes = []
    
                for i in range(1, len(data)):
                    changes.append(data[-i] - data[-i-1])
    
                if len(changes) == self.p.len:
                    avg_chng = np.average(changes)
                    stdev = np.std(changes)
                    sqroot = np.sqrt(self.p.len)
    
                    mqn = sqroot * (avg_chng/stdev)
    
                    self.l.mqn[0] = mqn
    
                    ma = self.l.mqn.get(size=self.p.ma_len)
                    self.l.ma[0] = np.average(ma)
    

    Calling this in my strategy, and passing in the specified data (asset) does the trick and plots each separate indicator, with its respective SMA.

    I hope my documentation of progress here has/will be useful to some.

    My final questions are:

    1. Is the above code the most efficient/cleanest/best way to go about handling the operations I'm doing? As mentioned earlier, I have several other indicators, similar in nature to this one, so I would like to make sure I really nail the right way before making my way through programming all of them.

    2. Is there a way/hack to eliminate the "instantiation lag" that occurs with indicators in BT? Is it possible to pass in data from before the start of the backtest so that any indicators are already fully instantiated at t=0?


  • administrators

    @mango-loco said in Multi-Data Indicator implementation help.:

    def __init__(self):
        self.addminperiod(self.p.len+1)
    

    Before I read the messages. See please the article below and why that isn't a good idea.



  • @backtrader said in Multi-Data Indicator implementation help.:

    Before I read the messages. See please the article below and why that isn't a good idea.

    Will do, thank you!

    If you could respond to the questions as well, that would be amazing! :)


  • administrators

    @mango-loco said in Multi-Data Indicator implementation help.:

    1. Is the above code the most efficient/cleanest/best way to go about handling the operations I'm doing?

    The fact is that backtrader allows you to do it in a declarative way or in an iterative way (and to mix both). There seems to a fixation out thee in many cases in using numpy (imported as np) for whatever reason which I fail to understand, even if it is meant to only calculate a single value. But if it suits you, there is no reason to do it otherwise.

    @mango-loco said in Multi-Data Indicator implementation help.:

    1. Is there a way/hack to eliminate the "instantiation lag" that occurs with indicators in BT? Is it possible to pass in data from before the start of the backtest so that any indicators are already fully instantiated at t=0?

    There is NO lag. Your reasoning and understanding is wrong. If you want a 20-period moving average you need 20 data points to calculate it. Full stop. If you want your indicators at YOUR DEFINED t=0 to be available, work with a data set which has an earlier start datetime.


Log in to reply
 

});