Equity

ETF Constituents Universes

Introduction

An ETF constituents universe lets you select a universe of securities in an exchange traded fund. The US ETF Constituents dataset includes 2,650 US ETFs you can use to create your universe.

Create Universes

To add an ETF Constituents universe, call the Universe.ETFuniverse.etf method.

public class ETFConstituentsAlgorithm : QCAlgorithm
{
    public override void Initialize() 
    {
        UniverseSettings.Asynchronous = true;
        AddUniverse(Universe.ETF("SPY"));
    }
}
class ETFConstituentsAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.universe_settings.asynchronous = True        
        self.add_universe(self.universe.etf("SPY"))

The following table describes the ETFetf method arguments:

Argument: etfTickeretf_ticker

The ETF ticker. To view the supported ETFs in the US ETF Constituents dataset, see Supported ETFs.

Data Type: stringstr | Default Value: None

Argument: universeSettingsuniverse_settings

The universe settings. If you don't provide an argument, it uses the algorithm UniverseSettingsuniverse_settings.

Data Type: UniverseSettings | Default Value: nullNone

Argument: universeFilterFuncuniverse_filter_func

A function to select some of the ETF constituents for the universe. If you don't provide an argument, it selects all of the constituents.

Data Type: Func<IEnumerable<ETFConstituentUniverse>, IEnumerable<Symbol>>Callable[[List[ETFConstituentUniverse]], List[Symbol]] | Default Value: nullNone

To select a subset of the ETF constituents, provide a universeFilterFuncuniverse_filter_func argument. The filter function receives ETFConstituentUniverse objects, which represent one of the ETF constituents. ETFConstituentUniverse objects have the following attributes:

public class ETFConstituentsAlgorithm : QCAlgorithm 
{
    private Universe _universe;
    public override void Initialize() 
    {
        UniverseSettings.Asynchronous = true;
        _universe = Universe.ETF("SPY", UniverseSettings, ETFConstituentsFilter);
        AddUniverse(_universe);
    }

    private IEnumerable<Symbol> ETFConstituentsFilter(IEnumerable<ETFConstituentUniverse> constituents)
    {
        // Get the 10 securities with the largest weight in the index
        return constituents.OrderByDescending(c => c.Weight).Take(10).Select(c => c.Symbol);
    }
}
class ETFConstituentsAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.universe_settings.asynchronous = True
        self._universe = self.universe.etf("SPY", self.universe_settings, self.etf_constituents_filter)
        self.add_universe(self.universe)

    def etf_constituents_filter(self, constituents: List[ETFConstituentUniverse]) -> List[Symbol]:
        # Get the 10 securities with the largest weight in the index
        selected = sorted([c for c in constituents if c.weight],
            key=lambda c: c.weight, reverse=True)[:10]
        return [c.symbol for c in selected]

Historical Data

To get historical ETF constituents data, call the Historyhistory method with the Universe object and the lookback period. The return type is a IEnumerable<BaseDataCollection> and you have to cast its items to ETFConstituentUniverse.

To get historical ETF constituents data, call the Historyhistory method with the Universe object and the lookback period. The return type is a multi-index pandas.Series of ETFConstituentUniverse list.

var history = History(_universe, 30, Resolution.Daily);
foreach (var constituents in history)
{
    foreach (ETFConstituentUniverse constituent in constituents)
    {
        Log($"{constituent.Symbol} weight at {constituent.EndTime}: {constituent.Weight}");
    }
}
history = self.history(self.universe, 30, Resolution.DAILY)
for (universe_symbol, time), constituents in history.items():
    for constituent in constituents:
        self.log(f'{constituent.symbol} weight at {constituent.end_time}: {constituent.weight}')

For more information about ETF Constituents data, see US ETF Constituents.

Selection Frequency

Equity universes run on a daily basis by default. To adjust the selection schedule, see Schedule.

Examples

The following example chains an ETF constituents universe and fundamental universe. It first selects all the constituents of the QQQ ETF and then filters them down in the fundamental universe to select the assets that are trading above their average price over the last 200 days. The output of the fundamental universe selection method is the output of the chained universe. The Fundamental objects that the fundamental universe filter function recieves contains the prices of the ETF constituents. By chaining the ETF constituents universe into the fundamental universe, you can update the indicators with the price instead of making a history request.

using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Util;
using QuantConnect.Indicators;
using QuantConnect.Data.Fundamental;
using QuantConnect.Securities;

namespace QuantConnect.Algorithm.CSharp
{
    public class ChainedUniverseAlgorithm : QCAlgorithm
    {
        private Dictionary<Symbol, SymbolData> _symbolDataBySymbol = new Dictionary<Symbol, SymbolData>();

        public override void Initialize()
        {
            SetCash(100000);
            SetStartDate(2023, 6, 1);
            UniverseSettings.Asynchronous = true;
            AddUniverse(Universe.ETF("QQQ"), FundamentalSelection);
        }

        public IEnumerable<Symbol> FundamentalSelection(IEnumerable<Fundamental> fundamental)
        {
            // Create/Update the indicator for each asset in the ETF
            List<Symbol> universeSymbols = new List<Symbol>();
            foreach (var f in fundamental)
            {
                universeSymbols.Add(f.Symbol);
                if (!_symbolDataBySymbol.ContainsKey(f.Symbol))
                {
                    // Create an indicator for each asset that enters the ETF
                    _symbolDataBySymbol[f.Symbol] = new SymbolData(this, f.Symbol);
                }
                // Update each indicator
                _symbolDataBySymbol[f.Symbol].Update(Time, f.AdjustedPrice);
            }

            // Remove indicators for assets that are no longer in the ETF
            var symbolsToRemove = _symbolDataBySymbol.Keys.Where(symbol => !universeSymbols.Contains(symbol)).ToList();
            foreach (var symbol in symbolsToRemove)
            {
                _symbolDataBySymbol.Remove(symbol);
            }

            // Select a subset of the ETF constituents based on the indicator value
            var universe = _symbolDataBySymbol.Where(kvp => kvp.Value.IsAboveSma).Select(kvp => kvp.Key);

            // Plot the results
            Plot("Universe", "Possible", fundamental.Count());
            Plot("Universe", "Selected", universe.Count());

            // Return the selected assets
            return universe;
        }
    }

    public class SymbolData
    {
        private SimpleMovingAverage _sma;
        public bool IsAboveSma { get; private set; }

        public SymbolData(QCAlgorithm algorithm, Symbol symbol, int period = 200)
        {
            _sma = new SimpleMovingAverage(period);
            algorithm.WarmUpIndicator(symbol, _sma, Resolution.Daily);
        }

        public void Update(DateTime time, decimal value)
        {
            IsAboveSma = _sma.Update(time, value) && value > _sma.Current.Value;
        }
    }
}
from AlgorithmImports import * 

class ChainedUniverseAlgorithm(QCAlgorithm):
    symbol_data_by_symbol = {}
    
    def initialize(self):
        self.set_cash(100000)
        self.set_start_date(2023, 6, 1)
        self.universe_settings.asynchronous = True
        self.add_universe(self.universe.etf("QQQ"), self.fundamental_selection)
    
    def fundamental_selection(self, fundamental: List[Fundamental]) -> List[Symbol]:
        # Create/Update the indicator for each asset in the ETF
        universe_symbols = []
        for f in fundamental:
            universe_symbols.append(f.symbol)
            if f.symbol not in self.symbol_data_by_symbol:
                # Create an indicator for each asset that enters the ETF
                self.symbol_data_by_symbol[f.symbol] = SymbolData(self, f.symbol)
                
            # Update each indicator
            self.symbol_data_by_symbol[f.symbol].update(self.time, f.adjusted_price)
    
        # Remove indicators for assets that are no longer in the ETF
        symbols_to_remove = [symbol for symbol in self.symbol_data_by_symbol.keys() if symbol not in universe_symbols]
        for symbol in symbols_to_remove:
            self.symbol_data_by_symbol.pop(symbol)

        # Select a subset of the ETF constituents based on the indicator value
        universe = [symbol for symbol, symbol_data in self.symbol_data_by_symbol.items() if symbol_data.is_above_sma]
            
        # Plot the results
        self.plot("Universe", "Possible", len(list(fundamental)))
        self.plot("Universe", "Selected", len(universe))
    
        # Return the selected assets
        return universe
    
    
class SymbolData(object):
    def __init__(self, algorithm, symbol, period=200):
        self._sma = SimpleMovingAverage(period)
        algorithm.warm_up_indicator(symbol, self._sma, Resolution.DAILY)
    
    def update(self, time, value):
        self.is_above_sma = self._sma.update(time, value) and value > self._sma.current.value

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: