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

Full example of custom indicator



  • I am trying to learn how to create a custom indicator based on the Indicator Development help page, but I am getting the following error:

    ---> 84 params = dict(period=20, movav=btind.MovAv.Simple)
    85 def init(self):
    86 movav = self.p.movav(self.data, period=self.p.period)

    AttributeError: module 'backtrader.indicator' has no attribute 'MovAv'

    creating the indicator class seems straightforward enough, but I do not understand how to make it available to the rest of the program. My full code is below if anyone is able to point me in the correct direction. Thanks in advance.

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    
    import datetime  # For datetime objects
    import os.path  # To manage paths
    import sys  # To find out the script name (in argv[0])
    
    # Import the backtrader platform
    import backtrader as bt
    import backtrader.indicator as btind
    
    # Create a Stratey
    class TestStrategy(bt.Strategy):
        params = (
            ('period', 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
    
            self.signal = OverUnderMovAv(
                self.datas[0], period = self.p.period)
    
            # To keep track of pending orders and buy price/commission
            self.order = None
            self.buyprice = None
            self.buycomm = None
    
        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                return
    
            # Check if an order has been completed
            # Attention: broker could reject order if not enough cash
            if order.status in [order.Completed]:
                if order.isbuy():
                    self.log(
                        'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                        (order.executed.price,
                         order.executed.value,
                         order.executed.comm))
    
                    self.buyprice = order.executed.price
                    self.buycomm = order.executed.comm
                else:  # Sell
                    self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                             (order.executed.price,
                              order.executed.value,
                              order.executed.comm))
    
                self.bar_executed = len(self)
    
            elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                self.log('Order Canceled/Margin/Rejected')
    
            self.order = None
    
        def notify_trade(self, trade):
            if not trade.isclosed:
                return
    
            self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                     (trade.pnl, trade.pnlcomm))
    
        def next(self):
            # Simply log the closing price of the series from the reference
            self.log('Close, %.2f' % self.dataclose[0])
    
            # Check if an order is pending ... if yes, we cannot send a 2nd one
            if self.order:
                return
    
            # Check if we are in the market
            if not self.position:
    
                # Not yet ... we MIGHT BUY if ...
                if self.signal > 0:
                    # BUY, BUY, BUY!!! (with default parameters)
                    self.log('BUY CREATE, %.2f' % self.dataclose[0])
    
                    # Keep track of the created order to avoid a 2nd order
                    self.order = self.buy()
    
            else:
    
                # Already in the market ... we might sell
                if self.signal < 0:
                    # SELL, SELL, SELL!!! (with all possible default parameters)
                    self.log('SELL CREATE, %.2f' % self.dataclose[0])
    
                    # Keep track of the created order to avoid a 2nd order
                    self.order = self.sell()
    
    # Add custom indicator
    class OverUnderMovAv(bt.Indicator):
        lines = ('overunder',)
        params = dict(period=20, movav=btind.MovAv.Simple)
    
        def __init__(self):
            movav = self.p.movav(self.data, period=self.p.period)
            self.l.overunder = bt.Cmp(movav, self.data)    
    
    
    if __name__ == '__main__':
        # Create a cerebro entity
        cerebro = bt.Cerebro()
    
        # Add a strategy
        cerebro.addstrategy(TestStrategy)
    
        # Datas are in a subfolder of the samples. Need to find where the script is
        # because it could have been called from anywhere
        # modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
        datapath = '/Users/rjbos2/backtrader/datas/orcl-1995-2014.txt'
    
        # Create a Data Feed
        data = bt.feeds.YahooFinanceCSVData(
            dataname=datapath,
            # Do not pass values before this date
            fromdate=datetime.datetime(2000, 1, 1),
            # Do not pass values before this date
            todate=datetime.datetime(2000, 12, 31),
            # Do not pass values after this date
            reverse=False)
    
        # Add the Data Feed to Cerebro
        cerebro.adddata(data)
    
        # Set our desired cash start
        cerebro.broker.setcash(100000.0)
    
        # Set the commission - 0.1% ... divide by 100 to remove the %
        cerebro.broker.setcommission(commission=0.001)
    
        # Print out the starting conditions
        print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    
        # Run over everything
        cerebro.run()
    
        # Print out the final result
        print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
        
    


  • Actually it is true, there is no attribute MovAv in bt.indicator. You may want to use

    movav = btind.SimpleMovingAverage
    


  • @ab_trader said in Full example of custom indicator:

    movav = btind.SimpleMovingAverage

    Thanks for the suggestion. I did notice in the indicators folder that that function was not there, but I was following an example. Unfortunately, SimpleMovingAverage does not work either :(.

    ---> 86 params = dict(period=20, movav = btind.SimpleMovingAverage)
    87 def init(self):
    88 movav = self.p.movav(self.data, period=self.p.period)

    AttributeError: module 'backtrader.indicator' has no attribute 'SimpleMovingAverage'

    Do you think there is something wrong with my installation? All of the "built in" indicator's work fine. I have used backtrader for a few weeks now with no other problems.



  • And for good measure I also tried MovingAverageSimple:

    ---> 86 params = dict(period=20, movav=btind.MovingAverageSimple)
    87 def init(self):
    88 movav = self.p.movav(self.data, period=self.p.period)

    AttributeError: module 'backtrader.indicator' has no attribute 'MovingAverageSimple'

    class MovingAverageSimple(MovingAverageBase):
        '''
        Non-weighted average of the last n periods
    
        Formula:
          - movav = Sum(data, period) / period
    
        See also:
          - http://en.wikipedia.org/wiki/Moving_average#Simple_moving_average
        '''
        alias = ('SMA', 'SimpleMovingAverage',)
        lines = ('sma',)
    
        def __init__(self):
            # Before super to ensure mixins (right-hand side in subclassing)
            # can see the assignment operation and operate on the line
            self.lines[0] = Average(self.data, period=self.p.period)
    
            super(MovingAverageSimple, self).__init__()
    
    


  • I confused you. I think this need to be changed so your original script:

    import backtrader.indicators as btind
    


  • @ab_trader Thank you.


  • administrators

    @Roger-Bos said in Full example of custom indicator:

    import backtrader.indicator as btind
    

    Notice that the original problem is the lack on an s. The code is importing backtrader.indicator and not backtrader.indicators

    And yes, MovAv.Simple and all other aliases aforementioned, do exist.


Log in to reply
 

});