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.

Dynamic vs Static Universes

If you create security subscriptions in your algorithm with AddEquity, AddCrypto, or a similar method, the security stays in your universe until you manually remove it. These methods create a static, user-defined universe. In contrast, universe selection creates a dynamic universe that periodically adds and removes security subscriptions.

Universe Settings

The universe settings of your algorithm configure some properties of the universe constituents. The following table describes the properties of the UniverseSettings object:

PropertyData TypeDescriptionDefault Value
ExtendedMarketHoursboolShould assets also feed extended market hours?falseFalse
FillForwardboolShould asset data fill forward?trueTrue
MinimumTimeInUniverseTimeSpantimedeltaWhat's the minimum time assets should be in the universe?TimeSpan.FromDays(1)timedelta(1)
ResolutionResolutionWhat resolution should assets use?Resolution.Minute
ContractDepthOffsetintWhat offset from the current front month should be used for continuous Future contracts? 0 uses the front month and 1 uses the back month contract. This setting is only available for Future assets.0
DataMappingModeDataMappingModeHow should continuous Future contracts be mapped? This setting is only available for Future assets.DataMappingMode.OpenInterest
DataNormalizationModeDataNormalizationModeHow should historical prices be adjusted? This setting is only available for Equity and Futures assets.DataNormalizationMode.Adjusted
LeveragedecimalfloatWhat leverage should assets use in the universe? This setting is not available for derivative assets.Security.NullLeverage

To set the UniverseSettings, update the preceding properties in the Initialize method before you add the universe. These settings are globals, so they apply to all universes you create.

// Request second resolution data. This will be slow!
UniverseSettings.Resolution = Resolution.Second;
AddUniverse(MyCoarseFilterFunction);
# Request second resolution data. This will be slow!
self.UniverseSettings.Resolution = Resolution.Second
self.AddUniverse(self.MyCoarseFilterFunction)

Configure Universe Securities

Instead of configuring global universe settings, you can individually configure the settings of each security in the universe with a security initializer. Security initializers let you apply any security-level reality model or special data requests on a per-security basis. To set the security initializer, in the Initialize method, call the SetSecurityInitializer method and then define the security initializer.

//In Initialize
SetSecurityInitializer(CustomSecurityInitializer);

private void CustomSecurityInitializer(Security security)
{
    // Disable trading fees
    security.SetFeeModel(new ConstantFeeModel(0, "USD"));
}
#In Initialize
self.SetSecurityInitializer(self.CustomSecurityInitializer)

def CustomSecurityInitializer(self, security: Security) -> None:
    # Disable trading fees
    security.SetFeeModel(ConstantFeeModel(0, "USD"))

For simple requests, you can use the functional implementation of the security initializer. This style lets you configure the security object with one line of code.

SetSecurityInitializer(security => security.SetFeeModel(new ConstantFeeModel(0, "USD")));
self.SetSecurityInitializer(lambda security: security.SetFeeModel(ConstantFeeModel(0, "USD")))

In some cases, you may want to trade a security in the same time loop that you create the security subscription. To avoid errors, use a security initializer to set the market price of each security to the last known price.

var seeder = new FuncSecuritySeeder(GetLastKnownPrices);
SetSecurityInitializer(security => seeder.SeedSecurity(security));
seeder = FuncSecuritySeeder(self.GetLastKnownPrices)
self.SetSecurityInitializer(lambda security: seeder.SeedSecurity(security))

If you call the SetSecurityInitializer method, it overwrites the default security initializer. The default security initializer uses the security-level reality models of the brokerage model to set the following reality models of each security:

The default security initializer also sets the leverage of each security and intializes each security with a seeder function. To extend upon the default security initializer instead of overwriting it, create a custom BrokerageModelSecurityInitializer.

// In Initialize
SetSecurityInitializer(new MySecurityInitializer(BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices)));

class MySecurityInitializer : BrokerageModelSecurityInitializer
{
    public MySecurityInitializer(IBrokerageModel brokerageModel, ISecuritySeeder securitySeeder)
        : base(brokerageModel, securitySeeder) {}
    
    public override void Initialize(Security security)
    {
        // First, call the superclass definition
        // This method sets the reality models of each security using the default reality models of the brokerage model
        base.Initialize(security);

        // Next, overwrite some of the reality models
        security.SetFeeModel(new ConstantFeeModel(0, "USD"));
    }
}
# In Initialize
self.SetSecurityInitializer(MySecurityInitializer(self.BrokerageModel, FuncSecuritySeeder(self.GetLastKnownPrices)))


class MySecurityInitializer(BrokerageModelSecurityInitializer):

    def __init__(self, brokerage_model: IBrokerageModel, security_seeder: ISecuritySeeder) -> None:
        super().__init__(brokerage_model, security_seeder)

    def Initialize(self, security: Security) -> None:
        # First, call the superclass definition
        # This method sets the reality models of each security using the default reality models of the brokerage model
        super().Initialize(security)

        # Next, overwrite some of the reality models
        security.SetFeeModel(ConstantFeeModel(0, "USD"))

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, maintain a dictionary of custom SymbolData objects. This technique is useful if you want to track stop loss levels or add indicators for each asset in the universe.

// In Initialize
Dictionary<Symbol, SymbolData> _symbolDataBySymbol = new();

public override void OnSecuritiesChanged(SecurityChanges changes)
{
    foreach (var security in changes.AddedSecurities)
    {
        _symbolDataBySymbol.Add(security.Symbol, new SymbolData());  // You need to define this class
    }

    foreach (var security in changes.RemovedSecurities)
    {
        _symbolDataBySymbol.Remove(security.Symbol);
    }
}
# In Initialize
self.symbol_data_by_symbol = {}

def OnSecuritiesChanged(self, changes: SecurityChanges) -> None:
    for security in changes.AddedSecurities:
        self.symbol_data_by_symbol[security.Symbol] = SymbolData() # You need to define this class

    for security in changes.RemovedSecurities:
        self.symbol_data_by_symbol.pop(security.Symbol, None)

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.

Derivative Universes

In a regular universe, you select a basket of assets from the entire universe of securities. In a derivative universe, you select a basket of contracts for a single underlying asset. The following derivative universes are available:

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: