MultiData Indicator implementation help.

Hi there,
As I've slowly been piecing together my bt setup, 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):
 calculate the change each day
 compute an average of this change over 'x' periods
 compute a standard deviation of this change of 'x' periods
 compute the square root of 'x' periods
 calculate (#4*(#2/#3))
Optional: 6) calculate SMA of #5  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[i1]) 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[i1]) 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 (tx) 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[i1]) 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:

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.

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?


@mangoloco said in MultiData 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.
 Articles  Momentum Strategy  Next and PreNext

@backtrader said in MultiData Indicator implementation help.:
Before I read the messages. See please the article below and why that isn't a good idea.
 Articles  Momentum Strategy  Next and PreNext
Will do, thank you!
If you could respond to the questions as well, that would be amazing! :)

@mangoloco said in MultiData Indicator implementation help.:
 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 asnp
) 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.@mangoloco said in MultiData Indicator implementation help.:
 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
20period
moving average you need20
data points to calculate it. Full stop. If you want your indicators at YOUR DEFINEDt=0
to be available, work with a data set which has an earlier start datetime.