Lookback period for custom indicator
-
Hi,
I am trying to implement a trading strategy based on this custom indicator.https://www.backtrader.com/blog/2019-05-20-momentum-strategy/momentum-strategy/
def momentum_func(the_array): r = np.log(the_array) slope, _, rvalue, _, _ = linregress(np.arange(len(r)), r) annualized = (1 + slope) ** 252 return annualized * (rvalue ** 2) class Momentum(bt.ind.OperationN): lines = ('trend',) params = dict(period=50) func = momentum_func
Params
params = dict( momentum=Momentum, momentum_period=20, volatr=bt.ind.ATR, vol_period=20, rebal_weekday=6, minimum_momentum=40 )
In the
__init__
method. I'm calculating like thisfor d in self.datas: self.inds[d]['momentum'] = self.p.momentum(d, period=20) self.inds[d]['volatility'] = self.p.volatr(d, period=20)
Notice that I'm passing the same period to both indicators.
However when I look at the calculated values. It has a value at the 20th index.# Momentum indicator array('d', [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, 137085.1866192367, 175141.41671668983, ...])
The ATR indicator on the other hand only has a value at the 21st index.
# ATR array('d', [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, 3.4824999999999817, 3.3083749999999825, ...])
Is my custom indicator suffering from lookahead bias? How can I resolve this?
On further investigation, here's the first array being passed into the
momentum_func
array('d', [4378.48, 4380.0, 4310.0, 4208.59, 4292.43, 4369.0, 4423.0, 4640.0, 4786.95, 4783.06, 4821.43, 5430.0, 5649.98, 5869.99, 5709.99, 5760.02, 5595.0, 5512.06, 5683.9, 6010.01])
However in the
next()
method, if I call get(). here's the result I get.datum.close.get(size=self.p.volatility_window) array('d', [4380.0, 4310.0, 4208.59, 4292.43, 4369.0, 4423.0, 4640.0, 4786.95, 4783.06, 4821.43, 5430.0, 5649.98, 5869.99, 5709.99, 5760.02, 5595.0, 5512.06, 5683.9, 6010.01, 6024.97])
So why is the data being shifted by 1?
-
The reason you are getting the discprency is the nature of how ATR is calculated. ATR has the following definition:
The range of a day's trading is simply [high − low]. The true range extends it to yesterday's closing price if it was outside of today's range.
Note that ATR must have the ability to look back one period. This one period look back requirement means that all ATR calculations will need n+1 periods for warmup calculation.
You can check this yourself by putting say bt.ind.SMA by itself for n periods (5 in this case):
2015-07-08, Close: 30.64, sma: 31.37, 2015-07-09, Close: 30.02, sma: 31.04,
Now add in the ATR with the same period, and you will see the results start one period later.
2015-07-09, Close: 30.02, sma: 31.04, vol: 0.64 2015-07-10, Close: 30.82, sma: 30.88, vol: 0.70
None of this has anything to do with your function, but is a matter of how ATR is calculated.