🚀 AI One-Click Generation of quantconnect Strategy Code

How to Automatically Liquidate a Security When It's Removed From Your Universe

quantconnect | Published on: 2025/12/4
The following content is generated by EasyQuant .

Problem Description

How can I automatically sell a stock if it drops out of my universe selection?

Solution

To automatically sell a stock when it is removed from your universe in QuantConnect, you should utilize the on_securities_changed event handler. This method is triggered whenever the universe selection logic updates (e.g., daily at midnight for coarse selection).

Here is the specific logic required:

  1. Override the on_securities_changed method.
  2. Iterate through the changes.removed_securities list.
  3. Check if your portfolio currently holds the security (security.invested).
  4. Call self.liquidate() to close the position.

Implementation Example

Below is a complete, runnable algorithm demonstrating this behavior. It uses a Coarse Universe to select the top 5 liquid stocks. When the composition of the top 5 changes, the algorithm automatically liquidates the stocks that fell off the list.

# region imports
from AlgorithmImports import *
# endregion

class UniverseRemovalLiquidation(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2023, 1, 1)
        self.set_end_date(2023, 6, 1)
        self.set_cash(100000)

        # Add a Coarse Selection Universe to dynamically select stocks
        self.add_universe(self.coarse_selection_function)
        self.universe_settings.resolution = Resolution.DAILY

    def coarse_selection_function(self, coarse):
        # Select top 5 stocks by Dollar Volume
        sorted_by_dollar_volume = sorted(coarse, key=lambda x: x.dollar_volume, reverse=True)
        return [x.symbol for x in sorted_by_dollar_volume[:5]]

    def on_data(self, data: Slice):
        # Simple entry logic: Invest in active securities if not already invested
        # This is just to populate the portfolio so we have something to liquidate later
        for security in self.active_securities.values():
            if not security.invested and security.symbol in data:
                self.set_holdings(security.symbol, 0.1)

    def on_securities_changed(self, changes: SecurityChanges):
        # 1. Handle Added Securities (Optional logging)
        for security in changes.added_securities:
            self.log(f"Added to Universe: {security.symbol}")

        # 2. Handle Removed Securities (The logic you requested)
        for security in changes.removed_securities:
            # Check if we currently have a position
            if security.invested:
                self.log(f"Removed from Universe: {security.symbol} - Liquidating position.")
                
                # Liquidate the specific symbol. 
                # You can pass a tag to the order for clarity in the logs.
                self.liquidate(security.symbol, tag="Universe Removal")

Key Components Explained

  • changes.removed_securities: This property contains a list of Security objects that were in the universe during the previous step but are no longer selected by your universe function.
  • security.invested: It is best practice to check this boolean before sending an order. It prevents sending unnecessary "sell" orders for stocks you were tracking but didn't actually own.
  • self.liquidate(symbol): This helper method automatically calculates the quantity needed to close your position (whether long or short) and sends a market order to flatten the position.

Q&A: Universe Management in QuantConnect

Q: Does on_securities_changed run before or after on_data?
A: In the LEAN engine event flow, on_securities_changed runs before on_data. This ensures that your universe is updated and any removal logic (like liquidation) is processed before the algorithm attempts to generate new entry signals for the current time step.

Q: Will liquidate work if I have open limit orders on that stock?
A: self.liquidate() cancels all open orders for the specific symbol first, and then submits a market order to close the remaining position. This ensures you don't end up with a "stuck" position due to pending orders.

Q: Can I use this for Manual Universes?
A: Yes. If you manually add securities using self.add_equity() and later remove them using self.remove_security(), the on_securities_changed event will still fire, adding the removed stock to the removed_securities list.

Q: What happens to the data subscription when a security is removed?
A: When a security is removed from the universe, LEAN automatically removes the data subscription for that asset unless you still hold a position or have open orders. However, once you liquidate the position inside on_securities_changed, the data subscription will be terminated shortly after to save resources.