Overall Statistics
Total Orders
1010
Average Win
0.20%
Average Loss
-0.09%
Compounding Annual Return
23.602%
Drawdown
10.900%
Expectancy
1.557
Start Equity
100000
End Equity
140926.62
Net Profit
40.927%
Sharpe Ratio
0.974
Sortino Ratio
1.369
Probabilistic Sharpe Ratio
69.345%
Loss Rate
19%
Win Rate
81%
Profit-Loss Ratio
2.17
Alpha
-0.011
Beta
0.986
Annual Standard Deviation
0.115
Annual Variance
0.013
Information Ratio
-0.267
Tracking Error
0.047
Treynor Ratio
0.114
Total Fees
$1008.62
Estimated Strategy Capacity
$5600000.00
Lowest Capacity Asset
CCEP WZC7IOQFHS9X
Portfolio Turnover
0.90%
# region imports
from AlgorithmImports import *
# endregion


class TechnicalUniverseOnEtfConstituentsAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2023, 1, 1)
        self._rsi_by_symbol = {}
        self._previous_symbols = []
        self._universe = self.add_universe(self.universe.etf('QQQ', universe_filter_func=self._select_assets))
        spy = Symbol.create('SPY', SecurityType.EQUITY, Market.USA)
        self.schedule.on(self.date_rules.every_day(spy), self.time_rules.after_market_open(spy, 1), self._trade)

    def _select_assets(self, constituents):
        for c in constituents:
            if c.symbol not in self._rsi_by_symbol:
                self._rsi_by_symbol[c.symbol] = RelativeStrengthIndex(14)
            elif c.symbol in self._previous_symbols:
                self._rsi_by_symbol[c.symbol].update(c.end_time, c.price)
        symbols = [c.symbol for c in constituents]
        new_symbols = set(symbols) - set(self._previous_symbols)
        for bars in self.history[TradeBar](list(new_symbols), 14, Resolution.DAILY):
            for symbol, bar in bars.items():
                self._rsi_by_symbol[symbol].update(bar.end_time, bar.close)
        self._previous_symbols = symbols
        return [symbol for symbol, rsi in self._rsi_by_symbol.items() if rsi.is_ready and rsi.current.value < 100/3]

    def _trade(self):
        if self._universe.selected is None: return
        weight = 1 / len(list(self._universe.selected))
        self.set_holdings([PortfolioTarget(symbol, weight) for symbol in self._universe.selected], True)