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/

    BBand Strategy

    Indicators/Strategies/Analyzers
    7
    15
    13813
    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.
    • tim3lord
      tim3lord last edited by backtrader

      Hello, I am excited to see that Backtrader now has a community section and I want to share a simple strategy with you all. This strategy uses Backtrader's BBand indicator and buys after the market dips into the lower band and sells on the moving average after the market hits the top band. This works great in sideways/bull markets. The idea is to buy during a low period and sell if the market dips below a moving average. Also note I am new to algotrading and programming in general so don't laugh to hard at this idea/strategy

      Example

      I actually use this strategy in the Bitcoin world and have had great results trading it live in the past 3 months. I use Tradewave to run it live with a small porting of my holdings.

      Tradewave

      You can use talib to create a BBands in Tradewave or you can use a simple math formula to recreate the bbands used in Backtrader....

      std = data(interval=INTERVAL).btc_usd.std(SETTING1)
      
      ma = data(interval=INTERVAL).btc_usd.ma(SETTING1)  
      
      upperband = ma + (Decimal(2) * std)
      
      lowerband = ma - (Decimal(2) * std)
      

      Note, I changed a lot of the code to work better such as not trading in low volatile markets, using a stop when the market turns and other things you can use to help make better, safer decisions. 😉

      So here is the code you can copy and paste....

      import datetime  # For datetime objects
      import os.path  # To manage paths
      import sys  # To find out the script name (in argv[0])
      
      # Import the backtrader platform
      import backtrader as bt
      
      # Create a Stratey
      class TestStrategy(bt.Strategy):
          params = (('BBandsperiod', 20),)
      
          def log(self, txt, dt=None):
              ''' Logging function fot this strategy'''
              dt = dt or self.datas[0].datetime.date(0)
              print('%s, %s' % (dt.isoformat(), txt))
      
          def __init__(self):
              # Keep a reference to the "close" line in the data[0] dataseries
              self.dataclose = self.datas[0].close
      
              # To keep track of pending orders and buy price/commission
              self.order = None
              self.buyprice = None
              self.buycomm = None
              self.redline = None
              self.blueline = None
      
              # Add a BBand indicator
              self.bband = bt.indicators.BBands(self.datas[0], period=self.params.BBandsperiod)
      
          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, order.Canceled, order.Margin]:
                  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)
      
              # Write down: no pending order
              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):
              # Simply log the closing price of the series from the reference
              self.log('Close, %.2f' % self.dataclose[0])
      
              # Check if an order is pending ... if yes, we cannot send a 2nd one
              if self.order:
                  return
      
              if self.dataclose < self.bband.lines.bot and not self.position:
              	self.redline = True
      
              if self.dataclose > self.bband.lines.top and self.position:
              	self.blueline = True
      
              if self.dataclose > self.bband.lines.mid and not self.position and self.redline:        	
              	# BUY, BUY, BUY!!! (with all possible default parameters)
                  self.log('BUY CREATE, %.2f' % self.dataclose[0])
                  # Keep track of the created order to avoid a 2nd order
                  self.order = self.buy()
      
              if self.dataclose > self.bband.lines.top and not self.position:
                  # BUY, BUY, BUY!!! (with all possible default parameters)
                  self.log('BUY CREATE, %.2f' % self.dataclose[0])
                  # Keep track of the created order to avoid a 2nd order
                  self.order = self.buy()
      
              if self.dataclose < self.bband.lines.mid and self.position and self.blueline:
                  # SELL, SELL, SELL!!! (with all possible default parameters)
                  self.log('SELL CREATE, %.2f' % self.dataclose[0])
                  self.blueline = False
                  self.redline = False
                  # Keep track of the created order to avoid a 2nd order
                  self.order = self.sell()
      
      if __name__ == '__main__':
      # Create a cerebro entity
      cerebro = bt.Cerebro()
      
      # Add a strategy
      cerebro.addstrategy(TestStrategy)
      
      # Datas are in a subfolder of the samples. Need to find where the script is
      # because it could have been called from anywhere
      modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
      datapath = os.path.join(modpath, 'TSLA-USD.csv')
      
      # Create a Data Feed
      data = bt.feeds.GenericCSVData(
          dataname=datapath,
          # Do not pass values before this date
          fromdate=datetime.datetime(2008, 4, 4),
          # Do not pass values before this date
          todate=datetime.datetime(2016, 12, 2),
      
      	nullvalue=0.0,
      
      	dtformat=('%m/%d/%Y'),
      
      	datetime=0,
      	high=2,
      	low=3,
      	open=1,
      	close=4,
      	volume=5,
      	openinterest=-1)
      
      # Add the Data Feed to Cerebro
      cerebro.adddata(data)
      
      # Set our desired cash start
      cerebro.broker.setcash(10000.0)
      
      # Add a FixedSize sizer according to the stake
      cerebro.addsizer(bt.sizers.FixedSize, stake=5)
      
      # Set the commission
      cerebro.broker.setcommission(commission=0.002)
      
      # 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()
      

      Also, you can just use this script and paste it into your browsers to grab quick market cvs data from yahoo.

      http://ichart.finance.yahoo.com/table.csv?s=ENTERSTOCKHERE
      

      I will be posting more simple examples and hope others do as well. I found backtesting ideas with Backtrader can be quick and when compared to using online backtesters and more powerful. Using the right parameters without oversampling is still a difficult thing to master. Hope to keep learning and trying new things!

      1 Reply Last reply Reply Quote 4
      • B
        backtrader administrators last edited by

        Thx for sharing. Really impressive.

        1 Reply Last reply Reply Quote 1
        • A
          ab_trader last edited by

          Thanks for the post. You used period of 20 for Bband indicator. How did you come to this value?

          • If my answer helped, hit reputation up arrow at lower right corner of the post.
          • Python Debugging With Pdb
          • New to python and bt - check this out
          tim3lord 1 Reply Last reply Reply Quote 0
          • tim3lord
            tim3lord @ab_trader last edited by

            @ab_trader I just wanted to take the past 30 days of data and use that to find the average low and highs for the BBand and trade on 1 day ticks. When backtesting, 20 days of data seemed to produce better results. I also found this to be true on Quantopian when I added the strategy there and tried many different stocks on 1 day ticks with 20 days of data.

            1 Reply Last reply Reply Quote 0
            • U
              Usct last edited by Usct

              Thanks for sharing,
              I tried executing this, the Buy orders are getting created but not executed.
              The log says
              2016-09-08, Close, 8952.50
              2016-09-08, BUY CREATE, 8952.50
              2016-09-08, BUY CREATE, 8952.50
              2016-09-09, BUY EXECUTED, Price: 0.00, Cost: 0.00, Comm 0.00
              2016-09-09, BUY EXECUTED, Price: 0.00, Cost: 0.00, Comm 0.00
              2016-09-09, Close, 8866.70

              0_1483957434236_upload-a56ff59d-2c7b-47e6-b68e-282573b88df6
              Any idea why this might be happening

              1 Reply Last reply Reply Quote 0
              • B
                backtrader administrators last edited by backtrader

                The code from @tim3lord will print EXECUTED even when the order has been canceled (or rejected because it cannot meet cash requirements). See the following lines

                        if order.status in [order.Completed, order.Canceled, order.Margin]:
                            if order.isbuy():
                                self.log(
                                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                                    (order.executed.price,
                                     order.executed.value,
                                     order.executed.comm))
                

                Of course this is purely cosmetic.

                The code also contains

                # Set our desired cash start
                cerebro.broker.setcash(10000.0)
                
                # Add a FixedSize sizer according to the stake
                cerebro.addsizer(bt.sizers.FixedSize, stake=5)
                

                Your asset is in the 8000s range and for a stocklike behavior you would need at least 5 * 8000 = 40,000 monetary units.

                The orders are obviously being rejected due to lack of enough cash.

                1 Reply Last reply Reply Quote 0
                • U
                  Usct last edited by

                  Awesome, I changed the setcash parameter and it works now, in fact showing some profits by changing the BB parameter.
                  I will be sharing something soon, still trying to get my strategy coded in backtrader framework (as I am new to python)

                  1 Reply Last reply Reply Quote 0
                  • K
                    kozlone last edited by

                    Thanks for sharing but I'm confused with a following.

                    if self.dataclose > self.bband.lines.mid and not self.position and self.redline
                    

                    If close is bigger than mid (higher visually) and at the same time lower than the bottom we are buying.
                    How is that possible? Please, clarify how it works, looks like I didn't get the concept of the indicator.

                    1 Reply Last reply Reply Quote 0
                    • B
                      backtrader administrators last edited by

                      redline is a flag which is set when a condition is met. But it doesn't mean that when is later checked together with other conditions, that the original condition is still true. But the flag is set.

                      1 Reply Last reply Reply Quote 1
                      • K
                        kozlone last edited by

                        Thanks, I see. I didn't get the strategy right.

                        1 Reply Last reply Reply Quote 0
                        • S
                          samk last edited by

                          @tim3lord

                          Hello, I am trying to run the code but gettin an error:

                          File "C:/Users/Sam/PycharmProjects/Test/.ipynb_checkpoints/Backtrader BB Strategy.py", line 108
                          cerebro = bt.Cerebro()
                          ^
                          IndentationError: expected an indented block

                          I do not see where the indent is off as it looks lined up to me. Can you take a quick look if you have the chance please? Thanks.

                          import datetime  # For datetime objects
                          import os.path  # To manage paths
                          import sys  # To find out the script name (in argv[0])
                          
                          # Import the backtrader platform
                          import backtrader as bt
                          import backtrader.feeds as btfeeds
                          
                          # Also, you can just use this script and paste it into your browsers to grab quick market cvs data from yahoo.
                          # http://ichart.finance.yahoo.com/table.csv?s=ENTERSTOCKHERE
                          
                          
                          # Create a Stratey
                          class TestStrategy(bt.Strategy):
                              params = (('BBandsperiod', 20),)
                          
                              def log(self, txt, dt=None):
                                  ''' Logging function fot this strategy'''
                                  dt = dt or self.datas[0].datetime.date(0)
                                  print('%s, %s' % (dt.isoformat(), txt))
                          
                              def __init__(self):
                                  # Keep a reference to the "close" line in the data[0] dataseries
                                  self.dataclose = self.datas[0].close
                          
                                  # To keep track of pending orders and buy price/commission
                                  self.order = None
                                  self.buyprice = None
                                  self.buycomm = None
                                  self.redline = None
                                  self.blueline = None
                          
                                  # Add a BBand indicator
                                  self.bband = bt.indicators.BBands(self.datas[0], period=self.params.BBandsperiod)
                          
                              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, order.Canceled, order.Margin]:
                                      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)
                          
                                  # Write down: no pending order
                                  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):
                                  # Simply log the closing price of the series from the reference
                                  self.log('Close, %.2f' % self.dataclose[0])
                          
                                  # Check if an order is pending ... if yes, we cannot send a 2nd one
                                  if self.order:
                                      return
                          
                                  if self.dataclose < self.bband.lines.bot and not self.position:
                                      self.redline = True
                          
                                  if self.dataclose > self.bband.lines.top and self.position:
                                      self.blueline = True
                          
                                  if self.dataclose > self.bband.lines.mid and not self.position and self.redline:
                                      # BUY, BUY, BUY!!! (with all possible default parameters)
                                      self.log('BUY CREATE, %.2f' % self.dataclose[0])
                                      # Keep track of the created order to avoid a 2nd order
                                      self.order = self.buy()
                          
                                  if self.dataclose > self.bband.lines.top and not self.position:
                                      # BUY, BUY, BUY!!! (with all possible default parameters)
                                      self.log('BUY CREATE, %.2f' % self.dataclose[0])
                                      # Keep track of the created order to avoid a 2nd order
                                      self.order = self.buy()
                          
                                  if self.dataclose < self.bband.lines.mid and self.position and self.blueline:
                                      # SELL, SELL, SELL!!! (with all possible default parameters)
                                      self.log('SELL CREATE, %.2f' % self.dataclose[0])
                                      self.blueline = False
                                      self.redline = False
                                      # Keep track of the created order to avoid a 2nd order
                                      self.order = self.sell()
                          
                          
                          if __name__ == '__main__':
                          # Create a cerebro entity
                          cerebro = bt.Cerebro()
                          
                          # Add a strategy
                          cerebro.addstrategy(TestStrategy)
                          
                          
                          
                          # data
                          data = btfeeds.GenericCSVData(
                                  dataname='C:\\Users\\Sam\\PycharmProjects\\Test\\.ipynb_checkpoints\\orcl-1995-2014.csv',
                          
                                  fromdate=datetime.datetime.strptime("1/1/2000", "%m/%d/%Y"),
                                  todate=datetime.datetime.strptime("12/31/2000", "%m/%d/%Y"),
                          
                                  nullvalue=0.0,  # missing values to be replaced with 0
                          
                                  dtformat=('%m/%d/%Y'),
                          
                                  datetime=0,
                                  time=-1,
                                  open=1,
                                  high=2,
                                  low=3,
                                  close=4,
                                  adjclose=5,
                                  volume=6,
                                  openinterest=-1,
                          
                          )
                          
                          
                          # Add the Data Feed to Cerebro
                          cerebro.adddata(data)
                          
                          # Set our desired cash start
                          cerebro.broker.setcash(10000.0)
                          
                          # Add a FixedSize sizer according to the stake
                          cerebro.addsizer(bt.sizers.FixedSize, stake=5)
                          
                          # Set the commission
                          cerebro.broker.setcommission(commission=0.002)
                          
                          # 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()
                          
                          1 Reply Last reply Reply Quote 0
                          • S
                            samk last edited by

                            @tim3lord

                            Lines where it is occuring:

                            if __name__ == '__main__':
                            # Create a cerebro entity
                            cerebro = bt.Cerebro()
                            
                            1 Reply Last reply Reply Quote 0
                            • S
                              samk last edited by

                              @tim3lord

                              Nevermind silly error! Figured it out thanks

                              1 Reply Last reply Reply Quote 0
                              • S
                                samk last edited by

                                @tim3lord

                                If the close is above the top of the bollinger bands and we are not in a trade, why would we be buying also? If we are looking for mean reversion, wouldnt we not buy in this instance. This is from the 5th if statement shown below:

                                 if self.dataclose > self.bband.lines.top and not self.position:
                                            # BUY, BUY, BUY!!! (with all possible default parameters)
                                            self.log('BUY CREATE, %.2f' % self.dataclose[0])
                                            # Keep track of the created order to avoid a 2nd order
                                            self.order = self.buy()
                                
                                R 1 Reply Last reply Reply Quote 0
                                • R
                                  remroc @samk last edited by

                                  Hello @samk

                                  This strategy might have some flaws but it shows quite well the intention to buy when the price is in bullish trend...

                                  How would you change it to fit more your ideas ?

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