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

Strange behavior with cheat_on_open – or that might have nothing to do with it



  • Implementing a simple strategy that looks at the ROC(10) of SPY monthly data. If ROC xAbove 0 go long at the open of the next month (via daily data), otherwise if ROC xBelow 0 go short at the open of the next month (via daily data).
    Monthly data from Yahoo is tagged against the first of the month, not the last day. To achieve what I want I provide two data feeds, daily SPY (to do the trades against) and Monthly SPY to trigger the trades. I have set cheat_on_open to true and configured the broker as well. In <strategy>.next_open I determine if the month is different from the last month (i.e., the first trading day of a new month). At this point I look at the current and previous value of ROC(10, monthly) to determine if any trade needs to be done.
    The weird thing is the values that are being served up for ROC. Sometimes they match my calculated values, and sometimes they repeat or skip forward. I can’t understand what is happening. The blue heading indicates data that came out of Backtrader versus the Yahoo data and my calculated ROC value in grey/orange.

    Any ideas?

    0_1561842707453_rocData.JPG

    Here is the main setup

            print('---------- Start: %s - End: %s ----------' % (fromdate.date(), todate.date()))
    
            # Create a cerebro entity, runonce handle mixed timeBase indicators
            # cerebro0 is strategy, cerebro_bnh is Buy and hold to compare against
            cerebro0 = CerebroAnalyzer.CerebroAnalyzer(runonce=False, cheat_on_open=True)
            cerebro_bnh = CerebroAnalyzer.CerebroAnalyzer(runonce=False, cheat_on_open=True)
            cerebros = (cerebro0, cerebro_bnh)
    
            # Add a strategy
            if 1:
                cerebro0.addstrategy(sRocSpx.RocCross, rocPeriod=10, emaRocPeriod=1, eLim=0.0)
            else:
                cerebro0.optstrategy(sRocSpx.RocCross, rocPeriod=range(4, 20, 2))
    
            # Create a Data Feed
            data = bt.feeds.YahooFinanceCSVData(
                dataname=os.path.join(datapath, 'SPY-1D.csv'),
                fromdate=fromdate,        # Do not pass values before this date
                todate=todate,            # Do not pass values after this date
                reverse=False,            #
            )
    
            data1 = bt.feeds.YahooFinanceCSVData(
                dataname=os.path.join(datapath, 'SPY-1M.csv'),
                fromdate=fromdate,        # Do not pass values before this date
                todate=todate,            # Do not pass values after this date
                reverse=False,            #
                timeframe=bt.TimeFrame.Months,
            )
    
            stake = 100
            comm = 5.0/stake
            for c in cerebros:
                # Add the Data Feed to Cerebro
                c.adddata(data)
                c.adddata(data1)
                # Set our desired cash start
                c.broker.setcash(100000.0)
                c.broker.set_coo(True)
    

    Here is the strategy

        params = {
            ('emaRocPeriod', 19),
            ('eLim', 0.003),
            ('rocPeriod', 10),
        }
    
        def __init__(self, *args, **kwArgs):
            super().__init__(*args, **kwArgs)
    
            self.dailyClose = self.datas[0].close
            self.monthClose = self.datas[1].close
    
            self.roc = bt.ind.ROC(self.monthClose, period=self.p.rocPeriod)
            #self.roc1 = self.roc(-1)
    
            # To keep track of pending orders and buy price/commission
            self.order = None
            self.buyprice = None
            self.buycomm = None
            self.tradeOpen = False
    
            self.rocCurrent = None
            bt.Indicator.csv = True
    
            self.lastMonth = None  # what was the last month that we looked at the close
            self.monthEnd = None   # what day 1-31 is the last trading day of the month
    
        #------------------------------------------------------------------------
        def next_open(self):
            """ We decide on the 1st day of the month if we need to trade based on how the monthly ROC
            finsihed
            """
            nowIs = self.datas[0].datetime.date(0)
            if self.lastMonth != nowIs.month:
                self.lastMonth = nowIs.month
    
                rocCurrent = self.roc.roc[0]
                self.rocCurrent = rocCurrent
    
                rocLast = self.roc.roc[-1] #self.roc1[0]
                #value1 = self.ema_roc1.ema[0]
    
                xa = rocCurrent > 0 and rocLast <= 0
                xb = rocCurrent < 0 and rocLast >= 0
    
                # Check if an order is pending ... if yes, we cannot send a 2nd one
                if self.order:
                    return
    
                self.log('>>> %7.3f,%7.4f,%7.4f,%5s,%5s' % (self.monthClose[0],
                                                            rocCurrent, rocLast, xa, xb))
    
                # Check if we are not in the market (no open)
                if self.tradeSize <= 0 and xa:
                    # Not yet ... we MIGHT BUY if ...
                    #if xa: # and (emaRoc0 <= -self.p.eLim or self.p.eLim == 0.0):
    
                    # BUY, BUY, BUY!!! (with all possible default parameters)
                    self.log('BUY CREATE, %.2f' % self.dailyClose[0])
    
                    # Simply log the closing price of the series from the reference
                    self.log('Close, %.2f, %.2f, %.2f, xa:%5s xb:%5s' % (
                        self.monthClose[-1], rocCurrent, rocLast, xa, xb))
    
                    # Keep track of the created order to avoid a 2nd order
                    size = 100 if self.tradeSize == 0 else 200
                    self.order = self.buy(size=size)
    
                elif self.tradeSize >= 0 and xb:
                    #if xb: # and (emaRoc0 >= self.p.eLim or self.p.eLim == 0.0):
                    #if self.dataclose[0] < self.sma[0]:
                        # SELL, SELL, SELL!!! (with all possible default parameters)
                    self.log('SELL CREATE, %.2f' % self.dailyClose[0])
    
                    # Simply log the closing price of the series from the reference
                    self.log('Close, %.2f, %.2f, %.2f, xa:%s xb:%s' % (
                        self.monthClose[0], rocCurrent, rocLast, xa, xb))
    
                    # Keep track of the created order to avoid a 2nd order
                    size = 100 if self.tradeSize == 0 else 200
                    # go short
                    self.order = self.sell(size=size)
    

  • administrators

    When using cheat-on-open:

    • The indicators have not been recalculated and hold the values that were last seen during the previous cycle in the equivalent xxx regular methods

    From: Docs - Cheat-On-Open


Log in to reply
 

});