Overall Statistics
Total Trades
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
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
-9.075
Tracking Error
0.079
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
Portfolio Turnover
0%
# region imports
from AlgorithmImports import *
import datetime
# endregion

class NdxMonthly(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2023, 12, 1)
        # self.SetEndDate(2023, 12, 31)
        self.SetEndDate(datetime.date.today())
        self.SetCash(100000)
        
        self.SetSecurityInitializer(lambda security: security.SetDataNormalizationMode(DataNormalizationMode.SplitAdjusted))

        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
        self.spx = self.AddIndex("SPX", Resolution.Daily).Symbol
        self.spy_ema5 = self.EMA(self.spy, 5)   
        self.spy_sma200 = self.SMA(self.spy, 200)
        
        # set data normaliaztion mode
        
        
        self.UniverseSettings.Resolution = Resolution.Daily
        self.universe_etf = self.AddUniverse(self.Universe.ETF("QQQ", Market.USA, self.UniverseSettings, self.ETFConstituentsFilter))

        self.EnableAutomaticIndicatorWarmUp = True

        self.indicators = {}

    def ETFConstituentsFilter(self, constituents: List[ETFConstituentData]) -> List[Symbol]:
        indicators = {}

        for c in constituents:  
            symbol = c.Symbol
  
            if c not in self.ActiveSecurities.Keys:
                self.AddEquity(symbol, Resolution.Daily)          
                            
            roc_short = self.ROC(symbol, 120).Current.Value * 100
            roc_long = self.ROC(symbol, 240).Current.Value * 100
            sma120 = self.SMA(symbol, 120)
            ema10 = self.EMA(symbol, 10)
            momentum = roc_short * (252/120) * .95 + roc_long * (252/240) * 1.05  # slightly weighted towards long

            factor = 0.4 * self.ROC(symbol, 63).Current.Value * 100 + 0.2 * self.ROC(symbol, 126).Current.Value * 100 + 0.2 * self.ROC(symbol, 189).Current.Value * 100 + 0.2 * self.ROC(symbol, 252).Current.Value * 100
            
            # Add the indicators to the dictionary
            indicators[symbol] = round(momentum, 2)
            
        # remove securities that we are subsribed to but are not in constituents
        for symbol in self.ActiveSecurities.Keys:
            if symbol not in indicators:
                self.RemoveSecurity(symbol)
            
        self.indicators = {c.Symbol: indicators[c.Symbol] for c in constituents}

        return [c.Symbol for c in constituents]

    def OnData(self, data: Slice) -> None:
        if self.IsWarmingUp:
            # log we are in warmup period
            self.Log("Warming up...")
            return

        bull_mkt = self.spy_ema5.Current.Value > self.spy_sma200.Current.Value
        if bull_mkt: # and self.Time.day == 1:
            self.Log("Bull market - number of universe members are currently " + str(len(self.indicators)))   
        else:
            self.Log(f'Not bull market.  SPY EMA5: {self.spy_ema5.Current.Value}, SPY SMA200: {self.spy_sma200.Current.Value}')
            # for symbol, indicator in self.indicators.items():
            #     self.Log(f"{symbol}: monthly momo {indicator['momentum']}")
            
            # sort the universe by momentum
        sorted_dict = dict(sorted(self.indicators.items(), key=lambda x: x[1], reverse=True))
        symbol_dict = {symbol.Value: value for symbol, value in sorted_dict.items()}
        # Only keep the top 20 items in symbol_dict
        symbol_dict = dict(list(symbol_dict.items())[:20])
        self.Log("Sorted universe by momentum")
        self.Log(str(symbol_dict))

        str_output = ''
        for symbol, indicator in symbol_dict.items():
            str_output += f"{symbol},{indicator}\n"

        try:
            spx_history = self.History([self.spx], 1, Resolution.Daily)
            spx_close = spx_history.loc[self.spx].close[-1]
            self.Log(f"SPX close price: {spx_close}")
            self.Log(str(spx_history))
        except:
            self.Log("Error getting SPX close price")