Universes
Key Concepts
Introduction
Universe selection is the process of selecting a basket of assets you may trade. Dynamic universe selection increase diversification and decrease selection bias in your algorithm.
How Universe Selection Works
When you add a universe to your algorithm, LEAN sends a large dataset into a filter function you define. Your filter function needs to return a list of Symbol
objects. LEAN automatically subscribes to these new symbols and adds them to your algorithm. Your algorithm can do almost anything inside your filter functions, but the goal should be to narrow down the set of securities to the securities that are most applicable for your algorithm.
Security Changed Events
When your universe adds and removes assets, LEAN notifies your algorithm through the OnSecuritiesChanged
event handler. The event handler receives a SecurityChanges
object, which contains references to the added and removed securities. To access the added securities, check the changes.AddedSecurities
property. To access the removed securities, check the changes.RemovedSecurities
property.
public override void OnSecuritiesChanged(SecurityChanges changes) { foreach (var security in changes.AddedSecurities) { Debug($"{Time}: Added {security.Symbol}"); } foreach (var security in changes.RemovedSecurities) { Debug($"{Time}: Removed {security.Symbol}"); if (security.Invested) { Liquidate(security.Symbol, "Removed from Universe"); } } }
def OnSecuritiesChanged(self, changes: SecurityChanges) -> None: for security in changes.AddedSecurities: self.Debug(f"{self.Time}: Added {security.Symbol}") for security in changes.RemovedSecurities: self.Debug(f"{self.Time}: Removed {security.Symbol}") if security.Invested: self.Liquidate(security.Symbol, "Removed from Universe")
The preceding example liquidates securities that leave the universe. In this case, LEAN creates a market on open order and frees up buying power when the market opens.
A convenient way to track the securities that are currently in the universe is to use the NotifiedSecurityChanges
class.
A convenient way to track the securities that are currently in the universe is to maintain a self.securities
list.
private readonly HashSet<Security> _securities = new(); public override void OnSecuritiesChanged(SecurityChanges changes) { NotifiedSecurityChanges.UpdateCollection(_securities, changes); }
# In Initialize self.securities = [] def OnSecuritiesChanged(self, changes: SecurityChanges) -> None: for security in changes.RemovedSecurities: if security in self.securities: self.securities.remove(security) self.securities.extend(changes.AddedSecurities)
If you need to save data or create objects for each security in the universe, add custom members to the respective Security
objectscast the Security
objects to dynamic
objects and then save custom members to them. This technique is useful if you want to track stop loss levels or add indicators for each asset in the universe.
public override void OnSecuritiesChanged(SecurityChanges changes) { foreach (var security in changes.AddedSecurities) { var dynamicSecurity = security as dynamic; // Create an indicator dynamicSecurity.Indicator = SMA(security.Symbol, 10); // Warm up the indicator WarmUpIndicator(security.Symbol, dynamicSecurity.Indicator); } foreach (var security in changes.RemovedSecurities) { DeregisterIndicator((security as dynamic).Indicator); } }
def OnSecuritiesChanged(self, changes: SecurityChanges) -> None: for security in changes.AddedSecurities: # Create an indicator security.indicator = self.SMA(security.Symbol, 10) # Warm up the indicator self.WarmUpIndicator(security.Symbol, security.indicator) for security in changes.RemovedSecurities: self.DeregisterIndicator(security.indicator)
Select Current Constituents
If you don't want to make any changes to the current universe, return Universe.Unchanged
from your filter functions.
public class MyUniverseAlgorithm : QCAlgorithm { public override void Initialize() { AddUniverse(MyCoarseFilterFunction); } IEnumerable<Symbol> MyCoarseFilterFunction(IEnumerable<CoarseFundamental> coarse) { return Universe.Unchanged; } }
class MyUniverseAlgorithm(QCAlgorithm): def Initialize(self) -> None: self.AddUniverse(self.MyCoarseFilterFunction) def MyCoarseFilterFunction(self, coarse: List[CoarseFundamental]) -> List[Symbol]: return Universe.Unchanged
Universe Manager
The universe manager tracks all the universes in your algorithm. If you add multiple universe, you can access the constituents of each individual universe. To access the constituents of a universe in a multi-universe algorithm, save references to each universe when you add them.
private Universe _universe; // In Initialize _universe = AddUniverse(MyCoarseFilterFunction); // In OnData var universeMembers = UniverseManager[_universe.Configuration.Symbol].Members; foreach (var kvp in universeMembers) { var symbol = kvp.Key; var security = kvp.Value; }
# In Initialize self.universe = self.AddUniverse(self.MyCoarseFilterFunction) # In OnData universe_members = self.UniverseManager[self.universe.Configuration.Symbol].Members for kvp in universe_members: symbol = kvp.Key security = kvp.Value
Active Securities
The ActiveSecurities
property of the algorithm class contains all of the assets currently in your universe. It is a dictionary where the key is a Symbol
and the value is a Security
. When you remove an asset from a universe, LEAN usually removes the security from the ActiveSecurities
collection and removes the security subscription. However, it won't remove the security in any of the following situations:
- You own the security.
- You have an open order for the security.
- The security wasn't in the universe long enough to meet the
MinimumTimeInUniverse
setting.
When LEAN removes the security, the Security
object remains in the Securities
collection for record-keeping purposes, like tracking fees and trading volume.