Developing a Recursive Indicator (with a seed)

One of the initial goals of backtrader was:

Being able to quickly prototype indicators to test new ideas

It doesn’t have to be a perfect indicator, but being able to quickly and easily develop them does help. To confirm the design was righ, one of the first indicators to be in the standard arsenal of backtrader was an Exponential Moving Average (aka EMA) which by definition is: recursive.

Note Trivia: as you may imagine the 1st ever indicator was a SimpleMovingAverage

Since the question of how to develop a recursive indicator has been posted in the backtrader community let’s develop a quick ExponentialMovingAverage indicators.

A recursive indicator like the

It uses the previous value to calculate the current value

You can see the mathematics for example in Wikipedia - Exponential Moving Average

If you have been brave enough to read it all, you’ll have seen that the period is used to calculate the Exponential Smoothing. We’ll use it.

To solve the conundrum for the calculation of the first value the industry settled on using a simple average of the previous period values.

As a leverage we are going to use bt.indicators.PeriodN which:

Already defines a period parameter

Informs the framework about the actual period used by the end user

See its definition at: Docs - Indicator Reference

Let’s then develop our EMA

import backtrader as bt class EMA(bt.indicators.PeriodN): params = {'period': 30} # even if defined, we can redefine the default value lines = ('ema',) # our output line def __init__(self): self.alpha = 2.0 / (1.0 + self.p.period) # period -> exp smoothing factor def nextstart(self): # calculate here the seed value self.lines.ema[0] = sum(self.data.get(size=self.p.period)) / self.p.period def next(self): ema1 = self.lines.ema[-1] # previous EMA value self.lines.ema[0] = ema1 * (1.0 - self.alpha) + self.data[0] * self.alpha

Almost easier done than said. The key being the provision of the seed value in nextstart , which

Will be called once, when the minimum warmp up period of the indicator has been met. As opposed to next which will be then called for each new data value that is delivered into the system

The default implementation of nextstart simply delegates the job to next which for most indicators (for example a Simple Moving Average) is the right thing to do. But in this case overriding and providing the seed value is the key.

Plotting it along the data

Being a moving average, it would be nice if the indicator plotted on the same axis as the data for which it calculates the average. Because we have inherited from PeriodN the default value for plotting is (see it in the docs):

subplot=True

which of course means that a subplot (another axis on the chart) will be created for our indicator. This can be easily overridden.

import backtrader as bt class EMA(bt.indicators.PeriodN): plot = dict(subplot=False)

And done. If you want to control more plotting options check Docs - Plotting

Good Luck!