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.ETF
universe.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 ETF
etf
method arguments:
Argument: |
Argument: |
Argument: |
To select a subset of the ETF constituents, provide a universeFilterFunc
universe_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 History
history
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 History
history
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