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

indicator with dynamically changing data



  • Hi,

    I am really new to backtrader, so apologies if this is trivial.

    If I create a custom dummy indicator, I can change its data feeds dynamically inside the strategy and if I run the strategy with runonce=False, it works fine (look below for the code). However, if I use an existing indicator instead e.g. bt.indicators.SimpleMovingAverage, the same approach does not work.

    Many thanks for your help.

    class DynamicIndicator(bt.Indicator):
        lines = ('indicator1',)
    
        def next(self):
            self.lines.indicator1[0] = self.datas[0][0]
    
    class TestStrategy(bt.Strategy):
        def __init__(self):
        self.ind = DynamicIndicator(self.datas[1].lines.close)
    
        def next(self):
            if <condition>:
                self.ind.datas[0] = self.datas[2].lines.close
    

  • administrators

    The indicators use a generic notation to refer to the data with which they operate and don't care about indexing the array.

    In any case I feel you are using the wrong approach imho. Instead of changing the data feeds, you should change what the data feed delivers, which at the end of the day delivers the same result, but is compatible with any way an indicator can be written.



  • Many thanks for the reply and the help.

    Do you mean instead of changing the data of the indicator, change the data feed itself?

    I tried that below and although the data feed changes, the moving average is not updated.

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    import backtrader as bt
    import pandas as pd
    import datetime
    
    # Create a Strategy
    class TestStrategy(bt.Strategy):
    
        params = (
            ('maperiod', 2),
        )
    
        def log(self, txt, dt=None):
            ''' Logging function for this strategy'''
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))
    
        def __init__(self):
            self.ind = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maperiod)
    
        def next(self):
            if self.datas[0].datetime.date(0) == datetime.datetime(2018, 1, 6).date():
                self.datas[0] = self.datas[1]
                # del self.datas[0]
            self.log("{}, {}".format(self.datas[0].lines.close[0], self.ind.lines.sma[0]))
    
    
    if __name__ == '__main__':
        # Create a cerebro entity
        cerebro = bt.Cerebro()
    
        # Add a strategy with parameters
        cerebro.addstrategy(TestStrategy, maperiod=2)
    
        df1 = pd.DataFrame({'Close': list(range(1,11))}, index=pd.date_range(start='1/1/2018', periods=10))
        df2 = pd.DataFrame({'Close': list(range(2,12))}, index=pd.date_range(start='1/1/2018', periods=10))
        cerebro.adddata(bt.feeds.PandasData(dataname=df1, nocase=True))
        cerebro.adddata(bt.feeds.PandasData(dataname=df2, nocase=True))
    
        # Set our desired cash start
        cerebro.broker.setcash(100000.0)
    
        # Print out the starting conditions
        print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    
        # Run over everything
        cerebro.run(runonce=False, preload=False)
    
        # Print out the final result
        print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    
    Starting Portfolio Value: 100000.00
    2018-01-02, 2.0, 1.5
    2018-01-03, 3.0, 2.5
    2018-01-04, 4.0, 3.5
    2018-01-05, 5.0, 4.5
    2018-01-06, 7.0, 5.5
    2018-01-07, 8.0, 6.5
    2018-01-08, 9.0, 7.5
    2018-01-09, 10.0, 8.5
    2018-01-10, 11.0, 9.5
    Final Portfolio Value: 100000.00
    

  • administrators

    @momentum said in indicator with dynamically changing data:

    Do you mean instead of changing the data of the indicator, change the data feed itself?

    No, I said that you create a data feed which dynamically changes the data it serves.

    You fail to understand that the self.datas array in each object is a different one. This is obvious if you consider that you can do this

    ma_on_ma = bt.ind.SMA(bt.ind.SMA(self.data, period=5), period=5)
    

    The outer SMA has, obviously, only 1 data feed which happens to be another SMA (the inner one)

    Futhermore, you expect things to use the array self.datas and you manipulate it with that expectation. As noted above, this is not supported, given that other notations and references are used in the internals.