Backtrader Community

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

    Placing a stop order

    General Code/Help
    2
    12
    1248
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • V
      Vypy1 last edited by

      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()}')
      
      run-out 1 Reply Last reply Reply Quote 0
      • run-out
        run-out @Vypy1 last edited by

        @vypy1 Issue the stop sell order directly from the notify order, not the strategy.

        RunBacktest.com

        V 4 Replies Last reply Reply Quote 0
        • V
          Vypy1 @run-out last edited by

          @run-out Thanks I will check this out.

          1 Reply Last reply Reply Quote 0
          • V
            Vypy1 @run-out last edited by

            @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.

            1 Reply Last reply Reply Quote 1
            • V
              Vypy1 @run-out last edited by

              @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()}')
              
              1 Reply Last reply Reply Quote 0
              • V
                Vypy1 @run-out last edited by

                @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()}')
                
                run-out 1 Reply Last reply Reply Quote 0
                • run-out
                  run-out @Vypy1 last edited by

                  @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 under if 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,
                      )
                  

                  RunBacktest.com

                  V 2 Replies Last reply Reply Quote 0
                  • V
                    Vypy1 @run-out last edited by

                    @run-out Thanks, let me give this a shot.

                    1 Reply Last reply Reply Quote 0
                    • V
                      Vypy1 @run-out last edited by

                      @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 1 Reply Last reply Reply Quote 0
                      • run-out
                        run-out @Vypy1 last edited by

                        @vypy1 Try using OCO

                        RunBacktest.com

                        V 1 Reply Last reply Reply Quote 0
                        • V
                          Vypy1 @run-out last edited by

                          @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?

                          1 Reply Last reply Reply Quote 0
                          • V
                            Vypy1 last edited by

                            @run-out Thanks!

                            1 Reply Last reply Reply Quote 0
                            • 1 / 1
                            • First post
                              Last post
                            Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors