Navigation

    Backtrader Community

    • Register
    • 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/

    Help with Simple BBand Strategy

    Indicators/Strategies/Analyzers
    2
    7
    2521
    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.
    • X
      xisisu last edited by

      Hi,

      I am trying to work on a simple Bband strategy, where when close up cross the bband bottom line, I buy; when close down cross the bband top line, I sell.

      Here is my code:

      class BBand_CrossOver(bt.Strategy):
        params = (('period', 20),)
      
        def __init__(self):
          self.bband = bt.indicators.BollingerBands(self.data0, period=self.params.period)
          self.buysig = bt.indicators.CrossOver(self.data0, self.bband.lines.bot)
          self.sellsig = bt.indicators.CrossOver(self.data0, self.bband.lines.top)
      
        def next(self):
          if self.position.size:
            if self.sellsig < 0:
              self.sell()
      
          elif self.buysig > 0:
            self.buy()
      

      I tried symbol 'BABA' on it for 2016 data, code:

        cerebro = bt.Cerebro()
        data = bt.feeds.YahooFinanceData(dataname='BABA', fromdate=datetime.datetime(2016, 1, 1),
                                         todate=bt.datetime.datetime(2016, 12, 31), timeframe=bt.TimeFrame.Days,
                                         compression=1)
        cerebro.adddata(data)
        cerebro.addstrategy(BBand_CrossOver, period=20)
      
        cerebro.broker.setcash(10000.0)
        cerebro.addsizer(bt.sizers.AllInSizer)
      
        cerebro.addwriter(bt.WriterFile, csv=True, out='test.csv')
      
        cerebro.run(optreturn=False)
        cerebro.plot()
      

      Interestingly, it did execute some orders, but it did not trigger on all of them.
      alt text

      link: https://www.dropbox.com/s/ysrsbwzde4f00eh/BABA.png?dl=0

      You can see it triggered the buy order around Feb 5th, and Sell order around March 20th, which are expected.

      However, around June 28th, there should be a up-cross. but the buy order is not triggered.

      Is there a good way to debug this?
      Can I output the self.bband.lines and the signals in the writer as well?

      Thanks very much!

      P 1 Reply Last reply Reply Quote 0
      • P
        Paska Houso @xisisu last edited by Paska Houso

        @xisisu said in Help with Simple BBand Strategy:

          cerebro.broker.setcash(10000.0)
          cerebro.addsizer(bt.sizers.AllInSizer)
        

        Your orders are getting for sure rejected due to the AllIn strategy. There is plenty of literature in the community itself.

        • Your strategy sees the signal and issues the order. The only available price for size calculation is the current close (to avoid the lookahead bias which was a question yesterday)
        • The next open price is greater (opening gap) than the close
        • Because the order is sent as a size (like in a normal broker), the current size * open > size * close and there is not enough money in your account to execute the operation

        See (quick googling, I know there are more because I read them)

        • https://community.backtrader.com/topic/644/order-submission-execution/
        • https://community.backtrader.com/topic/370/unexpected-additional-orders-created-rejected/

        Your alternatives:

        • cheat-on-open, but you cannot use the sizer, because the sizer is not designed to cheat. You have to make size calculation yourself

        • https://www.backtrader.com/blog/posts/2017-05-01-cheat-on-open/cheat-on-open.html

        I would personally avoid cheating but ...

        X 2 Replies Last reply Reply Quote 2
        • X
          xisisu @Paska Houso last edited by

          @Paska-Houso

          Thank you so much for the detailed reply!

          I have modified my code with the cheat_on_open option, but it seems using the current open price in next_open(), not the next day open price.

          class BBand_CrossOver(bt.Strategy):
            params = (('period', 20),)
          
            def log(self, txt, dt=None):
              dt = dt or self.datas[0].datetime.date(0)
              print('{}, {}'.format(dt.isoformat(), txt))
          
            def __init__(self):
              self.dataclose = self.data0.close
              self.dataopen = self.data0.open
          
              self.bband = bt.indicators.BollingerBands(self.data0, period=self.params.period)
              self.buysig = bt.indicators.CrossOver(self.data0, self.bband.lines.bot)
              self.sellsig = bt.indicators.CrossOver(self.data0, self.bband.lines.top)
          
            def next_open(self):
              self.log('Close: %.2f, Open: %.2f' % (self.dataclose[0], self.dataopen[0]))
          
              if self.position.size:
                if self.sellsig < 0:
                  self.log('SELL CREATE, size {}'.format(self.position.size))
                  self.sell(size=self.position.size)
          
              elif self.buysig > 0:
                size = int(self.broker.getcash() / self.data0.open)
                self.log('BUY CREATE, cash {}, size {}, open {}, close {}'.format(self.broker.getcash(), size, self.dataopen[0], self.dataclose[0]))
                self.buy(size=size)
          
            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 enougth cash
              if order.status in [order.Completed]:
                if order.isbuy():
                  self.log('BUY EXECUTED, price {}, cost {}, comm {}'.format(order.executed.price, order.executed.value,
                                                                             order.executed.comm))
                elif order.issell():
                  self.log('SELL EXECUTED, price {}, cost {}, comm {}'.format(order.executed.price, order.executed.value,
                                                                              order.executed.comm))
              elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                self.log('Order Canceled/Margin/Rejected')
          
              # Write down: no pending order
              self.order = None
          
            def notify_trade(self, trade):
              if not trade.isclosed:
                return
              self.log('OPERATION PROFIT, GROSS {}, NET {}'.format(trade.pnl, trade.pnlcomm))
          

          And here is the main program:

            cerebro = bt.Cerebro(cheat_on_open=True)
            data = bt.feeds.YahooFinanceData(dataname='BABA', fromdate=datetime.datetime(2016, 1, 1),
                                             todate=bt.datetime.datetime(2016, 12, 31), timeframe=bt.TimeFrame.Days,
                                             compression=1)
            cerebro.adddata(data)
            cerebro.addstrategy(BBand_CrossOver, period=20)
          
            cerebro.broker.setcash(10000.0)
            # cerebro.addsizer(bt.sizers.AllInSizer)
            # cerebro.broker.setcommission(commission=0.001)
          
            cerebro.addwriter(bt.WriterFile, csv=True, out='test.csv')
          
            print('start: {}'.format(cerebro.broker.getvalue()))
            cerebro.run(optreturn=False)
            print('finish: {}'.format(cerebro.broker.getvalue()))
            cerebro.plot()
          

          To be more specific, in the output, I have:

          2016-06-29, Close: 78.04, Open: 76.98
          2016-06-29, BUY CREATE, cash 12071.300000000001, size 156, open 76.98, close 78.04
          2016-06-29, Order Canceled/Margin/Rejected
          2016-06-30, Close: 79.53, Open: 78.31
          
          • if we use close price on 0629, size is 12071.3 / 78.04 = 154
          • if we use open price on 0629, size is 12071.3 / 76.98 = 156
          • if we use open price on 0630, size is 12071.3 / 78.31 = 154

          Seems here it is using the open price on current day, not next day.

          Any suggestions?

          1 Reply Last reply Reply Quote 0
          • X
            xisisu last edited by

            Another general question:

            If we know that the AllInSizer have this price gap problem, and we cannot use it with the cheat-on-open option, what would be the correct use case for the AllInSizer?

            Thanks.

            1 Reply Last reply Reply Quote 0
            • X
              xisisu @Paska Houso last edited by

              @Paska-Houso

              Digging around, I saw there is also the option of Order.Partial, link: https://www.backtrader.com/docu/order.html

              However, seems it is not triggered automatically.

              is there a way to

              • still use the AllInSizer
              • if price gap happens, partially execute the order, and cancel the rest that cannot be filled?

              Thanks.

              1 Reply Last reply Reply Quote 0
              • P
                Paska Houso last edited by Paska Houso

                @xisisu said in Help with Simple BBand Strategy:

                However, seems it is not triggered automatically.
                is there a way to

                • still use the AllInSizer
                • if price gap happens, partially execute the order, and cancel the rest that cannot be filled?

                I believe you misunderstand Order.Partial. It's not an option to execute partially. It's telling you that your order executed partially (probably because the volume at your price was not enough to fill your entire request)

                But your order CANNOT be partially executed because it's not even accepted.

                @xisisu said in Help with Simple BBand Strategy:

                • if we use close price on 0629, size is 12071.3 / 78.04 = 154
                • if we use open price on 0629, size is 12071.3 / 76.98 = 156
                • if we use open price on 0630, size is 12071.3 / 78.31 = 154

                Let me formulate a theory:

                • The close price on 2016-06-29 is larger than the open.
                • Your size has been calculated against the opening price
                • And here comes the theory ... the broker is using the close price to check the validity.
                • And your order is rejected because it doesn't pass the submission criteria

                (The theory would have to be validated by looking at the sources)

                Deactivate order submission checking: https://www.backtrader.com/docu/broker.html

                • checksubmit (there is a method to do it, find it in the doc)
                X 1 Reply Last reply Reply Quote 1
                • X
                  xisisu @Paska Houso last edited by

                  @Paska-Houso

                  Thanks for the detailed reply!

                    cerebro.broker.set_checksubmit(checksubmit=False)
                  

                  Fixed the problem. Thank you so much! :)

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