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

Trying to execute on the open, although the order executes the next day



  • Hi,

    I've coded a simple strategy which buys at the open, after price gaps up by at least 1% while the previous bar was an NR7 bar (most narrow bar over the past 7 bars). I understand when making a decision at market close, we can only execute it the next bar. I'm making my decision at the open price however and I want to execute after having that information available. I haven't been able to do this however.

    NR7 indicator:

    class NR7_indicator(bt.Indicator):
        
        lines = ('NR7',)
        
        def __init__(self):
            
            self.HiLo = self.data.high - self.data.low
            
        def next(self):
            
            # get the smallest high-low value of the previous 6 bars
            smallest_hi_lo = min(self.HiLo[-x] for x in range(1,7))
            
            # if the current high - low is smaller than the previous 6 bars, we have an NR7 bar
            if self.HiLo[0] < smallest_hi_lo:
                self.NR7[0] = 1
                
            else:
                self.NR7[0] = 0
    

    Strategy:

    class NR7_strategy(bt.Strategy):
        
        params = (('Gap',.01), ('Target',0.1),)
        
        lines = ("StopLoss", )
        
        def log(self, txt, dt=None):
            ''' Logging function for this strategy'''
            dt = dt or self.datas[0].datetime.datetime(0)
            print('%s, %s' % (dt.strftime("%Y-%m-%d %H:%M"), txt))        
        
        def __init__(self):
            
            self.NR7 = NR7_indicator(self.datas[0])
            self.SQNval = SQN(self.datas[0])
        
            # 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):
                
            if self.order:
                return
            
            # 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:
                
                # check if we had at least a x% gap at the open and an NR7 the previous day
                if (self.data.open[0]/self.data.close[-1]-1) >= self.params.Gap and self.NR7[-1] ==1:
                    self.order = self.buy()
                    self.log('BUY CREATE, %.2f' % self.data.open[0])
                    # Stoploss is placed at the low of the NR7
                    self.StopLoss = self.data.low[-1]
                    self.log('Stoploss, %.2f' % self.StopLoss)
                    self.Target = self.data.open[0]*(1+self.params.Target)  
                    self.log('Target, %.2f' % self.Target)
                    
                    
            else:
                if self.data.low[0] <= self.StopLoss:
                    self.log('StopLoss Hit, SELL CREATE, %.2f' % self.StopLoss)
                    self.order = self.sell()
                    
    
    
    
                elif self.data.high[0] >= self.Target:
                    self.order = self.sell()
                    self.log('Target Hit, SELL CREATE, %.2f' % self.Target)
                    
            
                        
    if __name__ == '__main__':
    
        cerebro = bt.Cerebro()
    
        # Add a strategy
        cerebro.addstrategy(NR7_strategy)
        
        # Create a Data Feed
        data = bt.feeds.PandasData(dataname=df)
        
        # Set our desired cash start
        cerebro.broker.setcash(100000.0)
        
        # Add the Data Feed to Cerebro
        cerebro.adddata(data)    
        
        # Add a FixedSize sizer according to the stake
        cerebro.addsizer(bt.sizers.PercentSizer, percents=20)
        
        # 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())
        
        # Plot the result
        cerebro.plot(style='Candlestick')
    

    I tried to add a limit order which is causing me an error. this is what I've gathered from the documentation so I must be interpreting something incorrectly

    self.buy(exectype=Order.Limit)
    

    Error: NameError: name 'Order' is not defined

    thanks



  • Right now you are making decision at the close of the bar with the gap, and buy at the open of the next bar. If you want to make decisions on the open when the actual gap happens, than there are two options in bt:

    imho cheat-on-open is simpler to implement and understand, but I've used both of them together, unfortunately don't remember the reason. :)

    Error: NameError: name 'Order' is not defined

    should be self.buy(exectype=bt.Order.Limit)



  • @ab_trader

    Right now you are making decision at the close of the bar with the gap, and buy at the open of the next bar.

    Yes, that's what I thought indeed, thanks.

    • cheat-on-open

    I read this as well in the documents and implemented it, it didn't make any change though.

    I just added this:

    cerebro = bt.Cerebro(cheat_on_open=True)
    

    I read the following in the documentation so I thought that's why it doesn't work as I'm working with gaps: Such a use case fails when the opening price gaps (up or down, depending on whether buy or sell is in effect)

    • BarReplayer_Open:

    would you mind giving an example here. I can't really see how to implement this. I've tried the following but it doesn't change anything

        data.addfilter(bt.filters.BarReplayer_Open)
    
    • should be self.buy(exectype=bt.Order.Limit):

    great, thanks, should've realized that!!

    below the start of the log btw. I'd have the same problem with the stoploss that I want to have it executed at the price I set the stoploss at and not the day after.

    Starting Portfolio Value: 100000.00
    2000-06-20 00:00, BUY CREATE, 3.52
    2000-06-20 00:00, Stoploss, 3.45
    2000-06-20 00:00, Target, 3.87
    2000-06-21 00:00, BUY EXECUTED, Price: 3.61, Cost: 19950.62, Comm 0.00
    2000-06-28 00:00, Target Hit, SELL CREATE, 3.87
    2000-06-29 00:00, SELL EXECUTED, Price: 3.79, Cost: 19950.62, Comm 0.00
    2000-06-29 00:00, OPERATION PROFIT, GROSS 1012.35, NET 1012.35
    2000-06-29 00:00, BUY CREATE, 3.79
    2000-06-29 00:00, Stoploss, 3.71
    2000-06-29 00:00, Target, 4.17
    2000-06-30 00:00, BUY EXECUTED, Price: 3.77, Cost: 20107.29, Comm 0.00
    2000-06-30 00:00, StopLoss Hit, SELL CREATE, 3.71
    2000-07-03 00:00, SELL EXECUTED, Price: 3.72, Cost: 20107.29, Comm 0.00
    2000-07-03 00:00, OPERATION PROFIT, GROSS -261.75, NET -261.75
    2000-07-03 00:00, BUY CREATE, 3.72
    2000-07-03 00:00, Stoploss, 3.65
    2000-07-03 00:00, Target, 4.10
    


  • @Jens-Halsberghe said in Trying to execute on the open, although the order executes the next day:

    I just added this:

    In order to use cheat-on-open strategy actions need to move to next_...() calls. Here I posted simple script which trades on the bar open prices, take a look. Maybe it can help to understand the idea.



  • @ab_trader thanks!! that seems to work.



  • I'm having more difficulties with this.

    I'll first explain what I'm trying to achieve:

    After the trading logic is satisfied, I'm setting my entry, stoploss and target

    scenario:

    1. set the entry for the current bar's high. if the next bar breaks the high, I want to buy (if it doesn't break, I cancel it or if equal highs, I have some different logic but I won't get into that now)
    2. set the stoploss for the low of the bar.
    3. set the target for 1.1 R:R

    The entry scenario works without any problem.
    the Stoploss and target scenario is an issue. to give an example where the order is placed on the current bar and where the next bar, it also hits the target:

    current bar is O:1.17168, H:1.17170, L:1.17163, C:1.17169
    next bar is: O:1.17169, H:1.17186, L:1.17169, C:1.17183

    in the log I find the following (when the buy is created, I'm also logging the SL and target values so we can compare):

    2020-08-12 08:30, BUY CREATE, 1.17170
    2020-08-12 08:30, SL: 1.17163, T: 1.17178 
    2020-08-12 08:31, BUY EXECUTED, Price: 1.17170
    2020-08-12 08:32, SELL EXECUTED, Price: 1.17183
    
    • the entry is executed correctly at the right price
    • the target was reached at the same bar, however it only executes the same bar and executes on the open.

    When reading the documentation, it's executing as expected as for a market order it reads: "A market order will be executed with the next available price. In backtesting it will be the opening price of the next bar"

    I still have a problem with building in my logic as I just described. I'm actually setting the orders on the bar I'm executing all my orders so in my mind, it would make sense that when my target or stoploss is hit, it should execute on the same bar.
    I must be completely overlooking something. do I have to combine next_open as well? is there a simpler solution and I'm overthinking something here? I've been searching on the forum but haven't seem to have found a relevant post to what I'm trying to achieve

    setting my entry, stoploss and target once the trade logic was met:

    self.order = self.buy(exectype=bt.Order.StopLimit, price=self.data.high[0],transmit=False)
    
    self.StopLoss = self.sell(price=self.data.low[0],exectype=bt.Order.Market, 
    transmit=False, size=self.order.size,parent=self.order)
    
    self.target = self.sell(price=(self.data.high[0]-self.data.low[0])*1.1+self.data.high[0], exectype=bt.Order.Market, 
    transmit=True, size=self.order.size,  parent=self.order)
    

    last thing, my stoploss and target orders are normally stop orders, however, something went wrong there as once the buy was executed, it would never sell after. I'm also not sure why that happens



  • After a lot of reading in the documentation I have come closer to what I'm trying to get to. I'm getting closer, still struggling with a few things. I'm adding the full code here

    class Test_Strategy(bt.Strategy):
    
        params = dict(
            pfast=10,  # period for the fast moving average
            pslow=30   # period for the slow moving average
        )
    
        
        def log(self, txt, dt=None):
            ''' Logging function for this strategy'''
            dt = dt or self.datas[0].datetime.datetime(0)
            print('%s, %s' % (dt.strftime("%Y-%m-%d %H:%M"), txt))
    
        def __init__(self):
            
            sma1 = bt.ind.SMA(period=self.p.pfast)  # fast moving average
            sma2 = bt.ind.SMA(period=self.p.pslow)  # slow moving average
            self.crossover = bt.ind.CrossOver(sma1, sma2)  # crossover signal
    
            # 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: %.5f, Cost: %.f, 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: %.5f, Cost: %.f, 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 %.5f, NET %.5f' %
                     (trade.pnl, trade.pnlcomm))
    
        def next(self):
    
            # Check if an order is pending ... if yes, we cannot send a 2nd one
            if self.order:
                if self.order.status == 2 and len(self) == self.bar_order_submitted + 1:
                    if self.data.high[0] == self.data.high[-1]: # current's bar high is equal to the previous bar's high
                        self.order = self.buy(exectype=bt.Order.StopLimit, price=self.data.high[0], transmit=False)
                        self.bar_order_submitted = len(self)
    
    
    
                        if self.data.low[0] > self.data.low[-1]: # if the current bar's low is higher, we adjust ourstoploss
                            self.StopLoss = self.sell(price=(self.data.low[0])
                            ,exectype=bt.Order.Stop,transmit=True, size=self.order.size,  parent=self.order)
                else:
                    self.broker.cancel(self.order)
                    self.log("order was cancelled")
                
    
            # Check if we are in the market
            if not self.position:
    
                # Not yet ... we MIGHT BUY if ...
                if self.crossover > 0:  # if fast crosses slow to the upside
    
                    self.order = self.buy(exectype=bt.Order.StopLimit, price=self.data.high[0], transmit=False)
                    self.StopLoss = self.sell(price=self.data.low[0],exectype=bt.Order.Market, 
                                            transmit=False, size=self.order.size,parent=self.order)
                    self.target = self.sell(price=(self.data.high[0]-self.data.low[0])*1.1+self.data.high[0], exectype=bt.Order.Market, 
                                            transmit=True, size=self.order.size,  parent=self.order)
    
                    self.bar_order_submitted = len(self)
                    
    
                    self.log('BUY CREATE, %.5f' % self.order.price)
    
                    self.log('SL: %.5f, T: %.5f' %(self.StopLoss.price,self.target.price))
                
    
    if __name__ == '__main__':
    
        cerebro = bt.Cerebro()
    
        # Add a strategy
        cerebro.addstrategy(Test_Strategy)
        
        # Create a Data Feed
        data = bt.feeds.PandasData(dataname=df2020)
        cerebro.adddata(data)
          
        # Add a FixedSize sizer according to the stake
        cerebro.addsizer(bt.sizers.PercentSizer, percents=5000)
        
        #Add Analyzer 
        cerebro.addanalyzer(bt.analyzers.PyFolio)
        
        cerebro.broker.setcommission(commission=0.0,leverage=500)
        
        # Set our desired cash start
        cerebro.broker.setcash(1000000.0)
        
        # Print out the starting conditions
        print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
        
        HG = cerebro.run()
        
        # Print out the final result
        print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    

    The first trade buys correctly, however it immediately sells after without having hit the target / nor the stoploss. I don't know why it just holds on but this seems to be a recurring issue.

    Starting Portfolio Value: 1000000.00
    2020-08-12 00:36, BUY CREATE, 1.17434
    2020-08-12 00:36, SL: 1.17402, T: 1.17469
    2020-08-12 00:37, BUY EXECUTED, Price: 1.17434, Cost: 50005962, Comm 0.00
    2020-08-12 00:38, SELL EXECUTED, Price: 1.17406, Cost: 50005962, Comm 0.00
    2020-08-12 00:38, Order Canceled/Margin/Rejected
    

    Normally, when the trade doesn't get triggered the next bar, it will cancel the order. this seems to work properly. however there are also situations like below:

    the order is created at 03:53.
    the bar at 03:54 has a low which is lower than the stoploss I set
    the bar at 03:55 has a high which exceeds the entry level.
    with this setup, somehow it seems to trigger me in a trade.
    I don't understand why. if the bar at 03:55 wouldn't have exceeded the entry level, it would have cancelled as normal.

    2020-08-12 03:53, BUY CREATE, 1.17352
    2020-08-12 03:53, SL: 1.17333, T: 1.17373
    2020-08-12 03:55, BUY EXECUTED, Price: 1.17352, Cost: 49405112, Comm 0.00
    2020-08-12 03:56, SELL EXECUTED, Price: 1.17361, Cost: 49405112, Comm 0.00
    2020-08-12 03:56, Order Canceled/Margin/Rejected
    

    last example:
    this trade gets triggered correctly. the bar at 06:29 which triggers the trade in also hits the target. it should buy and take profit the same bar but that doesn't seem to happen. can't figure out why either.

    2020-08-12 06:28, BUY CREATE, 1.17185
    2020-08-12 06:28, SL: 1.17171, T: 1.17200
    2020-08-12 06:29, BUY EXECUTED, Price: 1.17185, Cost: 49345663, Comm 0.00
    2020-08-12 06:30, SELL EXECUTED, Price: 1.17203, Cost: 49345663, Comm 0.00
    2020-08-12 06:30, Order Canceled/Margin/Rejected
    2020-08-12 06:30, OPERATION PROFIT, GROSS 7579.65552, NET 7579.65552
    

    greatly appreciate your help. I've been pretty stuck with this for the past weeks



  • A further update. I've simplified it further. the order is now being cancelled as it should. The execution is also fine bar one last problem. If the bar where the trade gets entered also hits the target, it doesn't execute the buy and sell on the same bar. I don't see how this can be achieved.

    class Order_testing(bt.Strategy):
    
        params = dict(
            pfast=10,  # period for the fast moving average
            pslow=30   # period for the slow moving average
        )
    
        
        def log(self, txt, dt=None):
            ''' Logging function for this strategy'''
            dt = dt or self.datas[0].datetime.datetime(0)
            print('%s, %s' % (dt.strftime("%Y-%m-%d %H:%M"), txt))
    
        def __init__(self):
            
            sma1 = bt.ind.SMA(period=self.p.pfast)  # fast moving average
            sma2 = bt.ind.SMA(period=self.p.pslow)  # slow moving average
            self.crossover = bt.ind.CrossOver(sma1, sma2)  # crossover signal
    
            # 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: %.5f, Cost: %.f, 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: %.5f, Cost: %.f, 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 %.5f, NET %.5f' %
                     (trade.pnl, trade.pnlcomm))
    
        def next(self):
    
            # Check if an order is pending ... if yes, we cannot send a 2nd one
            if self.order:
                if self.order.status == 2 and len(self) == self.bar_order_submitted + 1:
                    self.broker.cancel(self.order)
                    self.log("order was cancelled")
    
    
            # Check if we are in the market
            if not self.position:
    
                # Not yet ... we MIGHT BUY if ...
                if self.crossover > 0:  # if fast crosses slow to the upside
    
                    self.order = self.buy(exectype=bt.Order.StopLimit, price=self.data.high[0], transmit=False)
                    self.StopLoss = self.sell(price=self.data.low[0],exectype=bt.Order.Stop, 
                                            transmit=False, size=self.order.size,parent=self.order)
                    self.target = self.sell(price=(self.data.high[0]-self.data.low[0])*1.1+self.data.high[0], exectype=bt.Order.Limit, 
                                            transmit=True, size=self.order.size,  parent=self.order)
    
                    self.bar_order_submitted = len(self)
                    
    
                    self.log('BUY CREATE, %.5f' % self.order.price)
    
                    self.log('SL: %.5f, T: %.5f' %(self.StopLoss.price,self.target.price))
                              
    
    if __name__ == '__main__':
    
        cerebro = bt.Cerebro()
    
        # Add a strategy
        cerebro.addstrategy(Order_testing)
        
        # Create a Data Feed
        data = bt.feeds.PandasData(dataname=df2020)
        one_minute = cerebro.resampledata(data, timeframe=bt.TimeFrame.Minutes, compression=1)
    #     cerebro.adddata(data)
          
        # Add a FixedSize sizer according to the stake
        cerebro.addsizer(bt.sizers.PercentSizer, percents=5000)
        
        #Add Analyzer 
        cerebro.addanalyzer(bt.analyzers.PyFolio)
        
        cerebro.broker.setcommission(commission=0.0,leverage=500)
        
        # Set our desired cash start
        cerebro.broker.setcash(1000000.0)
        
        # Print out the starting conditions
        print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
        
        HG = cerebro.run()
        
        # Print out the final result
        print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    

    below is a copy of the log again. It should sell at 06:29.

    2020-08-12 06:28, BUY CREATE, 1.17185
    2020-08-12 06:28, SL: 1.17171, T: 1.17200
    2020-08-12 06:29, BUY EXECUTED, Price: 1.17185, Cost: 49324578, Comm 0.00
    2020-08-12 06:30, SELL EXECUTED, Price: 1.17203, Cost: 49324578, Comm 0.00
    2020-08-12 06:30, Order Canceled/Margin/Rejected
    2020-08-12 06:30, OPERATION PROFIT, GROSS 7576.41679, NET 7576.41679
    

Log in to reply
 

});