Overall Statistics
Total Orders
1023
Average Win
0.20%
Average Loss
-0.09%
Compounding Annual Return
26.028%
Drawdown
10.800%
Expectancy
1.557
Start Equity
100000
End Equity
145986.40
Net Profit
45.986%
Sharpe Ratio
1.107
Sortino Ratio
1.57
Probabilistic Sharpe Ratio
75.187%
Loss Rate
20%
Win Rate
80%
Profit-Loss Ratio
2.20
Alpha
-0.009
Beta
0.988
Annual Standard Deviation
0.116
Annual Variance
0.013
Information Ratio
-0.228
Tracking Error
0.047
Treynor Ratio
0.13
Total Fees
$1021.62
Estimated Strategy Capacity
$5300000.00
Lowest Capacity Asset
CCEP WZC7IOQFHS9X
Portfolio Turnover
0.89%
# 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 = []
        qqq = Symbol.create('QQQ', SecurityType.EQUITY, Market.USA)
        self._universe = self.add_universe(self.universe.etf(qqq, universe_filter_func=self._select_assets))
        self.schedule.on(self.date_rules.every_day(qqq), self.time_rules.after_market_open(qqq, 1), self._trade)
        self._rsi_period = self.get_parameter('rsi_period', 14)
        self._rsi_threshold = self.get_parameter('rsi_threshold', 30)

    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(self._rsi_period)
            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)
        [self._rsi_by_symbol[symbol].update(bar.end_time, bar.close) for bars in self.history[TradeBar](list(new_symbols), self._rsi_period, Resolution.DAILY) for symbol, bar in bars.items()]
        self._previous_symbols = symbols
        return [symbol for symbol, rsi in self._rsi_by_symbol.items() if rsi.is_ready and rsi.current.value < self._rsi_threshold]

    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)