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

Make an Indicator off a Custom 'Line'



  • Hi,

    Is it possible to apply an indicator on a custom data "line"?

    Eg. have a simplemovingaverage on a custom data line (which is not OHLC data)?

    If so, whats the best approach?

    Thanks!


  • administrators

    There is no best approach. You can pass any line to indicators.



  • Thanks @backtrader , how?

    For example in the following code, where do you set the indicators name so that backtrader doesnt use the OHLC data?

     for i, d in enumerate(d for d in self.datas):
                self.sma_mth[d] = bt.indicators.SimpleMovingAverage(d, period=21)
    

    For example, if my line is called 'X'; this doesnt appear to work?

    for i, d in enumerate(d for d in self.datas):
                self.smaX[d] = bt.indicators.SimpleMovingAverage(d.lines.X, period=5)
    

    I get this error:

    AttributeError: 'Lines_LineSeries_DataSeries_OHLC_OHLCDateTime_Abst' object has no attribute 'X'
    

  • administrators

    And what is the definition of data feed d? Sorry, but some lines out of context are proof of nothing.

    Things can be analyzed if you post something complete. Else ...

    @cwse said in Make an Indicator off a Custom 'Line':

    I get this error:

    AttributeError: 'Lines_LineSeries_DataSeries_OHLC_OHLCDateTime_Abst' object has no attribute 'X'
    

    Where is the implication in that error message that OHLC is being used?



  • Hi @backtrader

    I am not trying to prove anything, I am simply asking for help. How do I set a indicator (eg bt.indicators.simpleMovingAverage) to use a specific line?

    Note on the above error it specifically says OHLC

    This section is at the start of my Strategy code in the init:

        def __init__(self):
            self.o = {}  # orders per data (main, stop, limit, manual-close)
            self.sma_mth = {}
            self.sma50 = {}
            self.sma20 = {}
            self.sma10 = {}
            self.sma5 = {}
            self.sma3 = {}
            self.sma2 = {}
            #self.bollinger = {}
            for i, d in enumerate(d for d in self.datas):
                self.sma_mth[d] = bt.indicators.SimpleMovingAverage(d, period=21) #https://cryptocurrencyhaus.com/crypto-trading-how-to-use-simple-moving-averages/
                self.sma50[d] = bt.indicators.SimpleMovingAverage(d, period=50)
                self.sma20[d] = bt.indicators.SimpleMovingAverage(d, period=20) #https://cryptocurrencyhaus.com/crypto-trading-how-to-use-simple-moving-averages/
                self.sma10[d] = bt.indicators.SimpleMovingAverage(d, period=10)
                self.sma5[d] = bt.indicators.SimpleMovingAverage(d, period=5)
                self.sma3[d] = bt.indicators.SimpleMovingAverage(d, period=3)
                self.sma2[d] = bt.indicators.SimpleMovingAverage(d, period=2)
    

    My data has been fed to backtrader and is hence referd to as self.datas.

    Any tips helpers or pointers in the right direction, on how to use custom lines here would be much appreciated!

    Thank you!!



  • I dont quite understand what you mean by using a custom data line, because you can literally use any line data as a input for your indicators. So to put this into context.
    suppose I have an indicator which depends on the SMA12 and SMA26 of a line dataset, lets call it cumulative_average.

    def __ init__ (self):
          sma12 = bt.indicators.SimpleMovingAverage(self.datas[0], period=12)
          sma26 = bt.indicators.SimpleMovingAverage(self.datas[0], period=26)
          cumulative_average = sma26 - sma12 + self.datas[0].close
          another_average = bt.indicators.SimpleMovingAverage(cumulative_average, period = 14)
    

    So basically you performed some arithmetic operation on your line data and assigned it to cumulative_average and found the SMA of the line data. Essentially you made a new line data which you can further implement in your next method.
    You can refer to the docs for more info.


  • administrators

    @backtrader said in Make an Indicator off a Custom 'Line':

    And what is the definition of data feed d? Sorry, but some lines out of context are proof of nothing.

    @backtrader said in Make an Indicator off a Custom 'Line':

    Things can be analyzed if you post something complete. Else ...
    @cwse said in Make an Indicator off a Custom 'Line':

    I get this error:

    AttributeError: 'Lines_LineSeries_DataSeries_OHLC_OHLCDateTime_Abst' object has no attribute 'X'
    


  • Hi @backtrader

    Here is my data loader, I use many custom "analysis_cols"

    class CustomBTStockLoader(btfeeds.PandasData):
        #  None : column not present
        #  -1 : autodetect position or case-wise equal name
        #  >= 0 : numeric index to the colum in the pandas dataframe
        #  string : column name (as index) in the pandas dataframe
    
    
        params = (
                     ('openinterest', None),
                     ('open', 'Open'),
                     ('high', 'High'),
                     ('low', 'Low'),
                     ('close','Close'),
                     ('volume','Volume')
                 ) \
                 + tuple((e,-1) for e in v.analysiscols)   #<--- this loads my custom data columns.
    
        if v.Custom_Alg == True:
            lines = tuple(v.analysiscols) #('Opportunity',)
            datafields = btfeeds.PandasData.datafields + v.analysiscols
        else:
            datafields = btfeeds.PandasData.datafields
    

    I feel my question is pretty straight forward though and we are going on a bit of a tangent.

    How do you specify the data which an indicator uses?

    I believe it should just be a matter of editing this single line of code (following) where the indicator is created, to somehow specify the "line" of interest. Could you please tell me how?

    self.sma_mth[d] = bt.indicators.SimpleMovingAverage(d, period=21)

  • administrators

    @cwse said in Make an Indicator off a Custom 'Line':

    I feel my question is pretty straight forward though and we are going on a bit of a tangent.

    It is not and you are the only one going off roads.

    @cwse said in Make an Indicator off a Custom 'Line':

    class CustomBTStockLoader(btfeeds.PandasData):
        #  None : column not present
        #  -1 : autodetect position or case-wise equal name
        #  >= 0 : numeric index to the colum in the pandas dataframe
        #  string : column name (as index) in the pandas dataframe
    
    
        params = (
                     ('openinterest', None),
                     ('open', 'Open'),
                     ('high', 'High'),
                     ('low', 'Low'),
                     ('close','Close'),
                     ('volume','Volume')
                 ) \
                 + tuple((e,-1) for e in v.analysiscols)   #<--- this loads my custom data columns.
    
        if v.Custom_Alg == True:
            lines = tuple(v.analysiscols) #('Opportunity',)
            datafields = btfeeds.PandasData.datafields + v.analysiscols
        else:
            datafields = btfeeds.PandasData.datafields
    

    This code contains NO custom lines (see below). Now if we look backwards, you ask:

    Make an Indicator off a Custom 'Line'
    

    When queried about information you answer

    @cwse said in Make an Indicator off a Custom 'Line':

    For example, if my line is called 'X'; this doesnt appear to work?

    for i, d in enumerate(d for d in self.datas):
                self.smaX[d] = bt.indicators.SimpleMovingAverage(d.lines.X, period=5)
    
    

    I get this error:

    AttributeError: 'Lines_LineSeries_DataSeries_OHLC_OHLCDateTime_Abst' object has no attribute 'X'
    

    Which is already an indication that you have defined NO custom lines (i.e.: X is not defined). When asked to show the definition for data d it still takes 2 extra posts until it is shown.

    And the super data d contains as expected NO custom lines. Why?

    Because for sure your v.Custom_Alg is NOT set to True.

    God knows what v and where it is defined. Show it? Of course not. It is all about being straightforward. wasn't it?

    The answer: to use custom lines you have first to define them. See Docs - Extending a Data feed

    If you want to twist things until they don't work, your straightforward problem.



  • Hi @backtrader

    I am sorry this is turning to be a difficult to answer, it must be very complex process to add a line to an indicator in Backtrader. I am passing many lines into my strategy at present and do not need help with extending data feeds, this process works just fine thank you.

    Apologies if you think I am twisting things, I just didn't want to copy my over 2000 lines of code as I think it is distracting from the question at hand. I don't think it is a good use of your or my time to turn this into a full code review. I can assure you I am passing over 20 lines via pandas dataframes with ~30columns into the custom data loader with no issues, using my analysiscols to specify the list of column names to pass as lines. These lines are used by my Backtrader strategy in many places.

    Given it appears to not be a straightforward process to pass a line to an indicator, and I am not aware of any documentation on how to specify a line for use in an indicator, I will just calculate the SMA outside of BT (in pandas) and pass it into backtrader as another line.

    I am a big fan of backtrader software and use it daily, so this workaround will do just fine.

    Thank you!


  • administrators

    @cwse said in Make an Indicator off a Custom 'Line':

    I am sorry this is turning to be a difficult to answer, it must be very complex process to add a line to an indicator in Backtrader

    IT IS NOT. Your code is broken and you are the only one making things difficult.

    @cwse said in Make an Indicator off a Custom 'Line':

    I am passing many lines into my strategy at present and do not need help with extending data feeds, this process works just fine thank you.

    Apparently YOU NEED HELP.

    If if works so smoothly, can you please say why this happens

    @cwse said in Make an Indicator off a Custom 'Line':

    For example, if my line is called 'X'; this doesnt appear to work?

    for i, d in enumerate(d for d in self.datas):
                self.smaX[d] = bt.indicators.SimpleMovingAverage(d.lines.X, period=5)
    
    

    I get this error:

    AttributeError: 'Lines_LineSeries_DataSeries_OHLC_OHLCDateTime_Abst' object has no attribute 'X'
    

    Because the translation to plain English is: "data feed d has no line with the name X".

    @cwse said in Make an Indicator off a Custom 'Line':

    I just didn't want to copy my over 2000 lines of code as I think it is distracting from the question at hand

    Posting, reposting, non-working snippets, broken code is a lot easier than doing something simple. Of course ... because you have 2000 lines. You can only do complex things

    Doing this was very difficult for someone who can write 2000 lines of code

    class GenericCSV_X(bt.feeds.GenericCSVData):
        lines = ('X',)
        params = dict(dtformat='%Y-%m-%d', X=4)  # X same as close
    
    class St(bt.Strategy):
        def __init__(self):
            bt.ind.SMA(self.data.lines.X)  # or self.data.X
    
    cerebro = bt.Cerebro()
    cerebro.adddata(GenericCSV_X(dataname='../../datas/2006-day-001.txt'))
    cerebro.addstrategy(St)
    cerebro.run()
    

    And it works ... Holy Cow! A total of 10 code lines + 2 blanks. You can add the import backtrader as bt.

    @cwse said in Make an Indicator off a Custom 'Line':

    Given it appears to not be a straightforward process to pass a line to an indicator

    IT IS STRAIGHTFORWARD. Your code is broken. But you prefer to post, repost and remix broken questions and broken snippets rather than asking straightforward questions and presenting straightforward code.