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

Creating an indicator for multiple data feeds



  • Hello, I am trying to figure out how to make an indicator that can be used for multiple stocks. In the following example I have two stocks that I wish to produce a moving average for based on their volume. The trouble is that im not sure how to specify to the SMA indicator that i want it to make the SMA based on the volume.

    Below this code are other attempts iv tried.

    import datetime 
    from datetime import timedelta
    import pandas as pd
    import backtrader.feeds as btfeeds
    import pandas_datareader.data as web
    import backtrader as bt 
    import backtrader.indicators as btind
    import backtrader.analyzers as btanalyzers
    import matplotlib.pyplot as plt
    
    class PandasclassFilter(btfeeds.PandasData):
        lines = ('volume',)
        params =(('volume', -1),)
        datafields = btfeeds.PandasData.datafields + ([ 'volume'])
    
    class Universe_filter(bt.Strategy):
        params = dict(period=14)
    
        def __init__(self):
        
            self.newSMA = btind.MovingAverageSimple(period = self.params.period)
            
        def next(self):    
            for i, d in enumerate(self.datas):  
                
                day_open = self.datas[i].open
                day_close = self.datas[i].close
                day_low = self.datas[i].low 
                day_high = self.datas[i].high
                date = self.datas[i].datetime.date
                day_vol = self.datas[i].volume
                symbol = self.datas[i]._name
                Vol_14P_SMA = self.newSMA
              
                if Vol_14P_SMA[0] > 0 :
                    Vol_percent = (day_vol[0]/Vol_14P_SMA[0])*100
                else:
                    Vol_percent = .00001
                if date(0) == run().Filter_start:
                    return
                if Vol_percent < 300:
                    return
                if day_low[0] < ((day_close[-1])*.90):
                  
                    print("Date: %s Close: %s High: %s Low: %s Open: %s Vol: %s" %(date(0), day_close[0], day_high[0], day_low[0],day_open[0], day_vol[0]))               
                    print('Vol SMA: %s' % Vol_14P_SMA[0])
                    print('Vol Percent: %.2f%%' % Vol_percent)
                             
    
    class run:
        
        Stocklist = ['OSTK','GME']
        Filter_start = datetime.date(2017,4,27)
        Filter_end = datetime.date(2018,5,9)
       
        def runstrat(self):
    
            #Filter Cerebro start + add strat + data + run
                cerebro = bt.Cerebro()
                cerebro.addstrategy(Universe_filter)
                
                for i in (run.Stocklist): 
                    DF_1 = web.DataReader([i], "morningstar", run.Filter_start, run.Filter_end)
                    DF_1= pd.DataFrame(DF_1)
                    DF_1.reset_index(level = 'Symbol', inplace=True,drop=False)
                    data1 = PandasclassFilter(dataname = DF_1)
                    cerebro.adddata(data1, name = i)
           
                cerebro.run()   
                cerebro.plot(style = 'candle',barup = 'green')
           
    if __name__ == '__main__': 
        strat = run()
        strat.runstrat()
     
    

    Below this text is another attempt i made by specifying in the init method that I want the SMA to refer to volume, however when i use a loop within the init function the next method seems to only take in one of the two stocks.

    The code can be copy and pasted right into the init in the code above to replicate the issue.

    def __init__(self):
            for i,d in enumerate(self.datas): 
                print(i,d)
                day_vol = self.datas[i].volume
                self.newSMA = btind.MovingAverageSimple(day_vol, period = self.params.period)
    

  • administrators

    This may be like a dumb question but:

    • Where is code for the Indicator you are trying to create?

    You say you are trying to create an indicator but there is no indicator in that code.

    An example (amongst others) of an Indicator which uses multiple data feeds is CrossOver. It's in the sources. Furthermore you can even force the minimum number of data feeds which the indicator is bound to receive.

    You have a strategy and the extra code you say one can paste to reproduce an issue. Which issue?

    Can you:

    • Provide a complete example
    • Indicate what's actually not working and why it is not working
    • What's the input
    • What's the output


  • @backtrader I am just trying to use the SMA. As i understand it, that is already built into backtrader.indicators or am i mistaken?

    What i posted is the complete example. I am trying to compare the volume of the [0] day with SMA of the [0] value of a 14 period SMA on volume.

    If the volume of the [0] day is over 300% of the [0] value on the SMA and the low of the day is over 10% lower than the previous day close, then I have the print functions tell me the OHLC, Volume and Vol_percent of those days.

    Im not home now, but il post an additional reply with the outputs that this code makes. You should be able to just copy and paste it into any python interpreter though.


  • administrators

    @sagittarius19 said in Creating an indicator for multiple data feeds:

    I am just trying to use the SMA

    The SMA is not an indicator for multiple data feeds. You are therefore NOT creating an indicator for multiple data feeds.

    It seems that what you want to do is instantiate an indicator multiple times, each time with a single data feed.

    The volume today is: myvolumes[i] = self.datas[i].volume[0]. The SMA is: mysmas[i] = bt.ind.SMA(self.datas[i].volume, period=14) and the comparison myvolumes[i] > Factor_X * mysmas[i]

    It is still unclear what your actual problem is. Instantiating a SimpleMovingAverage on one data feed or on 1,000,000 data feeds is simply a repetitive (ie: a loop) task.



  • @backtrader I edited the code i posted as there was a typo. None the less i am still getting the errors i will show below. When i try and run the loop, I am only getting results for a single stock.

    When I run:

    import datetime 
    from datetime import timedelta
    import pandas as pd
    import backtrader.feeds as btfeeds
    import pandas_datareader.data as web
    import backtrader as bt 
    import backtrader.indicators as btind
    import backtrader.analyzers as btanalyzers
    import matplotlib.pyplot as plt
    
    class PandasclassFilter(btfeeds.PandasData):
        lines = ('volume',)
        params =(('volume', -1),)
        datafields = btfeeds.PandasData.datafields + ([ 'volume'])
    
    class Universe_filter(bt.Strategy):
        params = dict(period=14)
    
        def __init__(self):
            for i,d in enumerate(self.datas): 
                day_vol = self.datas[i].volume
                self.newSMA = btind.MovingAverageSimple(day_vol, period = self.params.period)
            
        def next(self):    
            for i, d in enumerate(self.datas):  
                
                day_open = self.datas[i].open
                day_close = self.datas[i].close
                day_low = self.datas[i].low 
                day_high = self.datas[i].high
                date = self.datas[i].datetime.date
                day_vol = self.datas[i].volume
                symbol = self.datas[i]._name
                Vol_14P_SMA = self.newSMA
              
                if Vol_14P_SMA[0] > 0 :
                    Vol_percent = (day_vol[0]/Vol_14P_SMA[0])*100
                else:
                    Vol_percent = .00001
                if date(0) == run().Filter_start:
                    return
                if Vol_percent < 300:
                    return
                if day_low[0] < ((day_close[-1])*.90):
                  
                    print(" %s %s Close: %s High: %s Low: %s Open: %s Vol: %s" %(date(0),symbol, day_close[0], day_high[0], day_low[0],day_open[0], day_vol[0]))               
                    print('Vol SMA: %s' % Vol_14P_SMA[0])
                    print('Vol Percent: %.2f%%' % Vol_percent)
                             
    
    class run:
        
        Stocklist = ['OSTK', 'GME']
        Filter_start = datetime.date(2017,4,27)
        Filter_end = datetime.date(2018,5,9)
       
        def runstrat(self):
    
            #Filter Cerebro start + add strat + data + run
                cerebro = bt.Cerebro()
                cerebro.addstrategy(Universe_filter)
                
                for i in (run.Stocklist): 
                    DF_1 = web.DataReader([i], "morningstar", run.Filter_start, run.Filter_end)
                    DF_1= pd.DataFrame(DF_1)
                    DF_1.reset_index(level = 'Symbol', inplace=True,drop=False)
                    data1 = PandasclassFilter(dataname = DF_1)
                    cerebro.adddata(data1, name = i)
           
                cerebro.run()   
                cerebro.plot(style = 'candle',barup = 'green')
           
    if __name__ == '__main__': 
        strat = run()
        strat.runstrat()
    

    I get:

    [Running] python "c:\Python36\.vscode\Strats\Filter2.py"
     2017-10-10 OSTK Close: 28.1 High: 33.3 Low: 27.575 Open: 31.0 Vol: 5122374.0
    Vol SMA: 1538266.71429
    Vol Percent: 333.00%
     2017-12-19 OSTK Close: 73.625 High: 82.7 Low: 68.05 Open: 73.45 Vol: 10734603.0
    Vol SMA: 3326904.85714
    Vol Percent: 322.66%
    
    [Done] exited with code=0 in 4.773 seconds
    

    I should be seeing results for both OSTK and GME but am only seeing results for OSTK.

    When I run GME alone in the "Stocklist" I get 4 different dates that the filter printed:

    [Running] python "c:\Python36\.vscode\Strats\Filter2.py"
     2017-05-26 GME Close: 22.22 High: 22.41 Low: 21.25 Open: 22.12 Vol: 14211097.0
    Vol SMA: 3749496.35714
    Vol Percent: 379.01%
     2017-08-25 GME Close: 19.4 High: 19.94 Low: 18.72 Open: 19.9 Vol: 20495518.0
    Vol SMA: 3517752.5
    Vol Percent: 582.63%
     2018-01-12 GME Close: 17.76 High: 18.54 Low: 17.63 Open: 18.5 Vol: 15445837.0
    Vol SMA: 3750032.57143
    Vol Percent: 411.89%
     2018-03-29 GME Close: 12.62 High: 13.0 Low: 12.2 Open: 12.65 Vol: 25461816.0
    Vol SMA: 5696925.71429
    Vol Percent: 446.94%
    
    [Done] exited with code=0 in 4.181 seconds
    

    So in short, there is some issue with having a loop in the init method that is preventing next from receiving both datafeeds. I tried using prenext but that didnt solve the problem either. I should be getting all the filtered dates for both OSTK and GME.

    I tried putting the [i] after self.newSMA but it was throwing errors as well. If that is a critical point let me know.

    Thanks again for your patience and help on this.

    EDIT
    For completeness here is the code and output with the [i] at the end of self.newSMA:

    import datetime 
    from datetime import timedelta
    import pandas as pd
    import backtrader.feeds as btfeeds
    import pandas_datareader.data as web
    import backtrader as bt 
    import backtrader.indicators as btind
    import backtrader.analyzers as btanalyzers
    import matplotlib.pyplot as plt
    
    class PandasclassFilter(btfeeds.PandasData):
        lines = ('volume',)
        params =(('volume', -1),)
        datafields = btfeeds.PandasData.datafields + ([ 'volume'])
    
    class Universe_filter(bt.Strategy):
        params = dict(period=14)
    
        def __init__(self):
            for i,elm in enumerate(self.datas): 
                day_vol = self.datas[i].volume
                self.newSMA[i] = btind.MovingAverageSimple(day_vol, period = self.params.period)
    
        def next(self):    
            for i, elm in enumerate(self.datas):  
                
                day_open = self.datas[i].open
                day_close = self.datas[i].close
                day_low = self.datas[i].low 
                day_high = self.datas[i].high
                date = self.datas[i].datetime.date
                day_vol = self.datas[i].volume
                symbol = self.datas[i]._name
                Vol_14P_SMA = self.newSMA[i]
              
                if Vol_14P_SMA[0] > 0 :
                    Vol_percent = (day_vol[0]/Vol_14P_SMA[0])*100
                else:
                    Vol_percent = .00001
                if date(0) == run().Filter_start:
                    return
                if Vol_percent < 300:
                    return
                if day_low[0] < ((day_close[-1])*.90):
                    print(' ')
                    print("%s %s Close: %s High: %s Low: %s Open: %s Vol: %s" %(date(0),symbol, day_close[0], day_high[0], day_low[0],day_open[0], day_vol[0]))               
                    print('Vol SMA: %s' % Vol_14P_SMA[0])
                    print('Vol Percent: %.2f%%' % Vol_percent)
                    
                             
    
    class run:
        
        Stocklist = ["OSTK", 'GME']
        Filter_start = datetime.date(2017,4,27)
        Filter_end = datetime.date(2018,5,9)
       
        def runstrat(self):
    
            #Filter Cerebro start + add strat + data + run
                cerebro = bt.Cerebro()
                cerebro.addstrategy(Universe_filter)
                
                for i in (run.Stocklist): 
                    DF_1 = web.DataReader([i], "morningstar", run.Filter_start, run.Filter_end)
                    DF_1= pd.DataFrame(DF_1)
                    DF_1.reset_index(level = 'Symbol', inplace=True,drop=False)
                    data1 = PandasclassFilter(dataname = DF_1)
                    cerebro.adddata(data1, name = i)
           
                cerebro.run()   
                #cerebro.plot(style = 'candle',barup = 'green')
           
    if __name__ == '__main__': 
        strat = run()
        strat.runstrat()
    
    [Running] python "c:\Python36\.vscode\Strats\Filter2.py"
    Traceback (most recent call last):
      File "c:\Python36\.vscode\Strats\Filter2.py", line 76, in <module>
        strat.runstrat()
      File "c:\Python36\.vscode\Strats\Filter2.py", line 71, in runstrat
        cerebro.run()   
      File "C:\Python27_path\lib\site-packages\backtrader\cerebro.py", line 1127, in run
        runstrat = self.runstrategies(iterstrat)
      File "C:\Python27_path\lib\site-packages\backtrader\cerebro.py", line 1217, in runstrategies
        strat = stratcls(*sargs, **skwargs)
      File "C:\Python27_path\lib\site-packages\backtrader\metabase.py", line 88, in __call__
        _obj, args, kwargs = cls.doinit(_obj, *args, **kwargs)
      File "C:\Python27_path\lib\site-packages\backtrader\metabase.py", line 78, in doinit
        _obj.__init__(*args, **kwargs)
      File "c:\Python36\.vscode\Strats\Filter2.py", line 22, in __init__
        self.newSMA[i] = btind.MovingAverageSimple(day_vol, period = self.params.period)
      File "C:\Python27_path\lib\site-packages\backtrader\lineseries.py", line 461, in __getattr__
        return getattr(self.lines, name)
    AttributeError: 'Lines_LineSeries_LineIterator_DataAccessor_Strateg' object has no attribute 'newSMA'
    
    [Done] exited with code=1 in 2.811 seconds
    

  • administrators

    @sagittarius19 said in Creating an indicator for multiple data feeds:

    So in short, there is some issue with having a loop in the init

    There is for sure no issue, because there are several tens of scripts doing a loop and accessing all data feeds.

    I fail to understand the complexities of the script. If you want to diagnose a problem, you should remove everything but the data feeds that you are inputting and then see if that feeds and the values produced by them are available to you. The rest of the code is simply bothering your ability to diagnose any mistake you are making.

    @sagittarius19 said in Creating an indicator for multiple data feeds:

    For completeness here is the code and output with the [i] at the end of self.newSMA:

    @sagittarius19 said in Creating an indicator for multiple data feeds:

    self.newSMA[i] = btind.MovingAverageSimple(day_vol, period = self.params.period)
    

    @sagittarius19 said in Creating an indicator for multiple data feeds:

    AttributeError: 'Lines_LineSeries_LineIterator_DataAccessor_Strateg' object has no attribute 'newSMA'
    

    It that's one of the major problems you are facing, I can only very sincerely suggest: master the basics of programming and Python before carrying on with algotrading. This is not meant with arrogance and not meant as patronizing. It is meant as sincere advice.



  • @backtrader The loop in the init function is accessing all the data feeds just fine.

    The trouble is that when i have a loop in the init method, the next method is only running one of the data feeds. I have concluded this issue by doing exactly what you described. I stripped the code of everything and then ran it with the loop in init and then without it. That's what iv found to be the issue.

    I understand that the issue most likely lies somewhere else and that's why i'm posting on the forums to see if anyone can point out where the problem is. Iv searched for examples where people try to use a SMA using a specific column from a datafeed but failed to find any.

    I wasn't totally sure if when you put [i] at the end of mysmas[i], if you were suggesting I write my code like that or were just referring to mysmas when it was running through any of the datafeeds.

    If my questions sound too elementary for your liking then I apologize. Iv done the basic python tutorials but I learn best by getting involved and trying to make a strategy in my head work, rather than spending countless hours doing theoretical exercises. I suspect many more people than you think also are looking for the same questions I have. These forums don't have people posting with a super high frequency and the documentation can be confusing to new people, so its good for the community to be ok with answering questions that seem basic/silly to you. If not you get people who are scared to post something in the fear of being told to master programming before they peruse algotrading.



  • I found a solution.

    import datetime 
    from datetime import timedelta
    import pandas as pd
    import backtrader.feeds as btfeeds
    import pandas_datareader.data as web
    import backtrader as bt 
    import backtrader.indicators as btind
    import backtrader.analyzers as btanalyzers
    import matplotlib.pyplot as plt
    
    class PandasclassFilter(btfeeds.PandasData):
        lines = ('volume',)
        params =(('volume', -1),)
        datafields = btfeeds.PandasData.datafields + ([ 'volume'])
    
    class Universe_filter(bt.Strategy):
        params = dict(period=14)
        
        def __init__(self):
            self.SMAlist = dict()
            for i, elm in enumerate(self.datas):  
                day_vol = self.datas[i].volume
                symbol = self.datas[i]._name
                self.SMAlist[symbol]= [btind.MovingAverageSimple(day_vol, period = self.params.period)]
            
    
        def next(self):    
            
            for i, elm in enumerate(self.datas):  
                
                day_open = self.datas[i].open
                day_close = self.datas[i].close
                day_low = self.datas[i].low 
                day_high = self.datas[i].high
                date = self.datas[i].datetime.date
                day_vol = self.datas[i].volume
                symbol = self.datas[i]._name
                Vol_14P_SMA = self.SMAlist[symbol][0]
                Vol_percent = (day_vol[0]/Vol_14P_SMA[0])*100
               
                if date(0) == run().Filter_start:
                    return
                               
                if Vol_14P_SMA[0] != Vol_14P_SMA[0] :
                    return
                if Vol_percent > 300:
                    if day_low[0] < ((day_close[-1])*.90):
                        print(' ')
                        print("%s %s Close: %s High: %s Low: %s Open: %s Vol: %s" %(date(0),symbol, day_close[0], day_high[0], day_low[0],day_open[0], day_vol[0]))               
                        print('Vol SMA: %s' % Vol_14P_SMA[0])
                        print('Vol Percent: %.2f%%' % Vol_percent)
    
    
    class run:
        
        Stocklist = ['OSTK', 'GME']
        Filter_start = datetime.date(2017,4,27)
        Filter_end = datetime.date(2018,5,9)
       
        def runstrat(self):
    
            #Filter Cerebro start + add strat + data + run
                cerebro = bt.Cerebro()
                cerebro.addstrategy(Universe_filter)
                
                for i in (run.Stocklist): 
                    DF_1 = web.DataReader([i], "morningstar", run.Filter_start, run.Filter_end)
                    DF_1= pd.DataFrame(DF_1)
                    DF_1.reset_index(level = 'Symbol', inplace=True,drop=False)
                    data1 = PandasclassFilter(dataname = DF_1)
                    cerebro.adddata(data1, name = i)
           
                cerebro.run()   
                #cerebro.plot(style = 'candle',barup = 'green')
           
    if __name__ == '__main__': 
        strat = run()
        strat.runstrat()
    

    In my conditonal statements before I had:

    if Vol_percent < 300:
                    return
    

    I changed it to:

    if Vol_percent > 300:
                    if day_low[0] < ((day_close[-1])*.90):
                        print(' ')
                        print("%s %s Close: %s High: %s Low: %s Open: %s Vol: %s" %(date(0),symbol, day_close[0], day_high[0], day_low[0],day_open[0], day_vol[0]))               
                        print('Vol SMA: %s' % Vol_14P_SMA[0])
                        print('Vol Percent: %.2f%%' % Vol_percent)
    

    I also had each SMA added to a dictionary when it was made:

    def __init__(self):
            self.SMAlist = dict()
            for i, elm in enumerate(self.datas):  
                day_vol = self.datas[i].volume
                symbol = self.datas[i]._name
                self.SMAlist[symbol]= [btind.MovingAverageSimple(day_vol, period = self.params.period)]