The code by @randyt is 100% ok. Some versions ago and to reduce the number of needed imports, the main package gives direct access to subpackages, so you may also do
import backtrader as bt
hi_bar = bt.indicators.Highest(...)
of for even less typing
import backtrader as bt
hi_bar = bt.ind.Highest(...)
The final implementation, already pushed to the development branch, respects the original implementation by Ehlers and rather than expecting the price component to have high and low it takes simply self.data (which unless changed is the close from regular data feeds)
Using it for the midpoint of the bar is easy:
midpoint = (self.data.high + self.data.low) / 2.0
lrsi = bt.ind.LRSI(midpoint)
The advantage of not having specifics like high and low in the code is that the LRSI can be calculated for anything which is a data feed or descendant of ... you could create the LRSI of a SimpleMovingAverage
In case anyone would be tempted to try it, here a pre-packaged CumulativeRSI indicator.
lines = ('cumrsi',)
params = (('period', 14), ('count', 2),)
alias = ('CumRSI',)
rsi = bt.ind.RSI(self.data, period=self.p.period)
self.lines.cumrsi = bt.indicators.SumN(rsi, period=self.p.count)
Another strategy using Super Trend and SAR
WHEN TO ENTER A TRADE:
Enter: a BUY trade when SUPERTREND is in a bullish trend(line is green), and SAR gives a BUY signal. (Don't enter a SELL trade if SUPERTREND is bullish and SAR gives a SELL signal.
Enter: a SELL trade when SUPERTREND is in a bearish trend(line is red), and SAR gives a SELL signal. (Don't enter a BUY trade if SUPERTREND is bearish and SAR gives a BUY signal.
Get out of a BUY trade when SAR give a SELL signal, and the opposite for a SELL trade.
Post some sample code (it really helps)
If the target is to write the values of indicators use the standard functionality Writer
Don't forget to do this with your indicator:
self.mysma = bt.indicators.SMA(self.data, period=30)
self.mysma.csv = True
The default behavior is to not write the value of indicators with a writer to avoid cluttering in the csv output and thus selected indicators must have the csv flag activated.
If a single data feed is added to the system with replaydata, only this data will be output. In this case the only data known to cerebro is the one with timeframe=bt.TimeFrame.Minutes and compression=15. The original is NOT in the system
Add it too with: cerebro.adddata(data)
The original behavior of backtrader enforced adding the larger timeframe data feeds after the smaller timeframe feeds. With the new sychronization mechanism available since 1.9.x., this is no longer needed. In any case the suggestions would be for this:
Add the larger timeframe (your replaydata) after the smaller timeframe (adddata)
In that case: self.data0 will be the smaller timeframe and self.data1 the larger timeframe. Use the appropriate reference when creating the indicators
Although not strictly needed use cerebro.run(next=True). This will keep the buffers fully sync'ed and allows plotting.
As pointed out by RandyT you can port the indicator easily, imho.
If it's a data feed there is direct support for loading a pandas.Dataframe. See:
https://www.backtrader.com/docu/dataautoref.html (look for PandasData)
If you have something precalculated which is not a data feed the use case is about synchronization, because you probably want to use it as an indicator. You should then check the datetime of the data feed passed to the indicator to fetch the proper data from the dataframe.
A more detailed use case would help in understanding what may (or may not) missing.
This is a non-expected usage pattern and for sure one which is not going to work.
LineBuffer is an internal object which is not meant for user consumption. And of course self.dval1 is turning into a float, you are assigning a float to the member attribute you created yourself.
This is not the same as self.lines.xxx = yyy during the __init__ phase, because in that case self.lines is an object, and xxx is constructed by means of Descriptors, which allows controlling things like assignment (via __set__). But assignment cannot be controlled on a member attribute you have created.
The use case is actually a lot easier:
self.dval1 = bt.ind.Lowest(self.data.low, period=10) # or bt.ind.MinN(self.data.low, period=10)
print('the current lowest value is:', self.dval1)
The indicator family is big and tries to cover all possible aspects