Overall Statistics
Total Orders
4201
Average Win
0.23%
Average Loss
-0.29%
Compounding Annual Return
1.998%
Drawdown
5.900%
Expectancy
0.002
Start Equity
100000
End Equity
110411.67
Net Profit
10.412%
Sharpe Ratio
-0.062
Sortino Ratio
-0.071
Probabilistic Sharpe Ratio
4.683%
Loss Rate
45%
Win Rate
55%
Profit-Loss Ratio
0.81
Alpha
-0.006
Beta
0.034
Annual Standard Deviation
0.038
Annual Variance
0.001
Information Ratio
-0.648
Tracking Error
0.156
Treynor Ratio
-0.069
Total Fees
$4444.40
Estimated Strategy Capacity
$31000000.00
Lowest Capacity Asset
BRKB R735QTJ8XC9X
Portfolio Turnover
69.49%
from AlgorithmImports import *
from QuantConnect.DataSource import *

class ETFConstituentUniverseAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2016, 1, 1)
        self.set_end_date(2021, 1, 1)
        self.set_cash(100000)
        self.universe_settings.asynchronous = True
        self.universe_settings.resolution = Resolution.MINUTE
        
        # Requesting data
        self.spy = self.add_equity("SPY").symbol
        self._universe = self.add_universe(self.universe.etf(self.spy, self.universe_settings, self.etf_constituents_filter))
        
        # Historical Universe data
        history = self.history(self._universe, 30, Resolution.DAILY)
        for (universe_symbol, time), constituents in history.items():
            for constituent in constituents:
                self.debug(f'{constituent.symbol}: {constituent.weight}')

        self.weight_by_symbol = {}
        
        self.schedule.on(
            self.date_rules.every_day(self.spy),
            self.time_rules.after_market_open(self.spy, 1),
            self.rebalance)

    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]
        self.weight_by_symbol = {c.symbol: c.weight for c in selected}
        
        return list(self.weight_by_symbol.keys())

    def rebalance(self) -> None:
        spy_weight = sum(self.weight_by_symbol.values())

        if spy_weight > 0:
            for symbol in self.portfolio.Keys:
                if symbol not in self.weight_by_symbol:
                    self.liquidate(symbol)
    
            for symbol, weight in self.weight_by_symbol.items():
                self.set_holdings(symbol, 0.5 * weight / spy_weight)
                
            self.set_holdings(self.spy, -0.5)

    def on_securities_changed(self, changes: SecurityChanges) -> None:
        for security in changes.removed_securities:
            if security.invested:
                self.liquidate(security.symbol, 'Removed From Universe')

        for security in changes.added_securities:
            # Historical data
            history = self.history(security.symbol, 7, Resolution.DAILY)
            self.debug(f'We got {len(history)} from our history request for {security.symbol}')