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/

    Why doesn't my 'buy' trigger?

    General Code/Help
    4
    7
    76
    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.
    • Chris Cappel
      Chris Cappel last edited by

      I have a very simple system, but I'm running into a small problem. I'm using an if/else block to facilitate my buys and sells.

      and its structured like this:

      if not self.position:
         if (entry signal):
              print('entry signal triggered')
              self.buy()
      

      now I'm finding that the buy function isn't following through all the time, sometimes it works and sometimes it doesn't. I'm tracking the trades via order.status. sometimes order.status reports 'BUY EXECUTED' other times it doesn't while the if/else blocks still prints 'entry signal triggered'. this is again confirmed on the plot where I can visually see a buy should have been triggered, but it's not.

      Am I missing something about the self.buy() function?

      1 Reply Last reply Reply Quote 0
      • vladisld
        vladisld last edited by

        It would be helpful to share your code.

        1 Reply Last reply Reply Quote 1
        • Chris Cappel
          Chris Cappel last edited by

          Here's my code:

          class HighBreakout(bt.Strategy):
          
              params = (('max', 100),
                        ('min', 100),
                        ('Long_MA', 200),
                        ('Short_MA', 50),
                        ('oneplot', True)
                       )
          
              def log(self, txt, dt=None):
                  ''' Logging function for 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.inds = dict()
                  self.holding = 0
                  self.buyprice = 0
                  self.sellprice = 0
                  self.wins = 0
                  self.losses = 0
                  self.totalwin = 0
                  self.totalloss =0
          
                  print(self.params.max)
          
                  for i, d in enumerate(self.datas):
                      self.inds[d] = dict()
                      self.inds[d]['MAX'] = bt.talib.MAX(d.close, timeperiod=self.params.max)
                      self.inds[d]['MIN'] = bt.talib.MIN(d.close, timeperiod=self.params.min)
                      self.inds[d]['sma_long'] = bt.indicators.SMA(d.close, period=self.params.Long_MA)
                      self.inds[d]['sma_short'] = bt.indicators.SMA(d.close, period=self.params.Short_MA)
          
                      if i > 0:  # Check we are not on the first loop of data feed:
                          if self.p.oneplot == True:
                              d.plotinfo.plotmaster = self.datas[0]
          
              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: %.2f, Cost: %.2f, Comm %.2f' %
                              (order.executed.price,
                               order.executed.value,
                               order.executed.comm))
                          print(f'max prev = {self.max_prev} > max prev prev = {self.max_prev_prev}')
          
          
                          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))
                  if trade.pnl > 0:
                      self.totalwin += trade.pnl
                  if trade.pnl < 0:
                      self.totalloss -= trade.pnl
                  if trade.pnl > 0:
                      self.wins += 1
                  if trade.pnl < 0:
                      self.losses += 1
          
              def next(self):
          
          
                  for i, d in enumerate(self.datas):
          
                      self.C = self.datas[i].close[0]
                      self.O = self.datas[i].open[0]
                      self.L = self.datas[i].low[0]
                      self.H = self.datas[i].high[0]
          
                      self.C_prev = self.datas[i].close[-1]
                      self.O_prev = self.datas[i].open[-1]
                      self.L_prev = self.datas[i].low[-1]
                      self.H_prev = self.datas[i].high[-1]
                      self.symbol = self.datas[i]._name
                      self.date = self.datas[0].datetime.datetime(0)
                      max_ = self.inds[d]['MAX'][0]
                      self.max_prev = self.inds[d]['MAX'][-1]
                      self.max_prev_prev = self.inds[d]['MAX'][-2]
                      min_prev = self.inds[d]['MIN'][-1]
             
                      if not self.position:
                          if self.max_prev > self.max_prev_prev:
                              print(f'{self.max_prev} is greater than {self.max_prev_prev}')
                              self.buy()
          
                      else:
                          if self.L_prev <= min_prev:
                              self.sell()
          
              def stop(self):
                  if self.losses + self.wins != 0:
                      batting_average = self.wins / (self.losses + self.wins)
                  percent_return = self.broker.getvalue() / self.broker.startingcash
                  if self.wins != 0:
                      average_win = self.totalwin / self.wins
                  if self.losses != 0:
                      average_loss = self.totalloss / self.losses
                  print('==================================================')
                  print('Starting Value - %.2f' % self.broker.startingcash)
                  print('Ending   Value - %.2f' % self.broker.getvalue())
                  print('Percent Return: %.2f' % percent_return)
                  if self.losses + self.wins != 0:
                      print('Batting average %.2f' % batting_average)
                  print('Wins: %.0f' % self.wins)
                  print('Losses: %.0f' % self.losses)
                  if self.wins != 0:
                      print('Average win: %.2f' % average_win)
                  if self.losses != 0:
                      print('Average loss: %.2f' % average_loss)
                  print('==================================================')
          

          and here's a sample output:

          176.38 is greater than 175.37
          176.495 is greater than 176.38
          2020-04-16, BUY EXECUTED, Price: 175.25, Cost: 11505.49, Comm 0.00
          max prev = 176.495 > max prev prev = 176.38
          2020-04-21, SELL EXECUTED, Price: 172.00, Cost: 11505.49, Comm 0.00
          2020-04-21, OPERATION PROFIT, GROSS -213.51, NET -213.51
          175.89 is greater than 174.84
          175.94 is greater than 175.89
          176.07 is greater than 175.94
          176.215 is greater than 176.07
          176.23 is greater than 176.215
          176.41 is greater than 176.23
          176.93 is greater than 176.41
          177.415 is greater than 176.93
          177.51 is greater than 177.415
          2020-04-29, BUY EXECUTED, Price: 177.24, Cost: 11298.87, Comm 0.00
          max prev = 177.51 > max prev prev = 177.415
          2020-05-13, SELL EXECUTED, Price: 182.43, Cost: 11298.87, Comm 0.00
          2020-05-13, OPERATION PROFIT, GROSS 331.01, NET 331.01
          185.745 is greater than 185.57
          2020-05-18, BUY EXECUTED, Price: 186.16, Cost: 11629.25, Comm 0.00
          max prev = 185.745 > max prev prev = 185.57
          2020-05-21, SELL EXECUTED, Price: 183.79, Cost: 11629.25, Comm 0.00
          2020-05-21, OPERATION PROFIT, GROSS -147.68, NET -147.68
          184.175 is greater than 184.04
          2020-05-29, BUY EXECUTED, Price: 183.81, Cost: 11482.20, Comm 0.00
          max prev = 184.175 > max prev prev = 184.04
          ==================================================
          Starting Value - 10000.00
          Ending   Value - 12299.57
          Percent Return: 1.23
          Batting average 0.50
          Wins: 7
          Losses: 7
          Average win: 373.73
          Average loss: 161.99
          ==================================================
          

          you can see the code passes through the if/else block and prints the "XX is greater than XX", but does not execute the buy operation despite the code calling the function.

          1 Reply Last reply Reply Quote 0
          • J
            Jonny8 last edited by

            is there enough cash?

            def log(self, arg):
                    print('{} {}'.format(self.datetime.date(), arg))
                    
                # This section is for logging of orders in greater detail to figure out whether the strategy is actually having no problem with orders
                def notify_order(self, order):
                    if order.status in [order.Accepted]:
                        # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                        return
                    if order.status in [order.Submitted]:
                        if order.isbuy():
                        
                            dt, dn = self.datetime.date(), order.data._name
                            print('Buy {} {} {} Price {:.2f} Value {:.2f} Size {} Cash {:.2f}'.format(
                                    order.getstatusname(), dt, dn, order.created.price, order.created.size * order.created.price , order.created.size, self.broker.getcash()))
                        if order.issell():
                            dt, dn = self.datetime.date(), order.data._name
                            print('Sell {} {} {} Price {:.2f} Value {:.2f} Size {}'.format(
                                    order.getstatusname(), dt, dn, order.created.price, order.created.size * order.created.price, order.created.size))
            
                        # 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():
                            dt, dn = self.datetime.date(), order.data._name
                            print('Buy {} {} Price {:.2f} Value {:.2f} Size {}'.format(
                                dt, dn, order.executed.price, order.executed.value, order.executed.size))
            
                        if order.issell():# Sell
                            dt, dn = self.datetime.date(), order.data._name
                            print('Sell {} {} Price {:.2f} Value {:.2f} Size {}'.format(
                                dt, dn, order.executed.price, order.executed.value, order.executed.size))
            
            
                    elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                        self.log('Order Canceled/Margin/Rejected')
            

            Code to have a closer look at the orders.

            1 Reply Last reply Reply Quote 1
            • Chris Cappel
              Chris Cappel last edited by

              There is enough cash, here's a snip of the graph to visually demonstrate the issue I'm facing.https://imgur.com/a/QDIZpFU

              The intent is to make a purchase every time the 'max' indicator makes a local high. It's passing over a number of new highs and then making a purchase at a seemingly random time, but still a new high. This graph tells me its not something wrong with my data or the way I've implemented the indicators, but probably something wrong with the way I've implemented the backtrader buy functionality. I just cant wrap my head around what the issue might be

              run-out 1 Reply Last reply Reply Quote 0
              • run-out
                run-out @Chris Cappel last edited by

                @Chris-Cappel Add a time stamp in front of print(f'{self.max_prev} is greater than {self.max_prev_prev}') and implement the code (def notify_order(self, order):) as suggested by @Jonny8 then share your log so we can see what's going on. Tough to tell without this information.

                1 Reply Last reply Reply Quote 2
                • Chris Cappel
                  Chris Cappel last edited by

                  Good call. The issue was I didn't have enough cash. Odd because size should be determined by total cash/price.

                  Even odder that this buy-order didn't fill.

                  Buy Submitted 2018-11-26  Price 105.81 Value 9324.90 Size 88.12458567695634 Cash 9324.90
                  

                  Among the results I saw orders like this, which clearly, correctly shouldn't have filled (and didn't):

                  Buy Submitted 2018-11-26  Price 105.81 Value 9328.43 Size 88.15791097525063 Cash 9324.90
                  

                  I've use a the workaround below to continue testing the efficiency of the strategy (previously 100%):

                  cerebro.addsizer(bt.sizers.PercentSizer, percents=99)
                  

                  Thanks for the help everyone!

                  1 Reply Last reply Reply Quote 2
                  • 1 / 1
                  • First post
                    Last post
                  Copyright © 2016, 2017, 2018 NodeBB Forums | Contributors
                  $(document).ready(function () { app.coldLoad(); }); }