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/

    Trade never closed with position = 0

    Indicators/Strategies/Analyzers
    2
    2
    131
    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.
    • Benjamin Kesby
      Benjamin Kesby last edited by

      I am having difficulty understanding why this error is occurring. Even though the position size goes to 0 with the following logic, seen in

      print(self.position.size)
      

      and the fact that

      if not self.position:
      

      is triggered, why is notify_trade() not called?

      Does there have to be a rest period, i.e more than 1 tick whilst position = 0 for it to trigger? The order output also states that the initial buy or sell order from within if not self.position: is accepted twice with the second one being cancelled once the first is completed. If I remove the stop_loss, take_profit logic then this problem does not occur.

      Any help on where i've gone wrong would be appreciated.

      Code:

      from __future__ import (absolute_import, division, print_function, unicode_literals)
      
      import backtrader as bt
      import backtrader.feeds as btfeeds
      import backtrader.indicators as btind
      
      __all__ = ['dema']
      
      
      class dema(bt.Strategy):
          params = dict(
              # Template parameters:
              stop_loss = 0,
              stop_trail = 0,
              take_profit = 0,
              sizer_fixed = 1,
              sizer_perc = 0.01,
              # Strategy dependant parameters
              period = 30,
              )
      
          def __init__(self):
              # Add indicators here:
              self.dema = btind.DEMA(self.data1, period=self.p.period)
              self.cross = btind.CrossOver(self.data1.close, self.dema.dema)
      
              self.o = []
      
          def start(self):
              self.lendata1_enter = 0
      
          def next(self):
              # Sizing logic
              pos_size = self.sizer()
              
              if not self.position:
                  # =============== Enter order logic whilst out of a position =============== #
                  if self.cross == 1:
                      self.lendata1_enter = len(self.data1)
                      buy = self.buy(size=pos_size)
                      buy.addinfo(Trigger="CrossUp  Out of Position.")
      
                      # Enter predifined exit points (This logic requires only one of stop_trail or take_profit can be used)
                      
                      # Submit stop loss order
                      # Calculate price to set stop treating stop_loss as a percentage
                      stop_loss_price = self.data.close[0] * (1 - self.p.stop_loss)
                      stop_loss = self.sell(size=buy.size, price=stop_loss_price, exectype=bt.Order.Stop,\
                                            parent=buy, transmit=False)
                      stop_loss.addinfo(Trigger="Stop loss.")
                      self.o.append(stop_loss)
      
                      # Submit take profit order
                      take_profit_price = self.data.close[0] * (1 + self.p.take_profit)
                      take_profit = self.sell(size=buy.size, price=take_profit_price, exectype=bt.Order.Limit,\
                                              parent=buy, transmit=True)
                      take_profit.addinfo(Trigger="Take profit.")
                      self.o.append(take_profit)
                      
      
                  if self.cross == -1:
                      self.lendata1_enter = len(self.data1)
                      sell = self.sell(size=pos_size)
                      sell.addinfo(Trigger="CrossDown  Out of Position.")
      
                      # Enter predifined exit points (This logic requires only one of stop_trail or take_profit can be used)
                      
                      # Submit stop loss order
                      # Calculate price to set stop treating stop_loss as a percentage
                      stop_loss_price = self.data.close[0] * (1 - self.p.stop_loss)
                      stop_loss = self.buy(size=sell.size, price=stop_loss_price, exectype=bt.Order.Stop,\
                                            parent=sell, transmit=False)
                      stop_loss.addinfo(Trigger="Stop loss.")
                      self.o.append(stop_loss)
      
                      # Submit take profit order
                      take_profit_price = self.data.close[0] * (1 + self.p.take_profit)
                      take_profit = self.buy(size=sell.size, price=take_profit_price, exectype=bt.Order.Limit,\
                                              parent=sell, transmit=True)
                      take_profit.addinfo(Trigger="Take profit.")
                      self.o.append(take_profit)
      
      
              else:
                  if len(self.data1) > self.lendata1_enter:
                      # =============== Enter order logic whilst in a position =============== #
                      cur_size = self.position.size
                      if self.cross == 1:
                          # Close out current order, then reverse direction
                          buy = self.buy(size=cur_size)
                          buy.addinfo(Trigger="CrossUp In a Position.")
      
      
                      if self.cross == -1:
                          # Close out current order, then reverse direction
                          sell = self.sell(size=cur_size)
                          sell.addinfo(Trigger="CrossDown In a Position.")
      
      
          def notify_trade(self, trade):
              date = self.data.datetime.datetime()
             
              if trade.isclosed:
                  print('='*32 + ' NOTIFY TRADE ' + '='*32)
                  print('{}, Close Price: {}, Profit, Gross {}, Net {}'.format(
                                                      date,
                                                      trade.price,
                                                      round(trade.pnl,2),
                                                      round(trade.pnlcomm,2)))
                  print('='*80)
      
              # Cancel and clear all remaining orders if a trade is closed.
              if trade.isclosed:
                  for order in self.o:
                      self.cancel(order)
      
                  self.o.clear()
      
      
          def notify_order(self, order):
              date = self.data.datetime.datetime()
      
              if order.status == order.Accepted:
                  print('-'*32 + ' NOTIFY ORDER ' + '-'*32)
                  print('Order Accepted\n')
                  print('{}, Status {}: Ref: {}, Size: {}, Price: {}, Info: {}'.format(
                                                              date,
                                                              order.status,
                                                              order.ref,
                                                              order.size,
                                                              'NA' if not order.price else round(order.price,5),
                                                              order.info
                                                              ))
      
      
              if order.status == order.Completed:
                  print('-'*32 + ' NOTIFY ORDER ' + '-'*32)
                  print('Order Completed')
                  print('{}, Status {}: Ref: {}, Size: {}, Price: {}, Info: {}'.format(
                                                              date,
                                                              order.status,
                                                              order.ref,
                                                              order.size,
                                                              'NA' if not order.price else round(order.price,5),
                                                              order.info
                                                              ))
                  print('Created: {} Price: {} Size: {}'.format(bt.num2date(order.created.dt), order.created.price,order.created.size))
                  print('-'*80)
                  print(self.position.size)
                  print('-'*80)
      
              if order.status == order.Canceled:
                  print('-'*32 + ' NOTIFY ORDER ' + '-'*32)
                  print('Order Canceled\n')
                  print('{}, Status {}: Ref: {}, Size: {}, Price: {}, Info: {}'.format(
                                                              date,
                                                              order.status,
                                                              order.ref,
                                                              order.size,
                                                              'NA' if not order.price else round(order.price,5),
                                                              order.info
                                                              ))
      
              if order.status == order.Rejected:
                  print('-'*32 + ' NOTIFY ORDER ' + '-'*32)
                  print('WARNING! Order Rejected')
                  print('{}, Status {}: Ref: {}, Size: {}, Price: {}, Info: {}'.format(
                                                              date,
                                                              order.status,
                                                              order.ref,
                                                              order.size,
                                                              'NA' if not order.price else round(order.price,5),
                                                              order.info
                                                              ))
                  print('-'*80)
                  
      
          def sizer(self):
              if self.p.sizer_fixed == 1:
                  pos_price = self.broker.startingcash * self.p.sizer_perc
                  pos_size = pos_price / self.data0.close[0]
              else:
                  pos_price = self.broker.get_cash() * self.p.sizer_perc
                  pos_size = pos_price / self.data0.close[0]
              return pos_size
      
      if __name__ == '__main__':
          pass
      

      Output sample:

      -------------------------------- NOTIFY ORDER --------------------------------
      Order Accepted
      
      2016-01-03 23:01:00, Status 2: Ref: 1, Size: 0.46464083263637207, Price: NA, Info: AutoOrderedDict([('Trigger', 'CrossUp  Out of Position.')])
      -------------------------------- NOTIFY ORDER --------------------------------
      Order Accepted
      
      2016-01-03 23:01:00, Status 2: Ref: 1, Size: 0.46464083263637207, Price: NA, Info: AutoOrderedDict([('Trigger', 'CrossUp  Out of Position.')])
      -------------------------------- NOTIFY ORDER --------------------------------
      Order Accepted
      
      2016-01-03 23:01:00, Status 2: Ref: 2, Size: -0.46464083263637207, Price: 426.1356, Info: AutoOrderedDict([('Trigger', 'Stop loss.')])
      -------------------------------- NOTIFY ORDER --------------------------------
      Order Accepted
      
      2016-01-03 23:01:00, Status 2: Ref: 3, Size: -0.46464083263637207, Price: 434.7444, Info: AutoOrderedDict([('Trigger', 'Take profit.')])
      -------------------------------- NOTIFY ORDER --------------------------------
      Order Completed
      2016-01-03 23:01:00, Status 4: Ref: 1, Size: 0.46464083263637207, Price: NA, Info: AutoOrderedDict([('Trigger', 'CrossUp  Out of Position.')])
      Created: 2016-01-03 23:00:00 Price: 430.44 Size: 0.46464083263637207
      --------------------------------------------------------------------------------
      0.46464083263637207
      --------------------------------------------------------------------------------
      -------------------------------- NOTIFY ORDER --------------------------------
      Order Canceled
      
      2016-01-03 23:01:00, Status 5: Ref: 1, Size: 0.46464083263637207, Price: NA, Info: AutoOrderedDict([('Trigger', 'CrossUp  Out of Position.')])
      -------------------------------- NOTIFY ORDER --------------------------------
      Order Accepted
      
      2016-01-04 14:01:00, Status 2: Ref: 4, Size: -0.46464083263637207, Price: NA, Info: AutoOrderedDict([('Trigger', 'CrossDown In a Position.')])
      -------------------------------- NOTIFY ORDER --------------------------------
      Order Completed
      2016-01-04 14:01:00, Status 4: Ref: 4, Size: -0.46464083263637207, Price: NA, Info: AutoOrderedDict([('Trigger', 'CrossDown In a Position.')])
      Created: 2016-01-04 14:00:00 Price: 432.29 Size: -0.46464083263637207
      --------------------------------------------------------------------------------
      0.0
      --------------------------------------------------------------------------------
      

      Sample of plot:

      Screen Shot 2019-09-24 at 13.04.24.png

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

        notify_trade (with a closed trade) would in any case be called after this has been printed out:

        @Benjamin-Kesby said in Trade never closed with position = 0:

        0.0
        

        Because your log ends there ... it is impossible to actually judge if notify_trade is called or not.

        Then ... you fail to take into account that the cross runs on the higher timeframe and you operate on the lower timeframe. The lower timeframe will tick SEVERAL times, whilst the higher timeframe remains at the same point. On all these ticks the cross will evaluate to be true.

        That's why you have order duplication and probably the source of your problems. You can only check the cross when it actually happens and have to ignore all other ticks which only belong to the lower timeframe.

        See: https://www.backtrader.com/docu/concepts/ and read about the len

        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(); }); }