How to add my own pre-calculated indicators to bt logic as bt.Indicator
Rubén Briones last edited by Rubén Briones
I just recently discovered the backtrader library, and I would like to add many of my own indicators (I've called them as AnalysisFilters) that I have already programmed to the backtrader logic. But I would like to add them as if they were bt.Indicator so that they can be easily plotted. (Later you will understand why I focus on saying that I want them to be bt.Indicator. Because I know how to add them as DataFeed (extra columns with my precalculated indicators values), but not as bt.Indicator).
The problem is that all the indicators that I have programmed follow this pattern: they have a method with the following definition:
def _calculate_indicator_at_index(data, index): """ :param data: (pd.DataFrame) with all the data and ['open', 'high', 'low', 'close'] columns. :param index: (int) index of the DataFrame for which we want to calculate the value of the indicator """
I've tried something like the following, but it obviously doesn't work because self.data is not a DataFrame.
import backtrader as bt from src.analysis_filters.blai5_atlas_filter import Blai5AtlasFilter class AtlasIndicator(bt.Indicator): lines = ('atlas',) params = (('boll_avg_value', 20), ('exp_avg_value', 120)) def __init __(self): atlas_filter = Blai5AtlasFilter(self.p.boll_avg_value, self.p.exp_avg_value) atlas_precalculated_values = *self.data.buflen() for i in range(self.data.buflen()): atlas_precalculated_values[i] = atlas_filter._calculate_indicator_at_index(self.data, i) self.lines.atlas = atlas_precalculated_values
So my question would be how to convert self.data to a DataFrame or something that my indicator can later access via data['close'], data['open'], etc.
Greetings, and thank you very much in advance for the help.
PS: Congratulations to the creator of the library, because the amount of possibilities it offers is impressive. I would have loved to discover it before.
The assignment is meant for lazy evaluation of things which haven't been calculated yet.
Your best bet is to feed that during each
nexttick in a custom indicator. Docs - Indicator Development
Rubén Briones last edited by Rubén Briones
Hello, thanks for your answer. Finally what I have done is load my indicator values as a
DataBase(passing first the same main data to my AnalsyisFilter and to Cerebro). Here is the code were I load my indicator values as a
class AnalysisFilterFakeDataLoader(bt.feeds.DataBase): lines = ('data_indicator',) def __init__(self, analysis_filter: AnalysisFilter = None): self.analysis_filter = analysis_filter self.plotinfo.plot = False def start(self): super(AnalysisFilterFakeDataLoader, self).start() # reset the iterator on each start self._rows = self.analysis_filter.data.itertuples() def _load(self): try: row = next(self._rows) except StopIteration: return False line_ind = getattr(self.lines, 'data_indicator') line_ind = self.analysis_filter._check_filter_condition_at_index(row.Index) return True
And then in my
StrategyI access this data with this lines:
class NearConfHighsBlai5Strategy(bt.Strategy): params = (('blai5_name', None), ('near_conf_highs_name', None)) def __init__(self): self.blai5_indicator = self.dnames.get(self.p.blai5_name).data_indicator self.near_indicator = self.dnames.get(self.p.near_conf_highs_name).data_indicator def next(self): if self.blai5_indicator > 0 or self.near_indicator> 0: self.buy() else: self.close()
But I don't know why this code does not works. Because, the
next()method is never executed. The assigment of the indicator values in the
__init__()is working properly, but the
next()is not executing never. Why?
EDIT: Also, when I add my own DataBase, any other strategis not related with this databases stop executing well. It seems, that when I add any data with my own implementation of bt.DataBase, the Strategies don't execute their next() method.
Sorry, but that code cannot work ... an exception should have been raised. You are obviously running something else.
class AnalysisFilterFakeDataLoader(bt.feeds.DataBase): lines = ('data_indicator')
That defines 14 lines ...
Rubén Briones last edited by
@backtrader Sorry, I have tried to simplify my example to make easy to understand. It's right that this code raise an exception because
linesis not a tuple. In my real use case I have defined 3 lines, so the code runs. But I added a comma in the declaration of
linesto convert to a tuple, and skip the exception, but I have the same problem described in my post: the strategies doesn't run.
PD: I don't understand so much what you want to say with "That defines 14 lines ..."