Entering and taking profit on the same bar
for simple MA crossover strategy, when an order is created, if the next bar triggers the entry but simultaneously hits the target, it will only execute the entry and then exit the bar after that on the open. I can't figure it out. I've been trying different order types but I haven't been able to change the behavior. I've experimented with cheat on open but that didn't get me anywhere
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.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, transmit=False) self.StopLoss = self.sell(price=self.data.low,exectype=bt.Order.Stop, transmit=False, size=self.order.size,parent=self.order) self.target = self.sell(price=(self.data.high-self.data.low)*1.1+self.data.high, 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) # Print out the starting conditions print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) cerebro.run() # Print out the final result print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
log for reference. it should both execute the buy and sell order 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: 1, Comm 0.00 2020-08-12 06:30, SELL EXECUTED, Price: 1.17203, Cost: 1, Comm 0.00 2020-08-12 06:30, Order Canceled/Margin/Rejected 2020-08-12 06:30, OPERATION PROFIT, GROSS 0.00018, NET 0.00018
I am not really sure with this. Maybe the
quicknotifyparam could help you with that problem.
@dasch much appreciate the quick response. I've tried it but it doesn't work. I haven't found any posts on the forum where it was mentioned either unfortunately.
could you post the data file to play with that problem a bit?
I would but I can't see how to add an attachment? if not possible, I'll add it to dropbox and then send it that way
you can send it directly to my email, which is available in my profile
run-out last edited by
if the next bar triggers the entry but simultaneously hits the target, it will only execute the entry and then exit the bar after that on the open.
This is default behaviour since it is impossible to know for sure what order the triggers for enter and sale happen when using just one bar of data. Perhaps the enter price happens just before the close but the stop price happens near the open. It's problematic. If you wish to have greater granularity you may wish to consider using a smaller time frame, or perhaps replay.
@run-out you are right. we went through this yesterday via email. The result was like you explained. The child order gets activated after the parent order gets executed and will be checked in next cycle.
One way to manually allow executing on the same bar is to activate the child order by hand.
after the order was created:
Which is not that good idea, since then you don't know, if the parent order was executed, too.
I agree and I understand the logic. I can't replay but also, it's useful to use target.activate() and it can be used to do only in certain scenarios where it looks most likely the target would have been hit. for example if the price never even reached the stoploss but got to the entry and then the target, it's perfectly fine to use it. in case of a big outside bar which would hit all three entry, target and stoploss there would be doubt but it's more an exceptional case.
thanks again for your help Dasch