Overall Statistics
Total Trades
2949
Average Win
0.42%
Average Loss
-0.38%
Compounding Annual Return
25.801%
Drawdown
29.800%
Expectancy
0.415
Net Profit
892.790%
Sharpe Ratio
0.994
Sortino Ratio
1.031
Probabilistic Sharpe Ratio
50.305%
Loss Rate
33%
Win Rate
67%
Profit-Loss Ratio
1.11
Alpha
0.099
Beta
0.973
Annual Standard Deviation
0.173
Annual Variance
0.03
Information Ratio
0.973
Tracking Error
0.1
Treynor Ratio
0.177
Total Fees
$2963.23
Estimated Strategy Capacity
$86000000.00
Lowest Capacity Asset
PX R735QTJ8XC9X
Portfolio Turnover
4.54%
from AlgorithmImports import *

class MomentumAndSMAStrategy(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2014, 1, 1)  # Start Date
        self.SetEndDate(2024, 1, 1)  # End Date
        self.SetCash(10000)  # Set Strategy Cash
        
        self.tickers = ["MSFT", "AAPL", "NVDA", "AMZN", "GOOG", "GOOGL", "META", "BRK/A", "BRK/B", "LLY", "AVGO", "TSLA", "JPM", "V", "WMT", "UNH", "MA", "XOM", "JNJ", "HD", "PG", "COST", "AMD", "MRK", "ABBV", "ORCL", "CRM", "CVX", "BAC", "NFLX", "KO", "ADBE", "PEP", "TMO", "LIN", "MCD", "ABT", "DIS"]
        self.symbols = [Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in self.tickers]
        self.AddEquity("SPY", Resolution.Daily)  # Benchmark
        
        for symbol in self.symbols:
            self.AddEquity(symbol.Value, Resolution.Daily)
        
        self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 10), self.RankStocks)
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), self.TimeRules.BeforeMarketClose("SPY", 10), self.Rebalance)
        
        self.ranked_symbols = []

    def RankStocks(self):
        scores = {}
        for symbol in self.symbols:
            history = self.History(symbol, 210, Resolution.Daily)  # Fetching enough data for 200-day SMA
            
            if history.empty or len(history) < 210:
                self.Log(f"Not enough data for {symbol.Value} to rank")
                continue
            
            # Calculate Indicators
            close = history['close']
            momentum = (close.iloc[-1] / close.iloc[-90]) - 1
            rsi = self.RSI(symbol, 14, MovingAverageType.Wilders, Resolution.Daily, Field.Close).Current.Value
            sma50 = self.SMA(symbol, 50, Resolution.Daily, Field.Close).Current.Value
            sma200 = self.SMA(symbol, 200, Resolution.Daily, Field.Close).Current.Value
            
            # Example scoring formula
            score = momentum + rsi/100 + (sma50 + sma200)/2
            scores[symbol] = score
        
        # Sort symbols by score in descending order and keep the top 10
        self.ranked_symbols = sorted(scores, key=scores.get, reverse=True)[:10]

    def Rebalance(self):
        if len(self.ranked_symbols) == 0:
            self.Log("No ranked symbols to rebalance")
            return
        
        # Calculate portfolio target size for each position
        target_size = 1.0 / len(self.ranked_symbols)
        
        # Sell symbols not in the top 10 anymore
        current_holdings = [s.Symbol for s in self.Portfolio.Values if s.Invested and s.Symbol not in self.ranked_symbols]
        for symbol in current_holdings:
            self.Liquidate(symbol)
        
        # Buy or adjust positions for the top 10 stocks
        for symbol in self.ranked_symbols:
            self.SetHoldings(symbol, target_size)
    
    def OnData(self, data):
        # If you don't need to do anything on each data slice, you can leave this empty
        pass