Overall Statistics
Total Orders
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Start Equity
100000
End Equity
100000
Net Profit
0%
Sharpe Ratio
0
Sortino Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-0.991
Tracking Error
0.106
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
Portfolio Turnover
0%
Drawdown Recovery
0
# region imports
from AlgorithmImports import *
# endregion


class EquityIndicatorUniverseSelectionAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self._selection_data_by_symbol = {}
        self.universe_settings.resolution = Resolution.DAILY
        self._universe = self.add_universe(self._select_assets)

    def _select_assets(self, fundamentals):
        # Update the indicator of all stocks in the universe dataset and
        # get the subset of stocks that have their indicator ready.
        ready_stocks = [
            f for f in fundamentals
            if self._selection_data_by_symbol.setdefault(
                f.symbol, EquityDataPointIndicatorSelectionData(self, f, SimpleMovingAverage(21))
            ).update(f)
        ]
        # As assests leave the Fundamental dataset, delete their SelectionData object.
        delisted_assets = list(
            set(self._selection_data_by_symbol.keys()) 
            - set(f.symbol for f in fundamentals)
        )
        for symbol in delisted_assets:
            self._selection_data_by_symbol.pop(symbol)
        # Select a subset of the stocks based on the indicator.
        # Example: 10 stocks furthest above their SMA.
        factor_by_symbol = {
            f.symbol: f.price / self._selection_data_by_symbol[f.symbol].indicator.current.value 
            for f in ready_stocks
        }
        return sorted(
            {k: v for k, v in factor_by_symbol.items() if v > 0}, 
            key=lambda symbol: factor_by_symbol[symbol]
        )[-100:]


class EquityDataPointIndicatorSelectionData:

    def __init__(self, algorithm, f, indicator):
        self._algorithm = algorithm
        self._price_scale_factor = f.price_scale_factor
        self.indicator = indicator

    def update(self, f):
        # If there has been a split or dividend, reset the indicator
        # and warm it up with the new adjusted history.
        if f.price_scale_factor != self._price_scale_factor:
            self._price_scale_factor = f.price_scale_factor
            self.indicator.reset()
            history = self._algorithm.history[TradeBar](
                f.symbol, 
                self.indicator.warm_up_period, 
                Resolution.DAILY, 
                data_normalization_mode=DataNormalizationMode.SCALED_RAW
            )
            for bar in history:
                self.indicator.update(bar)
        # Otherwise, just update the indicator like normally.
        else:
            self.indicator.update(f.end_time, f.price)
        return self.indicator.is_ready