For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/
2019-10-02: The community is currently in read-only mode

Trade never closed with position = 0



  • 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


  • administrators

    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


Log in to reply
 

});