For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

How to add my own pre-calculated indicators to bt logic as bt.Indicator



  • Hello,

    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 = [0]*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.


  • administrators

    The assignment is meant for lazy evaluation of things which haven't been calculated yet.

    Your best bet is to feed that during each next tick in a custom indicator. Docs - Indicator Development



  • 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 DataBase:

    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[0] = self.analysis_filter._check_filter_condition_at_index(row.Index)
            
            return True
    

    And then in my Strategy I 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] > 0 or self.near_indicator[0]> 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.


  • administrators

    Sorry, but that code cannot work ... an exception should have been raised. You are obviously running something else.

    @Rubén-Briones said in How to add my own pre-calculated indicators to bt logic as bt.Indicator:

    class AnalysisFilterFakeDataLoader(bt.feeds.DataBase):
        lines = ('data_indicator')
    

    That defines 14 lines ...



  • @backtrader Sorry, I have tried to simplify my example to make easy to understand. It's right that this code raise an exception because lines is 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 lines to 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 ..."


Log in to reply
 

});