Generate code in 3 minutes
Enter your idea and AI generates runnable code.
To automatically liquidate a stock when it is removed from your trading universe in QuantConnect, you should utilize the on_securities_changed event handler.
This method is triggered whenever the universe selection model adds or removes assets. The changes parameter passed to this method contains a list of removed_securities. You can iterate through this list, check if you currently hold a position, and then liquidate it.
on_securities_changed: This is the specific event handler for universe changes.changes.removed_securities: A list of Security objects that have dropped out of the universe.security.invested: A check to ensure you actually hold the asset before attempting to liquidate it (saves API calls and log noise).self.liquidate: The method to close the position.The following code demonstrates a strategy that selects the top 5 liquid stocks. If a stock drops out of the top 5 (is removed from the universe), the algorithm automatically liquidates the position.
# region imports
from AlgorithmImports import *
# endregion
class UniverseLiquidationAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 6, 1)
self.set_cash(100000)
# Use a coarse selection to dynamically select assets based on dollar volume
self.add_universe(self.coarse_selection_function)
# Initialize a list to track active symbols
self._active_symbols = []
def coarse_selection_function(self, coarse):
# Sort by Dollar Volume and take the top 5
# This ensures the universe changes frequently for demonstration purposes
sorted_by_volume = sorted(coarse, key=lambda x: x.dollar_volume, reverse=True)
return [x.symbol for x in sorted_by_volume[:5]]
def on_data(self, slice: Slice):
# Simple logic: Invest equally in all active symbols
# This ensures we have positions to liquidate when they are removed
for symbol in self._active_symbols:
if not self.portfolio[symbol].invested:
self.set_holdings(symbol, 1.0 / len(self._active_symbols))
def on_securities_changed(self, changes: SecurityChanges):
"""
Event fired when securities are added or removed from the universe.
"""
# 1. Handle Added Securities
for security in changes.added_securities:
self._active_symbols.append(security.symbol)
# Optional: Log the addition
# self.debug(f"Added {security.symbol} to universe")
# 2. Handle Removed Securities (The Solution)
for security in changes.removed_securities:
if security.symbol in self._active_symbols:
self._active_symbols.remove(security.symbol)
# Check if we are currently invested in this security
if security.invested:
# Liquidate the position immediately
self.liquidate(security.symbol, "Removed from Universe")
self.debug(f"Liquidated {security.symbol} because it was removed from the universe.")
Security object remains accessible in changes.removed_securities immediately, allowing you to check security.invested and security.holdings.self.liquidate cancels open orders by default. If you have specific logic for open orders (e.g., stop losses) that you want to handle differently, you should cancel them explicitly using self.transactions.cancel_open_orders(security.symbol).on_securities_changed instead of OnSecuritiesChanged), which is the standard for new QuantConnect Python projects.Q: Does liquidate close both long and short positions?
A: Yes, self.liquidate(symbol) closes any open position for that symbol, regardless of whether it is long or short, and cancels all open orders associated with it.
Q: Will on_securities_changed run before on_data?
A: Yes, in the LEAN engine event flow, on_securities_changed is triggered after universe selection but before the on_data slice is passed to the algorithm. This ensures you can clean up your portfolio before processing new data.
Q: What happens if I don't liquidate a removed security?
A: If you do not liquidate, you will continue to hold the shares. However, you will no longer receive data updates for that symbol in on_data (unless you manually added a subscription for it elsewhere), effectively leaving you with a "zombie" position that is harder to manage.
Q: Can I delay liquidation?
A: Yes. Instead of calling self.liquidate immediately inside on_securities_changed, you could set a flag or add the symbol to a "pending liquidation" list to be processed at a specific time (e.g., Market On Close) within your on_data or a Scheduled Event.