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 theopen
of the next bar. If you want to make decisions on theopen
when the actual gap happens, than there are two options inbt
:cheat-on-open
- Docs - Broker - Cheat-on-open- data feed filter BarReplayer_Open
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)
-
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 tonext_...()
calls. Here I posted simple script which trades on the baropen
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:
- 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)
- set the stoploss for the low of the bar.
- 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.17183in 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 achievesetting 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