CCI on other indicator



  • Hey guys,

    I'm trying to put a CCI on the +DI and -DI of an ADX indicator, and implement them in my strategy but I get an error with my code: "array out of range" here

    line 17, in init
    self.CCIplusDI = bt.indicators.CommodityChannelIndex(self.ADX.DIplus[0], period=20)

    My code is the following

    import backtrader as bt
    from  datetime import datetime
    
    class firstStrategy(bt.Strategy):
    
        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):
            self.dataclose = self.datas[0].close
            self.order = None
    
            self.rsi = bt.indicators.RSI_SMA(self.data.close, period=14)
            self.ADX = bt.indicators.DirectionalMovementIndex(self.data, period=12)
            self.CCIplusDI = bt.indicators.CommodityChannelIndex(self.ADX.DIplus[0], period=20)
            self.CCIminusDI = bt.indicators.CommodityChannelIndex(self.ADX.DIminus[0], period=20)
    
        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 enougth 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')
    
            # Write down: no pending order
            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):
            self.log('Close, %.2f' % self.dataclose[0])
            if self.order:
                return
    
            # Check if we are in the market
            if not self.position:
    
                # Not yet ... we MIGHT BUY if ...
                if self.rsi < 30 and self.CCIplusDI[-1] > 100 and self.CCIminusDI[-1] < 100:
                    self.log('BUY CREATE, %.2f' % self.dataclose[0])
                    self.order = self.buy()
            else:
    
                if self.rsi > 70 and self.CCIplusDI[-1] < 100 and self.CCIminusDI[-1] > 100:
                    self.log('SELL CREATE, %.2f' % self.dataclose[0])
                    self.order = self.sell()
    if __name__ == '__main__':
        # Create a cerebro entity
        cerebro = bt.Cerebro()
        startcash = 10000
    
        # Add a strategy
        cerebro.addstrategy(firstStrategy)
    
        # Create a Data Feed
        data = bt.feeds.YahooFinanceData(dataname='BNO', fromdate=datetime(2016, 1, 1), todate=datetime(2017, 11, 25))
    
         # Add the Data Feed to Cerebro
        cerebro.adddata(data)
    
        # Set our desired cash start
        cerebro.broker.setcash(startcash)
    
        # Set the commission
        cerebro.broker.setcommission(commission=0.0005)
    
        cerebro.run()
        # Print out the starting conditions
        print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    
        # Get final portfolio Value
        portvalue = cerebro.broker.getvalue()
        pnl = portvalue - startcash
    
        # Print out the final result
        print('Final Portfolio Value: ${}'.format(portvalue))
        print('P/L: ${}'.format(pnl))
    
        # Finally plot the end results
        cerebro.plot()
    


  • I assume it should with no [0] in the _init()':

            self.CCIplusDI = bt.indicators.CommodityChannelIndex(self.ADX.DIplus, period=20)
            self.CCIminusDI = bt.indicators.CommodityChannelIndex(self.ADX.DIminus, period=20)
    

    but it should be indexing in the next():

            if not self.position:
    
                # Not yet ... we MIGHT BUY if ...
                if self.rsi[whatever_index_you_need] < 30 and self.CCIplusDI[-1] > 100 and self.CCIminusDI[-1] < 100:
                    self.log('BUY CREATE, %.2f' % self.dataclose[0])
                    self.order = self.buy()
            else:
    
                if self.rsi[whatever_index_you_need] > 70 and self.CCIplusDI[-1] < 100 and self.CCIminusDI[-1] > 100:
                    self.log('SELL CREATE, %.2f' % self.dataclose[0])
                    self.order = self.sell()
    


  • @ab_trader Hey thanks for your answer

    I still get errors

    Traceback (most recent call last):
      File "C:/Users/mm/PycharmProjects/AlgoTrading/Algo09.py", line 97, in <module>
        cerebro.run()
      File "C:\Users\mm\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\cerebro.py", line 1127, in run
        runstrat = self.runstrategies(iterstrat)
      File "C:\Users\mm\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\cerebro.py", line 1214, in runstrategies
        strat = stratcls(*sargs, **skwargs)
      File "C:\Users\mm\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\metabase.py", line 88, in __call__
        _obj, args, kwargs = cls.doinit(_obj, *args, **kwargs)
      File "C:\Users\mm\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\metabase.py", line 78, in doinit
        _obj.__init__(*args, **kwargs)
      File "C:/Users/mm/PycharmProjects/AlgoTrading/Algo09.py", line 17, in __init__
        self.CCIplusDI = bt.indicators.CommodityChannelIndex(self.ADX.DIplus, period=20)
      File "C:\Users\mm\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\indicator.py", line 53, in __call__
        return super(MetaIndicator, cls).__call__(*args, **kwargs)
      File "C:\Users\mm\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\metabase.py", line 88, in __call__
        _obj, args, kwargs = cls.doinit(_obj, *args, **kwargs)
      File "C:\Users\mm\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\metabase.py", line 78, in doinit
        _obj.__init__(*args, **kwargs)
      File "C:\Users\mm\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\indicators\cci.py", line 62, in __init__
        tp = (self.data.high + self.data.low + self.data.close) / 3.0
      File "C:\Users\mm\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\lineseries.py", line 461, in __getattr__
        return getattr(self.lines, name)
    AttributeError: 'Lines_LineSeries_LineSeriesStub' object has no attribute 'high'


  • As I know CCI calculates typical price which requires high, low and close prices. You calculate CCI on ADX indicator, which doesn't have values required. Probably this is a problem.

    You may need to re-write this indicator to be applicable in your case. The code is really simple - backtrader - CCI script



  • @Horizion said in CCI on other indicator:

    I'm trying to put a CCI on the +DI and -DI of an ADX indicator,

    The problem is really here: a commodity channel for 2 values and the code shows attempts at using 1 value

    @ab_trader said in CCI on other indicator:

    As I know CCI calculates typical price which requires high, low and close prices

    Because as quoted here: 3 values are used to calculate the commodity channel

    If there were no mismatch between 1, 2 and 3, one could easily foresee a very small custom indicator which wraps the ADX and copies the needed values into custom lines high, low and close, which can be fed into the CCI



  • @Paska-Houso @ab_trader Hey guys,

    Thanks for your answers. I have to admit that I am a little lost, as I don't know much about Python. I was trying to recreate what I used on an other Charting website (Tradingview) which allowed me to put a CCI on the +DI and -DI of the ADX.

    when you say "The problem is really here: a commodity channel for 2 values and the code shows attempts at using 1 value" what exactly do you mean ?

    What I was trying to do is get two CCI's on both the +DI and -DI. So I don't understand why the code shows attempts at using 1 value.

    Regarding the high, low and close: I don't know how to implement these, since the +DI has only one value, and same for the -DI...

    I don't know what to do... And where to look

    alt text



  • It's not a Python thing ... see

    0_1512137485782_17e9e6fb-5796-4113-aafb-9042a4e2bc81-image.png

    0_1512137492600_d73886e6-6779-42ea-907b-5b7b2bb1894b-image.png

    Typical Price == pt in the first formula.

    The calculation is based on averaging the high, low and close as pointed out by @ab_trader, and the implementation in backtrader obviously followed that definition (i.e.: you need 3 components)

    Because the calculation is made later exclusively based on the averaged price, one could do it (as you try in your code) by passing a single value. But with the current implementation, passing a single data feed (or line) is obviously doomed to fail.



  • First ADX indicators need to be improved a bit, hence write new indicator (shown ADXDIPlus only):

    class ADXDIPlus3(bt.Indicator):
    
        lines = ('high', 'low', 'close')
    
        params = (('period', 20),)
    
        def __init__(self):
    
            self.ADX = bt.indicators.DirectionalMovementIndex(period=self.p.period)
    
            self.lines.high = self.ADX.DIplus
            self.lines.low = self.ADX.DIplus
            self.lines.close = self.ADX.DIplus
    

    Then call this indicator in your strategy __init__:

        def __init__(self):
    
            self.ADXPlus3 = ADXDIPlus3(period=12)
            self.CCI_ADXPlus = bt.indicators.CommodityChannelIndex(self.ADXPlus3, period=20)
    

    I didn't check the numbers, but the logic seems right. ADXMinus need to be treated the same way.



  • linealias as described in this blog post, could make things even simpler

    Something along the lines of

    class For_CCI_On_DIplus(bt.indicators.DirectionalMovementIndex)
        linealias = (('DIplus', 'high'), ('DIplus', 'low'), ('DIplus', 'close'),)
    


  • @Paska-Houso @ab_trader

    Thanks a LOT guys, amazing community here...

    I'll try to implement your solutions as soon as I have access to my PC


Log in to reply
 

Looks like your connection to Backtrader Community was lost, please wait while we try to reconnect.