Developing an Indicator
After much ado in fine tuning backtrader (give it had already been runningfor a while) I decided to not only share it via GitHub but to also tell theworld it was there and posted about its existence in "Reddit".
Following a comment about why trading/algorithmic trading platforms pop up and aprivate question about the platform supporting live trading for manysimultenaous tickers I came to the conclusion that my own child deserved its ownblog.
And here we are. But let’s focus on business.
backtrader is meant to let me experiment quickly with ideas and check whatmy eyes may have told me it may be a chance.
backtrader (right now) is all about backtesting and has not been connectedto any live trading platform and may not even be (although I do believe thetechnical implementation would allow for it)
When I use the expression "experimenting with ideas" I mean two things:
Being able to quickly draft an indicator and be able to visually evaluate its behavior
Be it the case, engage in developing potential strategy around that indicator or a combination with others
My personal trading is 100% judgemental in that no single decision is taken byan automated system. But I look at what indicators have to tell. Whether the"indications" are really a signal or not, it’s left to my faulty human mind.
But let’s experiment. Right after publishing for the 1st time to Reddit I addeda well known indicator:
- Trix
Stockcharts has a nice discussion on Trix at:ChartSchool - Trix
Let’s experiment how to do it, with the minimum possible of lines:
from __future__ import (absolute_import, division, print_function, unicode_literals)import backtrader as btimport backtrader.indicators as btindclass MyTrix(bt.Indicator): lines = ('trix',) params = (('period', 15),) def __init__(self): ema1 = btind.EMA(self.data, period=self.p.period) ema2 = btind.EMA(ema1, period=self.p.period) ema3 = btind.EMA(ema2, period=self.p.period) self.lines.trix = 100.0 * (ema3 - ema3(-1)) / ema3(-1)And the Trix indicator is up and running. Looking at this and as the author ofthe platform I do truly believe my goal of being able to quickly experiment withnew ideas with ease of use … has been reached.
A breakdown of the development:
lines = (‘trix’,)
This tuple defines the output lines (just one in this case) of theindicator. This statement at the beginning of the class declarationgenerates a lot of background action during class creation and objectinstantiation.
Suffice to say that the object has an attribute "lines" which hold the"trix".
As a bonus and if the name "trix" is not used inside the indicator itself,the "line" could also be reached with "self.trix". But for clarity I doprefer "self.lines.trix"
Additional access methods:
self.l.trix
self.lines[0] … being the index is the one corresponding to the position in the tuple
params = ((‘period’, 15),)
This tuple of tuples (can also be a dict or OrderedDict) defines theparameters the indicator accepted and declares the default values.
The burden of parsing kwargs is taken off the shoulders of the users.
Parameters can be accessed with the "self.params.xxxxx" notation or with theshorthand "self.p.xxxxx"
The calculation (where EMA stands for ExponentialMovingAverage)
ema1 = btind.EMA(self.data, period=self.p.period)
A new bonus is shown … "self.data". This seems to come out of the bluebut this is again pre-processing done in the background for theindicator.
Any "data" passed to the indicator for calculation is intercepted andplaced in a
self.datasarray, where the usualself.datas[0]could be used to reach the first data.Shorthands do exist and look like: self.data and self.data0 for thefirst data in the array. From then onwards self.data1, self.data2.
Trix only needs one data
ema2 = btind.EMA(ema1, period=self.p.period)
Not much to say. EMA uses ema1 as input data
ema3 = btind.EMA(ema2, period=self.p.period)
Even less to say
self.lines.trix = 100.0 * (ema3 - ema3(-1)) / ema3(-1)
First and foremost a simple 1-period percentage difference calculationis done.
The magic ema3(-1) is a notation to indicate: the previous value of ema.
And the result of the calculation is assigned to the output "line""trix" defined during class creation.
Easy Peasy. But "experimenting" would not be done if I didn’t get visualfeedback of what Trix is doing (even if Stockcharts has a nice essay on it).
Note
The actual Trix implemenation has some additionals bells and whistlesmostly itended to beautify plotting which are of no relevance for thispost.
We assume we have put theMyTrix indicator in a mytrix.py file.
from __future__ import (absolute_import, division, print_function, unicode_literals)import backtrader as btimport backtrader.feeds as btfeedsfrom mytrix import MyTrixclass NoStrategy(bt.Strategy): params = (('trixperiod', 15),) def __init__(self): MyTrix(self.data, period=self.p.trixperiod)if __name__ == '__main__': # Create a cerebro entity cerebro = bt.Cerebro() # Add a strategy cerebro.addstrategy(NoStrategy, trixperiod=15) # Create a Data Feed datapath = ('../datas/2006-day-001.txt') data = bt.feeds.BacktraderCSVData(dataname=datapath) # Add the Data Feed to Cerebro cerebro.adddata(data) # Run over everything cerebro.run() # Plot the result cerebro.plot()And the visual output is below (open the chart in a new window/tab for a fullsize image), hopefully showing how quickly and easy an indicator can be createdand visually evaluated withbacktrader
