Placing a stop order
-
I am a bit confused on how I can place my stop order. In my code below, my buy will get triggered upon meeting a certain condition. I want to also place a stop order once that happens. What I know so far is that I can define the exectype as bt.Order.Stop to place the stop order.
Not sure if that is to be done inside self.buy(size = x, exectype = y) etc. From what I understand the buy order itself cannot be a stop order, it needs to be a different order where in I can specify the exec type.
I have gone through the strategy documentation that talks about the different order types. I have also gone through a few of the questions previously asked around this topic but haven't got a simple answer to this yet.
Thanks in advance.
class Strats(bt.Strategy): lines = ('sma', ) params = (('sma', 20), ('period', 10)) def __init__(self): self.dataclose = self.datas[0].close self.sma = bt.indicators.SMA(self.datas[0], period=self.p.sma) def log(self, txt, dt=None): dt = self.datas[0].datetime.date() print(f'{dt}, {txt}') def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: return if order.status in [order.Completed]: if order.isbuy(): self.log(f'BUY EXECUTED: Price: {order.executed.price}, Cost: {order.executed.value}, Comms: {order.executed.comm}') self.log(f'Account Balance: {cerebro.broker.getcash()}') self.buyprice = order.executed.price self.totalcost = order.executed.value elif order.issell(): self.log(f'SELL EXECUTED: Price: {order.executed.price}, Cost: {order.executed.value}, Comms: {order.executed.comm}') elif order.status in [order.Rejected, order.Margin, order.Cancelled]: self.log('Order rejected/cancelled by broker or insufficient margin') self.order = None def notify_trade(self, trade): if trade.isopen: return else: self.log(f'OPERATION PROFIT: GROSS {trade.pnl}, NET {trade.pnlcomm}, Trade PnL: {trade.pnlcomm/self.totalcost}') self.log(f'Updated Account Balance: {cerebro.broker.getcash()}') def next(self): self.log(f'HDFC Close: {self.dataclose[0]}') if self.order: return if not self.position: if self.sma[0] > self.dataclose[0]: self.log(f'Share Bought: {self.dataclose[0]}') self.order = self.buy(size = 50, exectype = bt.Order.Stop, ) else: if self.dataclose[0] > (self.buyprice + (self.buyprice * 0.05)): self.log(f'Share Sell: {self.dataclose[0]}') self.order = self.sell(size = 50) cerebro = bt.Cerebro() cerebro.broker.set_cash(100000) print(f'Starting Portfolio Value: {cerebro.broker.getvalue()}') data = bt.feeds.PandasData(dataname=hdfc, datetime=None, open=-1, high=-1, low=-1, close=-1, volume=-1) cerebro.adddata(data) cerebro.addobserver(bt.observers.TimeReturn, timeframe = bt.TimeFrame.NoTimeFrame) cerebro.addstrategy(Strats) cerebro.broker.setcommission(commission=0.001) cerebro.run() print(f'Final Portfolio Value: {cerebro.broker.getvalue()}')
-
@vypy1 Issue the stop sell order directly from the notify order, not the strategy.
-
@run-out Thanks I will check this out.
-
@run-out I checked this out, turns out that I was actually looking for bracket order. I think I have been able to figure how to implement that.
-
@run-out so I just ran the bracket order code and I realised that the stop and target orders are being created based on the price at which the buy order got created and not the price at which the buy order got executed.
Ideally the execution price should decide what stop and target orders to place. I kept reference to the executed price using self.execprice variable inside notify order, but I will not get the exec price till the time the main bracket order gets executed.
I am unsure of how to approach this, how do you suggest I approach this?
import datetime import backtrader as bt class St(bt.Strategy): params = dict( ma=bt.ind.SMA, p1=5, p2=15, limit=0.005, limdays=3, limdays2=1000, stop = 0.98, tgt = 1.05 ) def __init__(self): self.dataclose = self.datas[0].close self.dataopen = self.datas[0].open ma1 = self.p.ma(period=self.p.p1) ma2 = self.p.ma(period=self.p.p2) self.cross = bt.ind.CrossOver(ma1, ma2) self.orefs = list() print(f'Order Numbers are : {self.orefs}') self.execprice = None def log(self, txt, dt=None): dt = self.datas[0].datetime.date() print(f'{dt}: {txt}') def notify_order(self, order): print(f'{self.data.datetime.date()}: Close: {self.dataclose[0]}, Order ref: {order.ref} / Type {"Buy" * order.isbuy() or "Sell"} / Status {order.getstatusname()}') if order.status == order.Completed: self.execprice = order.executed.price self.log(f'Executed price: {self.execprice}') if not order.alive() and order.ref in self.orefs: self.orefs.remove(order.ref) print(f'Order Numbers are {self.orefs}') def next(self): self.log(f'Close: {self.dataclose[0]}') if self.orefs: return # pending orders do nothing if not self.position: if self.cross > 0.0: # crossing up p1 = self.dataclose[0] p2 = p1 - 0.02 * self.dataclose[0] # Stop and Tgt prices should be set from the executed price p3 = p1 + 0.02 * self.dataclose[0] # not the submitted price os = self.buy_bracket(price=p1, stopprice=p2, limitprice=p3, exectype = bt.Order.Market) self.log(f'Buy at {p1}, Stop sell at {p2}, Tgt sell at {p3}') self.orefs = [o.ref for o in os] print(f'Order Numbers are : {self.orefs}') cerebro = bt.Cerebro() cerebro.broker.set_cash(100000) print(f'Cash Available: {cerebro.broker.get_cash()}') data = bt.feeds.PandasData(dataname=hdfc, datetime=None, open=-1, high=-1, low=-1, close=-1, volume=-1) cerebro.adddata(data) cerebro.addsizer(bt.sizers.FixedSize, stake = 50) cerebro.addstrategy(St) cerebro.run() print(f'Final Value: {cerebro.broker.get_cash()}')
-
@run-out What I have done is instead of referencing to the current close price, I have referenced to the next bars opening price to get the price at which order is executed instead of the price at which it is created.
My code, does it seem legit?
import datetime import backtrader as bt class St(bt.Strategy): params = dict( ma=bt.ind.SMA, p1=5, p2=15, limit=0.005, limdays=3, limdays2=1000, stop = 0.98, tgt = 1.05 ) def __init__(self): self.dataclose = self.datas[0].close self.dataopen = self.datas[0].open ma1 = self.p.ma(period=self.p.p1) ma2 = self.p.ma(period=self.p.p2) self.cross = bt.ind.CrossOver(ma1, ma2) self.orefs = list() print(f'Order Numbers are : {self.orefs}') self.execprice = None def log(self, txt, dt=None): dt = self.datas[0].datetime.date() print(f'{dt}: {txt}') def notify_order(self, order): print(f'{self.data.datetime.date()}: Close: {self.dataclose[0]}, Order ref: {order.ref} / Type {"Buy" * order.isbuy() or "Sell"} / Status {order.getstatusname()}') if order.status == order.Completed: self.execprice = order.executed.price self.log(f'Executed price: {self.execprice}') if not order.alive() and order.ref in self.orefs: self.orefs.remove(order.ref) print(f'Order Numbers are {self.orefs}') def next(self): self.log(f'Close: {self.dataclose[0]}') if self.orefs: return # pending orders do nothing if not self.position: if self.cross > 0.0: # crossing up p1 = self.dataopen[1] p2 = p1 - 0.02 * self.dataopen[1] # Stop and Tgt prices should be set from the executed price p3 = p1 + 0.02 * self.dataopen[1] # not the submitted price os = self.buy_bracket(price=p1, stopprice=p2, limitprice=p3, exectype = bt.Order.Market) self.log(f'Buy at {p1}, Stop sell at {p2}, Tgt sell at {p3}') self.orefs = [o.ref for o in os] print(f'Order Numbers are : {self.orefs}') cerebro = bt.Cerebro() cerebro.broker.set_cash(100000) print(f'Cash Available: {cerebro.broker.get_cash()}') data = bt.feeds.PandasData(dataname=hdfc, datetime=None, open=-1, high=-1, low=-1, close=-1, volume=-1) cerebro.adddata(data) cerebro.addsizer(bt.sizers.FixedSize, stake = 50) cerebro.addstrategy(St) cerebro.run() print(f'Final Value: {cerebro.broker.get_cash()}')
-
@vypy1 A good way to handle this is not with bracket orders. Just execute your buy order by itself, without the stop.
Then in the
notify_order
method, you insert your stop order in underif status is completed
conditional.if order.status in [order.Completed]: self.stopOrder = self.close( price=order.executed.price, exectype=bt.Order.StopTrail, trailpercent=self.params.stopLoss, )
-
@run-out Thanks, let me give this a shot.
-
@run-out Hey, thanks, I was able to successfully place a stop order.
I also then used the same method to place a limit sell order which is basically a target order.
I realised that in this case execution of stop or target order did not automatically cancel the other, so I though of using the order.cancel. But I think for that I need ref ID.
When I use self.stoporder.ref to get ref ID for the stop order, it just says its a None type object. Same thing with self.tgtorder.ref.
Why does it say that? when I just print self.stoporder I can see the data and I can also see the ref ID. My code is below:
# Rough Work class Strats(bt.Strategy): lines = ('sma', ) params = (('sma', 10), ('fastma', 20), ('stop_loss', 0.02), ('tgt', 0.05)) def __init__(self): self.dataclose = self.datas[0].close self.slowsma = bt.indicators.SMA(self.datas[0], period=self.p.sma) self.fastsma = bt.indicators.SMA(self.datas[0], period=self.p.fastma) self.cross = bt.indicators.CrossOver(self.fastsma, self.slowsma) self.order = None self.stoporder = None self.tgtorder = None self.orefs = [] def log(self, txt, dt=None): dt = self.datas[0].datetime.date() print(f'{dt}, {txt}') def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: self.log(f'Status: {order.getstatusname()}') if order.status in [order.Completed]: self.log(f'BUY EXECUTED: Price: {order.executed.price}, Cost: {order.executed.value}, Comms: {order.executed.comm}') self.log(f'Account Balance: {cerebro.broker.getcash()}') self.buyprice = order.executed.price self.totalcost = order.executed.value self.stoporder = self.close(exectype=bt.Order.StopTrail, trailpercent = 0.05) self.tgtorder = self.close(exectype=bt.Order.Limit, price = self.buyprice * 1.05) # If statement checking if either stop order or tgtorder is dead # If one of them is dead means the order is executed # Cancel the other order # self.log(f'Order Numbers are: {order.ref}') self.log(f'STOP ORDER AT: {self.stoporder}') self.log(f'Order ID: {self.stopoder.ref}') self.log(f'TARGET ORDER AT: {self.tgtorder}') # self.log(f'TARGET EXECUTED AT: {order.executed.price}') # elif order.issell(): # self.log(f'SELL EXECUTED: LPrice: {order.executed.price}, Cost: {order.executed.value}, Comms: {order.executed.comm}') elif order.status in [order.Rejected, order.Margin, order.Cancelled]: self.log('order rejected/cancelled by broker or insufficient margin') self.order = None def notify_trade(self, trade): if trade.isopen: return else: self.log(f'OPERATION PROFIT: GROSS {trade.pnl}, NET {trade.pnlcomm}, Trade PnL: {trade.pnlcomm/self.totalcost}') self.log(f'Updated Account Balance: {cerebro.broker.getcash()}') def next(self): self.log(f'Open: {self.data.open[0]}, High: {self.data.high[0]}, Low: {self.data.low[0]}, Close: {self.dataclose[0]}') # if self.dataclose[0] > self.dataclose[-1]: # self.log(f'STOP MOVED TO: {self.dataclose[0] * 0.95}') if self.order: return if not self.position: if self.cross > 0: self.order = self.buy() self.log(f'BUY CREATED: {self.dataclose[0]}') # else: # pass cerebro = bt.Cerebro() cerebro.broker.set_cash(100000) print(f'Starting Portfolio Value: {cerebro.broker.getvalue()}') data = bt.feeds.PandasData(dataname=hdfc, datetime=None, open=-1, high=-1, low=-1, close=-1, volume=-1) cerebro.adddata(data) # cerebro.addobserver(bt.observers.TimeReturn, timeframe = bt.TimeFrame.NoTimeFrame) cerebro.addstrategy(Strats) cerebro.addsizer(bt.sizers.FixedSize, stake=50) cerebro.broker.setcommission(commission=0.001) cerebro.run() print(f'Final Portfolio Value: {cerebro.broker.getvalue()}')
-
-
@run-out Hey, is there any way I can keep track of OCO order that I trigger from the notify order method instead of next?
Basically I want to issue buy order from next after some conditions are met, keep track of its ref ID and then once in a position trigger an OCO order out of which one will be a stop order and the other will be stoptrail order and keep track of both the orders ref ID. How will I be able to do that?
-
@run-out Thanks!