---
title: "ETF Constituents Universes"
description: "The ETFConstituentsUniverseSelectionModel selects a daily universe of US Equities based on the constituents of an ETF."
canonical: https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/universe-selection/etf-constituents-universes
markdown: https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/universe-selection/etf-constituents-universes.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/03%20Writing%20Algorithms/34%20Algorithm%20Framework/02%20Universe%20Selection/05%20ETF%20Constituents%20Universes
modified: 2026-11-01
index: https://www.quantconnect.com/llms.txt
---

# ETF Constituents Universes

[Documentation](https://www.quantconnect.com/docs/v2.md) > [Writing Algorithms](https://www.quantconnect.com/docs/v2/writing-algorithms.md) > Algorithm Framework > Universe Selection > ETF Constituents Universes

> Code examples are shown in Python. Append `?lang=csharp` to this URL for the C# version of this page.

## Introduction

The `ETFConstituentsUniverseSelectionModel` selects a universe of US Equities based on the constituents of an ETF. These Universe Selection models rely on the [US ETF Constituents](https://www.quantconnect.com/datasets/quantconnect-us-etf-constituents) dataset. They run on a daily schedule by default. To adjust the selection schedule, see [Schedule](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/universe-selection/universe-settings.md#10-Schedule).

## Add ETF Constituents Universe Selection

To add an `ETFConstituentsUniverseSelectionModel` to your algorithm, in the `initialize` method, call the `add_universe_selection` method. The `ETFConstituentsUniverseSelectionModel` constructor expects an ETF ticker.

```python
# Run universe selection asynchronously to speed up your algorithm.
self.universe_settings.asynchronous = True
self.add_universe_selection(ETFConstituentsUniverseSelectionModel("SPY"))
```

The following table describes the arguments the model accepts:

| Argument | Data Type | Description | Default Value |
| --- | --- | --- | --- |
| `etf_ticker` | `string` | Ticker of the ETF to get constituents for. To view the available ETFs, see [Supported ETFs](https://www.quantconnect.com/docs/v2/writing-algorithms/datasets/quantconnect/us-etf-constituents.md#08-Supported-ETFs). |  |
| `universe_settings` | `UniverseSettings` | The [universe settings](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/universe-selection/universe-settings.md). If you don't provide an argument, the model uses the `algorithm.universe_settings` by default. | `None` |
| `universe_filter_func` | `Callable[[List[ETFConstituentUniverse]], List[Symbol]]` | Function to filter ETF constituents. If you don't provide an argument, the model selects all of the ETF constituents by default. | `None` |

If you provide a `universe_filter_func` argument, you can use the following attributes of the `ETFConstituentUniverse` objects to select your universe:

*Members of `QuantConnect.Data.UniverseSelection.ETFConstituentUniverse` (JSON): https://www.quantconnect.com/services/inspector?type=T:QuantConnect.Data.UniverseSelection.ETFConstituentUniverse&language=python*

The following example shows how to select the 10 Equities with the largest weight in the SPY ETF:

```python
# Initialize asynchronous settings for speed and use the ETFConstituentsUniverseSelectionModel
# to select the top 10 SPY constituents by weight, focusing on blue-chip stocks with minimal risk.
def initialize(self) -> None:
    self.universe_settings.asynchronous = True
    self.add_universe_selection(
        ETFConstituentsUniverseSelectionModel("SPY", universe_filter_func=self._etf_constituents_filter)
    )

def _etf_constituents_filter(self, constituents: List[ETFConstituentUniverse]) -> List[Symbol]:
    # Select the 10 largest Equities in the ETF.
    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]
```

To move the ETF `Symbol` and the selection function outside of the algorithm class, create a universe selection model that inherits the `ETFConstituentsUniverseSelectionModel` class.

```python
# Initialize asynchronous settings for speed and use the LargestWeightSPYETFUniverseSelectionModel
# to select the top 10 blue-chip SPY constituents by weight, focusing on stocks with minimal risk.
self.universe_settings.asynchronous = True
self.add_universe_selection(LargestWeightSPYETFUniverseSelectionModel())

# Outside of the algorithm class
class LargestWeightSPYETFUniverseSelectionModel(ETFConstituentsUniverseSelectionModel):

    def __init__(self) -> None:
        super().__init__('SPY', universe_filter_func=self._etf_constituents_filter)

    def _etf_constituents_filter(self, constituents: List[ETFConstituentUniverse]) -> List[Symbol]:
        # Select the 10 largest Equities in the ETF.
        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]
```

To return the current universe constituents from the selection function, return `Universe. Unchanged UNCHANGED`.

For more information about this model, see the [class reference](https://www.lean.io/docs/v2/lean-engine/class-reference/py/QuantConnect/Algorithm/Framework/Selection/ETFConstituentsUniverseSelectionModel/) and [implementation](https://github.com/QuantConnect/Lean/blob/master/Algorithm.Framework/Selection/ETFConstituentsUniverseSelectionModel.py).

## Examples

The following examples demonstrate some common practices for implementing the framework ETF constituent universe selection model.

### Example 1: Weighted ETF Constituents

A subset of the SPY constituents outperform the SPY while many underperform the overall index. In an attempt to buy the ETF constituents that outperform the index, the following algorithm buys the top 50 weighted assets in the ETF. In this example, we will pass a function to the `ETFConstituentsUniverseSelectionModel` for selection.

```python
class FrameworkETFConstituentsUniverseSelectionAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_cash(10000000)

        self.etf_weight_by_symbol = {}

        # Add a universe of the SPY constituents.
        self.add_universe_selection(
            ETFConstituentsUniverseSelectionModel("SPY", universe_filter_func=self._etf_constituents_filter)
        )
        # Add an Alpha model to trade based on the constituent weights.
        self.add_alpha(EtfAlphaModel(self))
        # Position sizing was handled by insight weight (from ETF weight).
        self.set_portfolio_construction(InsightWeightingPortfolioConstructionModel())

    def _etf_constituents_filter(self, constituents: List[ETFConstituentUniverse]) -> List[Symbol]:
        # Cache the constituent weights in a dictionary for filtering and position sizing.
        self.etf_weight_by_symbol = {x.symbol: x.weight for x in constituents if x.weight}
        # Select the 50 constituents with the largest weight in the ETF.
        # They should have positive excess returns.
        return [x[0] for x in sorted(self.etf_weight_by_symbol.items(), key=lambda x: x[1], reverse=True)[:50]]

class EtfAlphaModel(AlphaModel):
    def __init__(self, algorithm: FrameworkETFConstituentsUniverseSelectionAlgorithm) -> None:
        self._algorithm = algorithm
        self._day = -1

    def update(self, algorithm: QCAlgorithm, slice: Slice) -> List[Insight]:
        insights = []

        # Daily rebalance only.
        if self._day == slice.time.day:
            return insights

        for symbol, weight in self._algorithm.etf_weight_by_symbol.items():
            # Get the ETF weight of all the assets currently in the universe.
            # To avoid trading errors, skip assets that have no price yet.
            if symbol in slice.bars:
                if not algorithm.securities[symbol].price:
                    continue
                insights.append(Insight.price(symbol, timedelta(1), InsightDirection.UP, weight=weight))

        self._day = slice.time.day

        return insights
```

### Example 2: Weight Trend Selection

The following algorithm trades the QQQ constituents with the upward trend of its weight constitution, indicated by an EMA indicator, suggesting its price trend is outperforming other constituents, or its weight was promoted by the NASDAQ index, which brings positive sentiment. To better utilize the EMA indicator, we inherit the `ETFConstituentsUniverseSelectionModel` superclass to create a custom universe selection model.

```python
class FrameworkETFConstituentsUniverseSelectionAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_cash(10000000)

        # Add a universe of the SPY constituents.
        self.add_universe_selection(
            UptrendETFUniverseSelectionModel("QQQ")
        )
        # Add Alpha model to trade based on the selections.
        self.add_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(1)))
        # Equally invest in insights to dissipate the capital risk evenly.
        self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())

        # Warm up the indicators.
        self.set_warm_up(60, Resolution.DAILY)

class UptrendETFUniverseSelectionModel(ETFConstituentsUniverseSelectionModel):
    ema_by_symbol = {}

    def __init__(self, etf_ticker: str) -> None:
        super().__init__(etf_ticker, universe_filter_func=self.etf_constituent_filter)

    def etf_constituent_filter(self, constituents: List[ETFConstituentUniverse]) -> List[Symbol]:
        # Remove the ones that are not in the ETF anymore.
        to_remove = set(self.ema_by_symbol.keys()).difference(set([x.symbol for x in constituents]))
        for symbol in to_remove:
            del self.ema_by_symbol[symbol]

        for c in constituents:
            symbol = c.symbol
            if symbol in self.ema_by_symbol and c.weight:
                # Update EMA with the latest weight.
                self.ema_by_symbol[symbol].update(c.end_time, c.weight)
            else:
                # Create an EMA to filter by trend.
                self.ema_by_symbol[symbol] = ExponentialMovingAverage(60)

        # Select the ones with the increasing trend of constituent weight.
        return [symbol for symbol, ema in self.ema_by_symbol.items() if ema.is_ready and ema.current.value > ema.previous.value]
```
