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

Using Cheat on open and cheat on close together



  • Hello,

    Iv been trying to make my script buy and sell the open and the close respectively on the same daily candle.

    This is what I have so far but it hasnt been working.

    def __init__(self):
            self.order = None
            self.o = dict()  
            self.W_L_dict = dict()
            self.cheating = self.cerebro.p.cheat_on_open
            
        def prenext(self):
            next(self)
    
        def prenext_open(self):
            self.next_open()
        
        def next(self):
            for i, d in enumerate(self.datas):
                if (len(self.datas[i])>0):
                    Open = self.datas[i].open
                    High   = self.datas[i].high
                    Low    = self.datas[i].low
                    Close  = self.datas[i].close
                    Symbol = self.datas[i]._name
                    Date = self.datas[i].datetime.datetime
                    position = self.broker.getposition(data = d)
                    Month_start = BMonthBegin()
                    Month_end = BMonthEnd()
                    print ('{} Open {}, High {}, Low {}, Close {}'.format(Date(0), Open[0], High[0], Low[0], Close[0]))
                    print(' ')
                              
            if self.cheating: #look at next_open()
                return
            self.operate(fromopen=False)
    
        def next_open(self):
            if not self.cheating:
                return
            self.operate(fromopen=True)   
    
        def operate(self,fromopen):
    
            for i, d in enumerate(self.datas):
                if (len(self.datas[i])>0):
    
                    Open = self.datas[i].open
                    High   = self.datas[i].high
                    Low    = self.datas[i].low
                    Close  = self.datas[i].close
                    Symbol = self.datas[i]._name
                    Date = self.datas[i].datetime.datetime
                    position = self.broker.getposition(data = d)
                    Month_start = BMonthBegin()
                    Month_end = BMonthEnd()
                   
                  
                    if Date(0) == Month_start.rollforward(Date(0)):                      
                        self.o[d] = [self.buy(data=d)]
                        print("{} BUY BUY BUY".format(Date(0)))
    
                    if position:
                        if Date(0) == Month_end.rollforward(Date(0)):
                                        self.o[d] = [self.close( data=d)]               
                                        print("{} SELL SELL SELL".format(Date(0)))         
    
     cerebro = bt.Cerebro(cheat_on_open=True)
     cerebro.broker.set_coo(True)
     cerebro.broker.set_coc(True)
    

    I get this result where the buy and sell are on the correct date but both are executing on the close price.

    2017-11-29 00:00:00 Open 263.02, High 263.63, Low 262.2, Close 262.71
     
    2017-11-30 00:00:00 SELL SELL SELL
    2017-11-30 00:00:00 Sell SPY Order 14 Status Accepted Price 0.0
    2017-11-30 00:00:00 Sell SPY Order 14 Status Completed Price 265.01
    2017-11-30 00:00:00 OPERATION PROFIT, GROSS 752.00, NET 752.00, Symbol:SPY
    2017-11-30 00:00:00 Open 263.76, High 266.05, Low 263.67, Close 265.01
     
    2017-12-01 00:00:00 BUY BUY BUY
    2017-12-01 00:00:00 Buy SPY Order 15 Status Accepted Price 0.0
    2017-12-01 00:00:00 Buy SPY Order 15 Status Completed Price 264.46
    2017-12-01 00:00:00 Open 264.76, High 265.31, Low 260.76, Close 264.46
     
    2017-12-04 00:00:00 Open 266.31, High 266.8, Low 264.08, Close 264.14
    

    Here I follow the suggestion I saw in another post and put the self.close in the next section instead of the next_open and added exectype=bt.Order.Close inside of its brackets.

    def __init__(self):
            self.order = None
            self.o = dict()  
            self.W_L_dict = dict()
            self.cheating = self.cerebro.p.cheat_on_open
            
        def prenext(self):
            next(self)
    
        def prenext_open(self):
            self.next_open()
        
        def next(self):
            for i, d in enumerate(self.datas):
                if (len(self.datas[i])>0):
                    Open = self.datas[i].open
                    High   = self.datas[i].high
                    Low    = self.datas[i].low
                    Close  = self.datas[i].close
                    Symbol = self.datas[i]._name
                    Date = self.datas[i].datetime.datetime
                    position = self.broker.getposition(data = d)
                    Month_start = BMonthBegin()
                    Month_end = BMonthEnd()
                    print ('{} Open {}, High {}, Low {}, Close {}'.format(Date(0), Open[0], High[0], Low[0], Close[0]))
                    print(' ')
    
                    if position:
                        if Date(0) == Month_end.rollforward(Date(0)):
                            self.o[d] = [self.close(exectype=bt.Order.Close, data=d)]               
                            print("{} SELL SELL SELL".format(Date(0)))     
                     
            if self.cheating: #look at next_open()
                return
            self.operate(fromopen=False)
    
        def next_open(self):
            if not self.cheating:
                return
            self.operate(fromopen=True)   
    
        def operate(self,fromopen):
    
            for i, d in enumerate(self.datas):
                if (len(self.datas[i])>0):
    
                    Open = self.datas[i].open
                    High   = self.datas[i].high
                    Low    = self.datas[i].low
                    Close  = self.datas[i].close
                    Symbol = self.datas[i]._name
                    Date = self.datas[i].datetime.datetime
                    position = self.broker.getposition(data = d)
                    Month_start = BMonthBegin()
                    Month_end = BMonthEnd()
                   
                  
                    if Date(0) == Month_start.rollforward(Date(0)):                      
                        self.o[d] = [self.buy(data=d)]
                        print("{} BUY BUY BUY".format(Date(0)))
    

    I get this result where the sells are on the wrong day and the the closing price is still being used for both buy and sell.

    2017-11-30 00:00:00 Open 263.76, High 266.05, Low 263.67, Close 265.01
     
    2017-11-30 00:00:00 SELL SELL SELL
    2017-12-01 00:00:00 BUY BUY BUY
    2017-12-01 00:00:00 Sell SPY Order 14 Status Accepted Price 0.0
    2017-12-01 00:00:00 Buy SPY Order 15 Status Accepted Price 0.0
    2017-12-01 00:00:00 Sell SPY Order 14 Status Completed Price 264.46
    2017-12-01 00:00:00 Buy SPY Order 15 Status Completed Price 264.46
    2017-12-01 00:00:00 OPERATION PROFIT, GROSS 697.00, NET 697.00, Symbol:SPY
    2017-12-01 00:00:00 Open 264.76, High 265.31, Low 260.76, Close 264.46
     
    2017-12-04 00:00:00 Open 266.31, High 266.8, Low 264.08, Close 264.14
    

    Any pointers?


  • administrators

    The broker tries its best ... and gives you cheating on the close. You cheat twice and hence you cheat yourself.

    You can do the following:

    • In next_open create orders with an extra named argument : coc=False. This deactivates cheat-on-close for that specific order

    Or

    • Use a higher resolution timeframe (i.e.: go sub-days)

    Or

    • Use a filter to break the daily bar into O + HLC (or OHL + C). See here: Filter Reference


  • Not to sound too defensive, but im not sure it really is cheating, unless you are using that phrasing as just a funny naming schema.

    While expecting this to be an automated system may or may not work, I am simply trying to back-test a strategy I would implement manually. Since i would be able to see at 09:30 of any given day if the preconditions for my trade logic are true, I could execute a buy at 09:30 and then sell it at the end of the day at 16:00 manually.

    So its a pitty that trying to make something like this work is considered "cheating" and thus backtrader doesn't really support it well, since there are practical real world applications for it.

    That being said, I appreciate the workarounds suggested and will report back after I try them. I did think of using a more granular timeframe as you said, but I fear it will dramatically reduce the speed at which I can test my strategy as it may become too data intensive.


  • administrators

    The logic of what you want to do is understood. It is in any case cheating because you are working during the open with a price (open) which you don't know if you will get. Preconditions is not the same as Conditions. You may of course assume that the difference will be small and that may be ok for you and even a real approximation.

    In the case of the close, take into account that the indicators have already been calculated. Any decision you make to sell will be based on those indicators. The close is usually one of the components used in the calculations. You are cheating because you are making a decision with a known value which is already gone (and with the final value of the indicators which is not available)

    Again, the approximation may work for you, and believe me: if it does, I will be happy.



  • @backtrader Sorry i should have been more clear. Yes, it is cheating if you are using indicators that are reliant on prices you wouldn't have known in advance.

    What I am saying is that having a simple way to backtest a buy or sell on either the open or close of the candle you are currently on is not always cheating so long as you dont plan on using the script to automate trades or make decisions you couldn't have made. For myself, I may want to buy at the open and the close because its the start and end to the day, or if I do decide to make those trades conditional on the price of the open in question, then its because im sitting at my computer with my finger on the buy key of my brokerage account at 9:29 and have a fairly strong opinion on what the open will look like.

    To say:

    " It is in any case cheating because you are working during the open with a price (open) which you don't know if you will get."

    isnt necessarily true since you can usually tell with most stocks and ETFs where the price is during the premarket and you can defiantly tell where where the price is right before the close. I could just as easily say that any "executed" trade on any backtesting platform is cheating since its impossible to know exactly what fills you will get in real life due to volume and slippage no matter how good your slippage model is.

    As you said, I have to work with an approximation. However, that isnt just restricted to what you might call "cheating". Its something that must be taken into account with every executed trade that any back testing test produces.

    I have taken a brief look at using a filter to break the bar into O + HLC and it seems like it might work, so I thank you for that. I do wish though that it was as simple as writing:

     self.buy(execute_now=True, exectype=bt.Order.Close,data=d)
    

    as having to do a work around will make it more difficult and lengthy to change the strategy quickly if i so choose to afterward. Please take it into consideration for a future update as I suspect many will be thankful for it.