Hi All, I'm fairly new to QuantConnect, but really enjoying the power it gives me to tailor my algorithms to the detail that I'd want. I'm working on Universe Selection for a trend following strategy right now, and want to do the following:

Filters:

  • Average Daily Dollar Volume for last 20 days > $50M
  • Price > 5
  • SMA(Close, 25) > SMA(Close, 50)

 

Ranking:

  • Rank by highest price percentage increase over last 200 trading days: Close / Close[200]

 

I have no clue if I'm doing this in the most efficient way. Specifically, I'm pulling history data for every security on a daily basis in order to compute the rank score.

I also realize that since I'm using indicators, I probably need to warm up the data so that I have some values on the start date. I ran SetWarmUp in the Initialize method, but not sure if that data is getting sent to the CoarseFilter method or not. It didn't seem like it was working. Worst case, I don't mind waiting for the indicators to be ready, but I wasn't sure how to make that work with Universe selection either.

Any help to solve the filtering and ranking I'm trying to do in the best way would be greatly appreciated!

Thanks,

Chetan

 

# region imports
from AlgorithmImports import *
# endregion

class TrendFollowing(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2022, 1, 1)  # Set Start Date
        self.SetEndDate(2022, 9, 25)
        self.SetCash(100000)  # Set Strategy Cash

        self.UniverseSettings.Resolution = Resolution.Daily
        self.stateData = {}

        #self.SetWarmUp(50, Resolution.Daily)

        self.AddUniverse(self.CoarseFilter)

    def CoarseFilter(self, coarse: List[CoarseFundamental]) -> List[Symbol]:
        for c in coarse:
            if c.Symbol not in self.stateData:
                self.stateData[c.Symbol] = SelectionData(c.Symbol, c.HasFundamentalData)

            avg = self.stateData[c.Symbol]
            
            try:
                price_200 = self.History(c.Symbol, 200, Resolution.Daily)["close"]
                avg.update(c.Time, c.Price, c.DollarVolume, price_200[-1])
            except:
                continue

        symbols = [x for x in self.stateData.values() if x.sma_dv_20.Current.Value > 5000000and x.price > 5 and x.has_fundamental_data and x.sma_price_25.Current.Value > x.sma_price_50.Current.Value]

        symbols.sort(key=lambda x: x.price_200_ratio, reverse=True)                                                        

        list_of_symbols = [x.symbol for x in symbols]
        return list_of_symbols
        

    def onData(self, data):
        pass

class SelectionData(object):
    def __init__(self, symbol, has_fundamental_data):
        self.symbol = symbol
        self.has_fundamental_data = has_fundamental_data
        self.price = 0.0
        self.price_200_ratio = 0.0
        
        self.sma_dv_20 = SimpleMovingAverage(20)
        self.sma_price_25 = SimpleMovingAverage(25)
        self.sma_price_50 = SimpleMovingAverage(50)

    def update(self, time, price, dollar_volume, price_200):
        self.sma_dv_20.Update(time, dollar_volume)
        self.sma_price_25.Update(time, price)
        self.sma_price_50.Update(time, price)
        self.price = price
        
        self.price_200_ratio = price / price_200