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

Create an indicator based on an analyzer



  • Dear community,

    I would like to create an indicator based on the daily returns of a data series. As far as I understand, the returns are not automatically "available", but first have to be calculated with an analyzer.

    So here is my approach:
    First, I load the data, then I create the analyzer called "myReturn":

    #  Load data from a pre-specified dataframe
    data = bt.feeds.PandasData(dataname=dataframe)
    
    # Add data to cerebro
    cerebro.adddata(data)
    
    # Add analyzer        
    cerebro.addanalyzer(bt.analyzers.TimeReturn,
                                timeframe=bt.TimeFrame.Days,
                                data=data,
                                _name='myReturn')
    

    After I run the strategy, I can access the analyzer (=returns), so that works well:

    thestrats = cerebro.run()
    thestrat = thestrats[0]
    
    tret_analyzer = thestrat.analyzers.getbyname('myReturn')
    print(tret_analyzer.get_analysis())
    

    However, when I would like to create an indicator based on the analyzer, I just can't figure out how to access it. As a simple example, I would like to create two moving averages:

    • a moving average of the price of the last 15 days
    • a moving average of the returns of the last 15 days
    # Sample strategy from backtrader quickstart example
    class TestStrategy(bt.Strategy):
        params = (
            ('maperiod', 15),
        )
    
        def log(self, txt, dt=None):
            ''' Logging function fot this strategy'''
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))
    
        def __init__(self):
            # Keep a reference to the "close" line in the data[0] dataseries
            self.dataclose = self.datas[0].close
    
            # To keep track of pending orders and buy price/commission
            self.order = None
            self.buyprice = None
            self.buycomm = None
    
            # Add a MovingAverageSimple indicator OF THE CLOSING PRICE
            self.pricesma = bt.indicators.SimpleMovingAverage(
                self.datas[0], period=self.params.maperiod)
    
            # Add a MovingAverageSimple indicator OF THE RETURNS (=ANALYZER)
            self.returnsma = bt.indicators.SimpleMovingAverage(
                self.analyzers[0], period=self.params.maperiod)
    

    The problem is obviously the last line in the code, as this seems to be the wrong way to access the values of the analyzers.

    Could anybody tell me how I correctly refer to an analyzer in order to create an indicator? That would be great!

    Thanks a lot!
    jf


  • administrators

    An Analyzer is not a data feed and cannot therefore be used as input for an Indicator. In any case you are not calculating returns (as in the returns of your accounts) but the daily variations of the data.

    Why not use the PercentChange indicator on the data and then make a SimpleMovingAverage of that?

    myind = bt.ind.SMA(bt.ind.PctChange(self.data), period=myperiod))
    


  • @backtrader: Ok great, thanks!

    @community: I guess a standard number that traders are interested in is the volatility of returns, which can be used for portfolio construction approaches such as "equal contribution to risk", volatility based risk management, etc.

    As backtrader explained above, it is best to implement this with an indicator based on an indicator. I currently implement return volatility calculation like this (using TA-LIB):

    returnvola = bt.talib.STDDEV(bt.ind.PctChange(self.data.close), timeperiod=30, plot=True)
    

    Would of course be interested to hear from you if somebody implements it differently, for example without TA-LIB... Also, has someone implemented this based on log-returns?

    Thanks!


  • administrators

    @jf said in Create an indicator based on an analyzer:

    returnvola = bt.talib.STDDEV(bt.ind.PctChange(self.data.close), timeperiod=30, plot=True)
    

    Would of course be interested to hear from you if somebody implements it differently, for example without TA-LIB

    returnvola = bt.ind.StdDev(bt.ind.PctChange(self.data.close), period=30)
    

    which you can shorten (to make it a lot more generic) to

    returnvola = bt.ind.StdDev(bt.ind.PctChange(self.data), period=30)
    

    or even

    returnvola = bt.ind.StdDev(bt.ind.PctChange(), period=30)
    


  • Perfect, thanks!