Problem Description
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
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.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
- Accessing the Data: The
delistingsobject passed to the method behaves like a dictionary. You iterate through it to handle multiple delistings that might occur simultaneously. - 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. - Liquidation: The
liquidatemethod is used to close out the position. Doing this onDelistingType.WARNINGis generally safer than waiting forDelistingType.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.