🚀 AI One-Click Generation of quantconnect Strategy Code

What Happens When a Stock Is Delisted? Handling Delistings in QuantConnect

quantconnect | Published on: 2025/11/28
The following content is generated by EasyQuant .

Problem Description

Please show me how to handle security delistings in my code.

Solution

To handle security delistings in QuantConnect, you should utilize the on_delistings event handler. This method is triggered whenever a security in your universe receives a delisting warning or is actually delisted.

Here is a complete, robust example of how to implement this.

Key Concepts

  1. DelistingType.WARNING: Occurs before the actual delisting (often days in advance). This is the safest time to close positions to avoid liquidity issues or forced liquidation at unfavorable prices.
  2. DelistingType.DELISTED: Occurs when the security is removed. At this point, you can no longer trade the asset on the exchange.

Strategy Code

# region imports
from AlgorithmImports import *
# endregion

class DelistingHandlingAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2020, 1, 1)
        self.set_end_date(2023, 1, 1)
        self.set_cash(100000)
        
        # Example: Add a universe or specific equities. 
        # We will use a manual list here, but this works for dynamic universes too.
        self.add_equity("SPY", Resolution.DAILY)
        
        # Adding a stock known to have delisted or had corporate actions for demonstration
        # (e.g., a bankrupt company or merger target would trigger this in a real backtest)
        self.add_equity("AAA", Resolution.DAILY) 

    def on_data(self, slice: Slice):
        # Standard entry logic (simplified for example)
        if not self.portfolio.invested:
            for symbol in slice.keys():
                self.set_holdings(symbol, 0.1)

    def on_delistings(self, delistings: Delistings):
        """
        Event handler for delisting events.
        :param delistings: A dictionary-like object containing Delisting objects keyed by Symbol
        """
        for symbol, delisting in delistings.items():
            
            # Case 1: Delisting Warning
            # This usually happens days before the actual delisting.
            if delisting.type == DelistingType.WARNING:
                self.log(f"{self.time} - WARNING: {symbol} will be delisted on {delisting.time}")
                
                # Strategy: Liquidate immediately upon warning to avoid holding illiquid assets
                if self.portfolio[symbol].invested:
                    self.log(f"Liquidating {symbol} due to delisting warning.")
                    self.liquidate(symbol, tag="Delisting Warning")
                
                # Cancel any open orders for this symbol
                self.transactions.cancel_open_orders(symbol)

            # Case 2: Actual Delisting
            # The security is being removed from the exchange.
            elif delisting.type == DelistingType.DELISTED:
                self.log(f"{self.time} - DELISTED: {symbol} has been delisted.")
                
                # Ensure the security is removed from internal lists or logic
                if self.portfolio[symbol].invested:
                    # In a live environment, this might be too late to trade on the exchange,
                    # but ensures the algorithm state is clean.
                    self.liquidate(symbol, tag="Asset Delisted")

Implementation Details

  1. Accessing the Data: The delistings object passed to the method behaves like a dictionary. You iterate through it to handle multiple delistings that might occur simultaneously.
  2. Order Cancellation: It is best practice to call self.transactions.cancel_open_orders(symbol) when a warning is received to prevent buying into a dying asset.
  3. Liquidation: The liquidate method is used to close out the position. Doing this on DelistingType.WARNING is generally safer than waiting for DelistingType.DELISTED.

Alternative: Using on_data

While on_delistings is the preferred event handler, delisting data is also available in the Slice object within on_data.

def on_data(self, slice: Slice):
    if slice.delistings.count > 0:
        for symbol, delisting in slice.delistings.items():
            if delisting.type == DelistingType.WARNING:
                self.liquidate(symbol)

Q&A

What happens if I don't handle delistings in my algorithm?

If you hold a position through a delisting event without handling it, the Lean engine will typically liquidate the position at the final price (often zero or the last traded price) when the asset is removed from the universe. However, relying on this default behavior can lead to inaccurate backtest results or "zombie" positions in live trading that lock up capital.

Can I trade a stock after receiving a Delisting Warning?

Yes, the stock usually continues to trade until the specific delisting.time provided in the warning object. However, liquidity often dries up, and volatility increases. Most algorithmic strategies choose to exit immediately upon receiving the warning to minimize risk.

Does on_delistings work for Options and Futures?

Yes, on_delistings works for all asset classes. For Options and Futures, it is particularly useful for handling contract expirations if the data provider treats expiration as a removal event, though on_assignment_order_event or contract expiration logic is more commonly used for the natural end of a derivative's life.