Sellstop order not executing if a buy trade is already open

I have run two tests which clearly explain the scenario. Please see code block below of notify_order and notify_trade.

**Test 1 (execute only 1 sellstop order):
**

When I solely execute a sellstop order, I get what is expected.
I get logs in my notify_order that my sell order gets SUBMITTED and ACCEPTED (not COMPLETED yet as the price has not reached the price of the sellstop yet).
When the price reaches the trigger price for the sellstop order, I get logs in my notify_order that my sell order is COMPLETED and immediately thereafter, I get logs in my notify_trade that a sell trade has been executed (I use trade.justopened to track this).

**Test 2 (execute 1 buy trade and 1 sellstop order):
**

When I open a buy trade (which executes immediately) and a sellstop order, I run into issues.
I get logs in my notify_order that my buy trade is SUBMITTED, ACCEPTED and COMPLETED and I get logs in my notify_trade that the buy trade get successfully executed. Similar to above in Test 1, I get logs in my notify_order at the same time as the buy trade that my sell order gets SUBMITTED and ACCEPTED (but not COMPLETED yet as the price has not reached the price of the sellstop yet).
When the price reaches the trigger price for the sellstop order, I still get logs in my notify_order that my sell order is COMPLETED but now I get no logs in my notify_trade. There is not code change expect for adding a buy trade in Test 2.

NOTE: When doing exactly the same with limit orders, everything works fine (limit orders are also pending orders like stop orders). It is strange that it only occurs with sell stop orders.

Has Backtrader published any Q&A regarding these order issues? Or does anybody have a solution?


def notify_order(self, order):
    """
    ORDERS are actions that are unfulfilled/pending
    (means they are waiting to enter or be triggered into the market)
    Args:
        order: order.status can be any of {order.Created, order.Submitted,
        order.Accepted, order.Partial,
        order.Completed, order.Canceled, order.Expired, order.Margin, order.Rejected}
        #todo type hint

    Returns: None
    """

    if order.status in [order.Submitted]:
        log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- Order {order.ref} Submitted at price {order.price}, commission {order.executed.comm}n{order}')

        if order.p.exectype == 0 : # Market order
            if order.isbuy():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- MARKET order of BUY type Ref ID {order.ref} SUBMITTED at Price: {order.price}, commission {order.executed.comm}')
            elif order.issell():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- MARKET order of SELL type Ref ID {order.ref} SUBMITTED at Price: {order.price}, commission {order.executed.comm}')
            else:
                pass

        elif order.p.exectype == 2 : # Limit order
            if order.isbuy():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- LIMIT order of BUY type Ref ID {order.ref} SUBMITTED at Price: {order.price}, commission {order.executed.comm}')
            elif order.issell():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- LIMIT order of SELL type Ref ID {order.ref} SUBMITTED at Price: {order.price}, commission {order.executed.comm}')
            else:
                pass

        elif order.p.exectype == 3 : # Stop order
            if order.isbuy():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- STOP order of BUY type Ref ID {order.ref} SUBMITTED at Price: {order.price}, commission {order.executed.comm}')
            elif order.issell():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- STOP order of SELL type Ref ID {order.ref} SUBMITTED at Price: {order.price}, commission {order.executed.comm}')
            else:
                pass
        else:
            pass
        return

    if order.status in [order.Accepted]:
        log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- Order {order.ref} Accepted at price {order.price}, commission {order.executed.comm}n{order}')
        
        if order.p.exectype == 0 : # Market order
            if order.isbuy():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- MARKET order of BUY type Ref ID {order.ref} ACCEPTED at Price: {order.price}, commission {order.executed.comm}')
            elif order.issell():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- MARKET order of SELL type Ref ID {order.ref} ACCEPTED at Price: {order.price}, commission {order.executed.comm}')
            else:
                pass

        elif order.p.exectype == 2 : # Limit order
            if order.isbuy():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- LIMIT order of BUY type Ref ID {order.ref} ACCEPTED at Price: {order.price}, commission {order.executed.comm}')
            elif order.issell():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- LIMIT order of SELL type Ref ID {order.ref} ACCEPTED at Price: {order.price}, commission {order.executed.comm}')
            else:
                pass

        elif order.p.exectype == 3 : # Stop order
            if order.isbuy():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- STOP order of BUY type Ref ID {order.ref} ACCEPTED at Price: {order.price}, commission {order.executed.comm}')
            elif order.issell():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- STOP order of SELL type Ref ID {order.ref} ACCEPTED at Price: {order.price}, commission {order.executed.comm}')
            else:
                pass
        else:
            pass
        return

    if order.status in [order.Completed]:
        log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- Order {order.ref} Completed at price {order.price}, commission {order.executed.comm}n{order}')
                    
        if order.p.exectype == 0 : # Market order
            if order.isbuy():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- MARKET order of BUY type Ref ID {order.ref} COMPLETED at Price: {order.price}, commission {order.executed.comm}')
            elif order.issell():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- MARKET order of SELL type Ref ID {order.ref} COMPLETED at Price: {order.price}, commission {order.executed.comm}')
            else:
                pass

        elif order.p.exectype == 2 : # Limit order
            if order.isbuy():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- LIMIT order of BUY type Ref ID {order.ref} COMPLETED at Price: {order.price}, commission {order.executed.comm}')
            elif order.issell():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- LIMIT order of SELL type Ref ID {order.ref} COMPLETED at Price: {order.price}, commission {order.executed.comm}')
            else:
                pass

        elif order.p.exectype == 3 : # Stop order
            if order.isbuy():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- STOP order of BUY type Ref ID {order.ref} COMPLETED at Price: {order.price}, commission {order.executed.comm}')
            elif order.issell():
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- STOP order of SELL type Ref ID {order.ref} COMPLETED at Price: {order.price}, commission {order.executed.comm}')
            else:
                pass
        else:
            pass
        return

    if order.status in [order.Canceled]:
        log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- Order {order.ref} Canceled. Specs of order:n{order}')

        if order.isbuy():
            if order.p.exectype == 0:
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- MARKET order of BUY type ID {order.info.ref} & Ref ID {order.ref} deleted')
            elif order.p.exectype == 2:
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- LIMIT order of BUY type ID {order.info.ref} & Ref ID {order.ref} deleted')
            elif order.p.exectype == 3:
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- STOP order of BUY type ID {order.info.ref} & Ref ID {order.ref} deleted')
            else:
                pass

        elif order.issell():
            if order.p.exectype == 0:
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- MARKET order of SELL type ID {order.info.ref} & Ref ID {order.ref} deleted')
            elif order.p.exectype == 2:
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- LIMIT order of SELL type ID {order.info.ref} & Ref ID {order.ref} deleted')
            elif order.p.exectype == 3:
                log.debug(f'{self.datetime.datetime(ago=0).isoformat()} --- STOP order of SELL type ID {order.info.ref} & Ref ID {order.ref} deleted')
            else:
                pass
        
    if order.status in [order.Expired]:
        log.info(f'{self.datetime.datetime(ago=0).isoformat()}  --- Order {order.ref} Expired and could not be executed')
        self.remove_open_order(order.p.tradeid)
        return

    if order.status in [order.Margin]:
        log.error(f'{self.datetime.datetime(ago=0).isoformat()}  --- Insufficient cash to execute order - increase starting cash or improve strategy: Order {order.ref} Margin')
        # log.error(f'{self.datetime.datetime(ago=0).isoformat()}  --- Current balance {self.broker.getcash()} and equity {self.broker.getvalue()} - trying to open order {order.ref} with size {}')
        self.cerebro.runstop()
        return

    if order.status in [order.Rejected]:
        log.info(f'{self.datetime.datetime(ago=0).isoformat()}  --- Order {order.ref} Rejected by broker and could not be executed')
        self.remove_open_order(order.p.tradeid)
        return

def notify_trade(self, trade):
    """
    TRADES are actions that are fulfilled
    (means they are active - in a trade and making/losing money)
    """
    if trade.justopened:

        # commission added when trade is opened (entry commission) and closed (exit commission)
        self.strategy.total_commission += trade.commission # calculating total commission paid
        # self.open_orders[trade.tradeid]['commission'] = trade.commission # assign commission of orders in open_orders dict
        
        try:
            if self.open_orders[trade.tradeid]['action'] == 'buy':
                log.debug(f"{self.datetime.datetime(ago=0).isoformat()} --- BUY trade ID {trade.tradeid} executed at price {trade.price}")
                
                # assign the actual executed entry price to the backtrader buy object
                self.buy_trade[trade.tradeid][0].price = trade.price

                # assign the actual executed entry price to initially opened buy trades; note that modification trades need to be tracked seperately
                if trade.tradeid not in self.modification_list:
                    self.strategy.total_long_orders += 1
                    self.open_orders[trade.tradeid]['price'] = trade.price
            
            elif self.open_orders[trade.tradeid]['action'] == 'sell':
                log.debug(f"{self.datetime.datetime(ago=0).isoformat()} --- SELL trade ID {trade.tradeid} executed at price {trade.price}")
                
                # assign the actual executed entry price to the backtrader sell object
                self.sell_trade[trade.tradeid][0].price = trade.price

                # assign the actual executed entry price to initially opened sell trades; note that modification trades need to be tracked seperately
                if trade.tradeid not in self.modification_list:
                    self.strategy.total_short_orders += 1
                    self.open_orders[trade.tradeid]['price'] = trade.price
        
            elif self.open_orders[trade.tradeid]['action'] == 'buylimit':
                log.debug(f"{self.datetime.datetime(ago=0).isoformat()} --- BUYLIMIT {self.buy_limit[trade.tradeid][0].ref} -> BUY TRADE ID {trade.tradeid}")
                self.open_orders[trade.tradeid]['action'] = 'buy'
                # assign the actual executed entry price to the backtrader buy object when a buylimit becomes a buy
                self.open_orders[trade.tradeid]['price'] = trade.price
                self.buy_trade[trade.tradeid] = self.buy_limit[trade.tradeid] # move buylimit objects to buy objects
                del self.buy_limit[trade.tradeid] # removing the buylimit objects as it became a buy trade
            
            elif self.open_orders[trade.tradeid]['action'] == 'buystop':
                log.debug(f"{self.datetime.datetime(ago=0).isoformat()} --- BUYSTOP {self.buy_stop[trade.tradeid][0].ref} -> BUY TRADE ID {trade.tradeid}")
                self.open_orders[trade.tradeid]['action'] = 'buy'
                # assign the actual executed entry price to the backtrader buy object when a buystop becomes a buy
                self.open_orders[trade.tradeid]['price'] = trade.price
                self.buy_trade[trade.tradeid] = self.buy_stop[trade.tradeid] # move buystop objects to buy objects
                del self.buy_stop[trade.tradeid] # removing the buystop objects as it became a buy trade
            
            elif self.open_orders[trade.tradeid]['action'] == 'selllimit':
                log.debug(f"{self.datetime.datetime(ago=0).isoformat()} --- SELLLIMIT {self.sell_limit[trade.tradeid][0].ref} -> SELL TRADE ID {trade.tradeid}")
                self.open_orders[trade.tradeid]['action'] = 'sell'
                # assign the actual executed entry price to the backtrader buy object when a selllimit becomes a sell
                self.open_orders[trade.tradeid]['price'] = trade.price
                self.sell_trade[trade.tradeid] = self.sell_limit[trade.tradeid] # move selllimit objects to sell objects
                del self.sell_limit[trade.tradeid] # removing the selllimit objects as it became a sell trade
            
            elif self.open_orders[trade.tradeid]['action'] == 'sellstop':
                log.debug(f"{self.datetime.datetime(ago=0).isoformat()} --- SELLSTOP {self.sell_stop[trade.tradeid][0].ref} -> SELL TRADE ID {trade.tradeid}")
                self.open_orders[trade.tradeid]['action'] = 'sell'
                # assign the actual executed entry price to the backtrader buy object when a sellstop becomes a sell
                self.open_orders[trade.tradeid]['price'] = trade.price
                self.sell_trade[trade.tradeid] = self.sell_stop[trade.tradeid] # move sellstop objects to sell objects
                del self.sell_stop[trade.tradeid] # removing the sellstop objects as it became a sell trade
            else:
                pass
        except KeyError:
            pass
        else:
            pass
        log.debug(f"{self.datetime.date(ago=0).isoformat()} {self.datetime.time(ago=0).isoformat()} --- TRADE ID {trade.tradeid} with ENTRY PRICE {trade.price} CLOSED, final PNL {trade.pnl} & NET PROFIT {trade.pnlcomm}")
    

I have run multiple tests and have concluded that Test 1 and Test 2 define the problem well.

In Test 1, I prove that sell orders are correctly executed by Backtrader. In Test 2, I prove that adding a single buy trade to the situation causes sell orders to not be executed correctly by Backtrader

New contributor

Pierre Cilliers is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật