Simple channel indicator
-
Hi
I have tried to write a simple channel indicator. The indicator has 2 lines. The maximum of the last 20 Highs and the minimum of the last 20 lows.
Here is my attempt.class channel20(bt.Indicator): lines = ('maxi','mini',) params = (('period', 20),) def __init__(self): self.l.maxi= math.max(self.data.high(p.period)) self.l.mini= math.min(self.data.low(p.period))
putting it into a simple strategy to see if it works, I tried this:
from datetime import datetime import backtrader as bt class channel20(bt.Indicator): lines = ('maxi','mini',) params = (('period', 20),) def __init__(self): self.l.maxi= math.max(self.data.high(p.period)) self.l.mini= math.min(self.data.low(p.period)) class test(bt.SignalStrategy): def __init__(self): channel = bt.indicator.channel20 cerebro = bt.Cerebro() cerebro.addstrategy(test) data0 = bt.feeds.YahooFinanceData(dataname='YHOO', fromdate=datetime(2011, 1, 1), todate=datetime(2012, 12, 31)) cerebro.adddata(data0) cerebro.run() cerebro.plot()
to get the following error.
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-22-d93cd4afba9e> in <module>() 23 cerebro.adddata(data0) 24 ---> 25 cerebro.run() 26 cerebro.plot() /home/rory/anaconda2/envs/backtrader/lib/python2.7/site-packages/backtrader/cerebro.pyc in run(self, **kwargs) 1068 # let's skip process "spawning" 1069 for iterstrat in iterstrats: -> 1070 runstrat = self.runstrategies(iterstrat) 1071 self.runstrats.append(runstrat) 1072 else: /home/rory/anaconda2/envs/backtrader/lib/python2.7/site-packages/backtrader/cerebro.pyc in runstrategies(self, iterstrat, predata) 1144 data._start() 1145 if self._dopreload: -> 1146 data.preload() 1147 1148 for stratcls, sargs, skwargs in iterstrat: /home/rory/anaconda2/envs/backtrader/lib/python2.7/site-packages/backtrader/feed.pyc in preload(self) 687 688 # preloaded - no need to keep the object around - breaks multip in 3.x --> 689 self.f.close() 690 self.f = None 691 AttributeError: 'NoneType' object has no attribute 'close'
I was hoping to simply get a printout of the indicator.
I wonder if anyone could shed some light on what I have done wrong.
ThanksRM
-
Hi @rorymack
I ran your script and got the same error. Taking out your indicator from the strategy
__init__
still returned the same error.After a bit of tinkering I found a few issues:
- The data feed is causing the first error you are seeing
AttributeError: 'NoneType' object has no attribute 'close'
I replaced your data feed with my own and got passed this.
-
You do not need to load your indicator with
bt.indicator.channel20
as it is in your script. You can initialize it withself.channel = channel20()
-
You have not imported the
math
module. However if you do, you will get the following error
module 'math' has no attribute 'max'
-
If I understand you correctly, you want the indicator to display the highest high of the last 20 candles and the lowest low of the last 20 candles. This is no compatible with a signal strategy. For signal strategies, the lines need to alternate between 1 and -1 values for long and short signals.
-
you are missing a few
self
declarations.
Some other thoughts and a solution:
- You probably want to add a
self.addminperiod(self.p.period)
line to your indicators__init__
so that it doesn't try and do anything until it has 20 candles worth of data.
Here is a working version of what I think you are looking for. Replace the data with your own. (Or investigate why the Yahoo call isn't working. I don't use Yahoo data so have not looked into it)
from datetime import datetime import backtrader as bt class channel20(bt.Indicator): lines = ('maxi','mini',) params = (('period', 20),) def __init__(self): self.addminperiod(self.p.period) def next(self): highs = self.data.high.get(size=self.p.period) lows = self.data.low.get(size=self.p.period) self.lines.maxi[0] = max(highs) self.lines.mini[0] = min(lows) class test(bt.Strategy): def __init__(self): self.channel = channel20() cerebro = bt.Cerebro() cerebro.addstrategy(test) fromdate = datetime(2012,1,1) todate = datetime(2012,1,5) datapath = '../data/csv/commodities/XAUUSD/XAUUSD_m1_Ask_2012-2016.csv' data0 = bt.feeds.GenericCSVData( timeframe=bt.TimeFrame.Minutes, compression=1, dataname=datapath, nullvalue=0.0, dtformat=('%m/%d/%Y'), tmformat=('%H:%M:%S'), fromdate=fromdate, todate=todate, datetime=0, time=1, high=3, low=4, open=2, close=5, volume=6, openinterest=-1 #-1 means not used ) cerebro.adddata(data0) cerebro.run() cerebro.plot()
This resulted in the following
Hope this helps!
-
Thank you for your reply. I am blundering my way around here and appreciate you taking time to help me out.
- Interesting that is was the data feed, I hacked this attempt from the SMACrossover example.
Looking at the DataFeeds reference https://www.backtrader.com/docu/dataautoref.html
It appears that it has all the lines needed.
Is it generally better to create csv's of the data ?
I notice quickstart tutorial does that.
I ran it again with your modifications and the yahoo data feed and it worked! Not sure if it is a version thing.
- AhHa ! Thank you my python fu is not what it should be...
- Ok, again poor Python skills on my side.
- Not sure if I follow you here, I had in mind that I would set up the indicator and then set up the signal in
def next(self):
with this logic :
If previous period channel max is < current period high buy
and
If previous period channel low is > current period low sell
Which would generate the long and short signals.I had it in my head that the indicator is separate from the signal, Is that conceptually correct?
- Again thanks bad python on my part.
The solution
- Yes I do !
Thank you for the solution, I will play around some more with it to get to grips with the platform.
As an aside, how do you get the indicator to plot as an overlay on the data? As opposed to in a separate plot.Thanks again.
RM - Interesting that is was the data feed, I hacked this attempt from the SMACrossover example.
-
@rorymack said in Simple channel indicator:
def __init__(self): self.l.maxi= math.max(self.data.high(p.period)) self.l.mini= math.min(self.data.low(p.period))
Unfortunately
math.max
(actuallymax
) doesn't generate a lines lazily evaluated object andself.data.high(period)
gives you a delayed lines lazily evaluated object, but not an array, which is the expectation ofmax
.The platform includes
Highest
(akaMaxN
) andLowest
(akaMinN
) indicators for that.self.lines.maxi = bt.ind.MaxN(self.data, period=self.p.period)
This is also wrong:
channel = bt.indicator.channel20
because there is no instantiation. You are simply assigning a class (and not an instance of it) to
channel
-
@rorymack said in Simple channel indicator:
As an aside, how do you get the indicator to plot as an overlay on the data? As opposed to in a separate plot.
If you want the indicator to appear on the price chart. (I agree, this indicator is a good one to appear with price). Then add
subplot=False
when initializing the indicator. This is a Backtrader keyword argument for Indicators.self.channel = channel20(subplot=False)
It will result in something that looks like this:
I ran it again with your modifications and the yahoo data feed and it worked! Not sure if it is a version thing.
It also could have been a Yahoo server issue... It was the weekend.
with this logic :
If previous period channel max is < current period high buy
and
If previous period channel low is > current period low sell
Which would generate the long and short signals.Ok - If I follow this correctly, the script I gave you would need a tweak because the current candles high / low would never be above the min/max for the period as the current candle is included in the calculation. You can see this in the above example image.
To do this you can just pop the most recent result out of the list and you can check if the current candle is above or below the period max/min. Note that the period you are now comparing against is 19 candles instead of 20.
-
@ThatBlokeDave said in Simple channel indicator:
Then add subplot=False when initializing the indicator
Or else add
plotinfo = dict(subplot=False)
to the indicator definition
-
Vote to add indicator into main package! It's great for
Forex
market. -
Main package already has
Highest
andLowest
indicators.
docs - indicators ref