Overall Statistics
Total Trades
20
Average Win
0%
Average Loss
0%
Compounding Annual Return
-30.234%
Drawdown
0.900%
Expectancy
0
Net Profit
-0.533%
Sharpe Ratio
9.516
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0.086
Beta
-0.051
Annual Standard Deviation
0.008
Annual Variance
0
Information Ratio
-0.59
Tracking Error
0.141
Treynor Ratio
-1.535
Total Fees
$20.00
# (c) Blash16

from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel   # Help us Create.Symbols for manual universes
from config import parameters                                                               # List of parameters defining the algorithm
import universes                                                                            # Universe Selection classes
from universes import Uparam, ManualSymbolsLists, SymbolData
from operator import itemgetter

class BlashAlgorithm(QCAlgorithm):

    def Initialize(self):

        # ------------------------------------ Initialization & setup ----------------------------------------
        
        self.SetTimeZone("America/New_York")
    
        # Back testing only
        self.SetStartDate(parameters["start_date_year"], parameters["start_date_month"], parameters["start_date_day"])  
        self.SetEndDate(parameters["end_date_year"], parameters["end_date_month"], parameters["end_date_day"])  
        
        self.AddEquity(parameters["Benchmark"],Resolution.Daily)
        self.SetBenchmark(parameters["Benchmark"]) 
        
        # In live trading actual cash will be set at the Brokerage level 
        self.SetCash(parameters["StartCash"])
        
        
        # ------------------------------------- Universe creation --------------------------------------------
    
        # self.UniverseSettings.DataNormalizationMode=DataNormalizationMode.Raw   ??? 
        self.UniverseSettings.Resolution = Resolution.Hour  # Universe list of symbols is refreshed Monthly but symbol data is updated Hourly 
        self.UniverseSettings.Leverage = 1
       
        self.AddRiskManagement(TrailingStopRiskManagementModel(parameters["LossSellTrigger"])) # AUTOMATIC STOP-LOSS SELL 
   
        if Uparam["SelectedUniverse"] == "":
            self.AddUniverseSelection(universes.QC500UniverseSelectionModel()) # Auto seleced Universe
        else: # Manual selected Universes
            SymbolsList = ManualSymbolsLists[Uparam["SelectedUniverse"]]
            finalSymbols = []
            for ticker in SymbolsList:
                symbol = Symbol.Create(ticker, SecurityType.Equity, Market.USA)    # ??? What if Market is not USA?
                finalSymbols.append(symbol)
            self.AddUniverseSelection(ManualUniverseSelectionModel(finalSymbols))
        
        # My "Internal" Universe.
        # Contains a list of Symbols and Indicators. ??? History? Fundamentals?
        self.TickerTable = {}
        # Track lost buys because not enough Cash in Portfolio to buy
        self.LostBuys=0
        # Track our buying ticketsize
        self.TicketSizeValue = parameters["StartCash"] / (parameters["MaxNumberOfTickersToInvest"]+parameters["CashBufferNumber"]) 
        # track dates of last time a symbol was sold. Enables us to implement SellCooldown
        self.SoldSymbolDates = {}
    
        self.SetWarmUp(timedelta(days=30))
       
       # ----------------------------------------- Schedules ---------------------------------------------
       
        if parameters["SellLaggards"]:
            self.Schedule.On(self.DateRules.Every(parameters["SellLaggardsFrequency"]), self.TimeRules.At(9, 0), self.SellLaggardsFunction)
        self.Schedule.On(self.DateRules.Every(parameters["SellFrequency"]), self.TimeRules.At(10, 0), self.SellFunction)
        self.Schedule.On(self.DateRules.Every(parameters["BuyFrequency"]), self.TimeRules.At(11, 0), self.BuyFunction)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.Every(timedelta(hours=12)), self.PlotStuff)
        
       # ----------------------------------------- Opening Log--------------------------------------------
        
        self.Debug(f"Blash16: {self.Time}")
        
        if self.LiveMode:
            self.Debug("Trading Live!")
        
        self.Log(f"Init done:{self.Time} Cash:{self.Portfolio.Cash}")
        self.Log(f'Universe:{Uparam["SelectedUniverse"]} MaxInvested:{parameters["MaxNumberOfTickersToInvest"]}')
        self.Log(f'SMAgap:{parameters["SMABuyGapTrigger"]} SellGain:{parameters["GainSellTrigger"]} SellLoss:{parameters["LossSellTrigger"]}')
        self.Log("---")
        
    # ---------------------------------------- New Benchmark plot --------------------------------------------
    
        # Variable to hold the last calculated benchmark value
        self.lastBenchmarkValue = None
        # Our inital benchmark value scaled to match our portfolio
        self.BenchmarkPerformance = self.Portfolio.TotalPortfolioValue
        
    # ------------------------------------------------------------------------------------------------------   
    # ---------------------------------------------- Buy loop ----------------------------------------------
    
    def BuyFunction(self):
        
        # Calculate how many investments (Equities) we currently have 
        investedPortfolio = [x.Key.Value for x in self.Portfolio if x.Value.Invested]
        numberOfSymbolsInvested = len(investedPortfolio)
        
        # Find how many open positions to be invested before we reach our Max limit
        self.toInvest = parameters["MaxNumberOfTickersToInvest"] - numberOfSymbolsInvested 
        
        if self.toInvest == 0:
            return
        
        # ---- check for Candidates
        self.SymbolCandidateList = self.CheckCandidates()
        # ---- sort candidates
        self.RankedSymbolCandidateList = self.RankCandidates(self.SymbolCandidateList)
        # ---- buy best candidates
        self.BuyCandidates(self.RankedSymbolCandidateList)
        
    # ------------------------------------------- Check Candidates ------------------------------------------
    
    def CheckCandidates(self):
        temp_candidate_list = []
        
        for security, symbolData in self.TickerTable.items():
            symbol = security.Symbol
            
            if not self.Portfolio[symbol].Invested or parameters["AllowMultipleBuysBeforeSell"]:
                
                # Check SellCooldown
                if symbol in self.SoldSymbolDates:
                    last_sale_time = self.SoldSymbolDates[symbol]
                    current_time = self.Time
                    if (current_time - last_sale_time).days < parameters["SellCooldown"]:
                        continue
                    
                # Indicators
                RSI = symbolData.rsi.Current.Value
                SMA = symbolData.sma.Current.Value
                PPO = symbolData.ppo.Current.Value
                # Fundamentals
                # Bring Fundanetal data here
                
                # Strategy point: The algo for Candidate selection (based on indicators and fundamentals)
                if ( RSI <= parameters["RSIBuyGapTrigger"]) and (PPO <= parameters["PPOBuyGapTrigger"]) and (SMA > self.Securities[symbol].Price * parameters["SMABuyGapTrigger"]):
                    temp_candidate_list.append(security)
                
        return temp_candidate_list

    # ---------------------------------------- Rank (sort) Candidates ---------------------------------------

    def RankCandidates(self, SymbolCandidateList):
        temp_list = []
        
        for security in SymbolCandidateList:
            temp_list.append([security,
            self.TickerTable[security].rsi.Current.Value,
            self.TickerTable[security].sma.Current.Value,
            self.TickerTable[security].ppo.Current.Value])
        
        # Strategy point: The algo for Candidate sorting (based on indicators and fundamentals we collected)
        if parameters["SortCandidatesby"] == "RSI":
            RankedSymbolCandidateList = sorted(temp_list, key=itemgetter(1))[:self.toInvest] # sort by RSI and return :toInvest best results
        elif parameters["SortCandidatesby"] == "PPO":
            RankedSymbolCandidateList = sorted(temp_list, key=itemgetter(3))[:self.toInvest] # sort by RSI and return :toInvest best results
        elif parameters["SortCandidatesby"] == "PPO&RSI":
            RankedSymbolCandidateList = sorted(temp_list, key=itemgetter(3,1))[:self.toInvest] # sort by RSI and return :toInvest best results
        elif parameters["SortCandidatesby"] == "RSI&PPO":
            RankedSymbolCandidateList = sorted(temp_list, key=itemgetter(1,3))[:self.toInvest] # sort by RSI and return :toInvest best results                
        else:
            RankedSymbolCandidateList = temp_list[:self.toInvest]
        
        return RankedSymbolCandidateList

    # ---------------------------------------- Buy winning Candidates ---------------------------------------

    def BuyCandidates(self, RankedSymbolCandidateList):        
        for record in self.RankedSymbolCandidateList:
            security = record[0]
            symbol = security.Symbol
    
            # collect for taagging Order only
            RSI = record[1]
            SMA = record[2]
            PPO = record[3]
            
            self.TicketSizeValue = self.Portfolio.TotalPortfolioValue / (parameters["MaxNumberOfTickersToInvest"]+parameters["CashBufferNumber"])
            cash = self.Portfolio.MarginRemaining
            
            # SetHoldings(symbol, percentage)
            # 1. quantity = self.CalculateOrderQuantity(symbol, percentage)
            # 2. self.mMarketOrder(symbhol, quantity)
            
            portfolio_percentage = 1 / parameters["MaxNumberOfTickersToInvest"]
            quantity = self.CalculateOrderQuantity(symbol, portfolio_percentage)
            close_price = self.Securities[symbol].Price
            
            cost_of_trade = close_price * quantity
            adj_cost_of_trade = cost_of_trade * (1 + parameters["CashTradeBuffer"])
            
            if cash > adj_cost_of_trade:
                
                #self.Debug(f"margin: {cash} - cost: {cost_of_trade}")
                #self.Debug(len([symbol for symbol in self.Portfolio.Keys if self.Portfolio[symbol].Invested]))
                #self.Debug([(symbol.Value, self.Portfolio[symbol].HoldingsValue) for symbol in self.Portfolio.Keys if self.Portfolio[symbol].Invested])
                
                self.SetHoldings(symbol, 1/ (parameters["MaxNumberOfTickersToInvest"]+parameters["CashBufferNumber"]), False, "Buy: "+
                str(symbol)+ " SMA:"+str(round(SMA,2))+" RSI:"+str(round(RSI,2))+" PPO:"+str(round(PPO,2))+" Cash B4:"+str(round(self.Portfolio.Cash,2))+" toInvest:"+str(self.toInvest))
                
            else:
                self.LostBuys+=1    
        
    # ----------------------------------------- Sell loop -----------------------------------------
    def SellFunction(self):
        
        investedPortfolio = [x.Key.Value for x in self.Portfolio if x.Value.Invested]
        numberOfSymbolsInvested = len(investedPortfolio)
        
        for security, symbolData in self.TickerTable.items():
            symbol = security.Symbol
            if self.Portfolio[symbol].Invested:
                if self.Portfolio[symbol].UnrealizedProfitPercent >= parameters["GainSellTrigger"]:
                    self.Liquidate(symbol, "Profit: "+str(symbol)+" Gain:"+str(round(self.Portfolio[symbol].UnrealizedProfitPercent*100,2))+"%")
                    self.SoldSymbolDates[symbol]=self.Time
                
        
    # ------------------------------------- Sell Laggards loop -------------------------------------
    def SellLaggardsFunction(self):
        
        investedPortfolio = [x.Key.Value for x in self.Portfolio if x.Value.Invested]
        numberOfSymbolsInvested = len(investedPortfolio)
        
        if numberOfSymbolsInvested > 0:
            sortedInvestedPortfolio = sorted(investedPortfolio, key = lambda x: x.Value.UnrealizedProfitPercent, reverse = True)
        
            symbol= sortedInvestedPortfolio[0].symbol
            self.Liquidate(symbol, "Laggard: "+str(symbol)+" Unrealized:"+str(round(self.Portfolio[symbol].UnrealizedProfitPercent*100,2))+"%")
            self.SoldSymbolDates[symbol]=self.Time
        
    
    # -------------------------------------- Plots and Graphs --------------------------------------
    def PlotStuff(self):
        self.Plot('Portfolio Cash', 'Cash', self.Portfolio.Cash)
        self.Plot('Lost buys', 'Lost', self.LostBuys)
        self.Plot('Margin Remaining', 'MarginRemaining', self.Portfolio.MarginRemaining)
        self.Plot('Lost-buys', 'Lost', self.LostBuys)  ##ELI
        self.Plot('Ticket Size', 'Size', self.TicketSizeValue)
        bench = self.Securities[parameters["Benchmark"]].Close
        if self.lastBenchmarkValue is not  None:
           self.BenchmarkPerformance = self.BenchmarkPerformance * (bench/self.lastBenchmarkValue)
        # store today's benchmark close price for use tomorrow
        self.lastBenchmarkValue = bench
        # make our plots
        self.Plot("Strategy vs Benchmark", "Portfolio Value", self.Portfolio.TotalPortfolioValue)
        self.Plot("Strategy vs Benchmark", "Benchmark", self.BenchmarkPerformance)
        
    # ------------------------------------------  On Data  ------------------------------------------
    def OnData(self, data):
        pass

        
    # -------------------------------------- On Securities Change ---------------------------------------
    def OnSecuritiesChanged(self, changes):
        self.changes = changes
        self.Log(f"OnSecuritiesChanged({self.Time}):: {changes}")
        
        for security in changes.RemovedSecurities:
            #self.Liquidate(security)
            self.TickerTable.pop(security, None)
        
        for security in changes.AddedSecurities:
            if security not in self.TickerTable and security.Symbol.Value != parameters["Benchmark"]:
                symbol = security.Symbol
                rsi = self.RSI(symbol, 14, Resolution.Daily)
                sma = self.SMA(symbol, 30, Resolution.Daily)
                ppo = self.PPO(symbol, 4, 14, MovingAverageType.Simple, Resolution.Daily)
                
                symbolData = SymbolData(symbol, rsi, sma, ppo)
                
                self.TickerTable[security] = symbolData
                
                
    # ------------------------------------------- On End --------------------------------------------
    def OnEndOfAlgorithm(self):
        self.Liquidate()
        #for security in self.Portfolio:
        #    if self.Portfolio[security.Symbol].Invested: # ??? 
        #        self.Debug(str(ticker)+" "+str(self.Portfolio[ticker].Quantity))
        
        self.Debug("Cash: "+str(self.Portfolio.Cash))
        self.Debug("Total Holdings Value: "+str(self.Portfolio.TotalHoldingsValue))
        self.Debug("Total Portfolio Value: "+str(self.Portfolio.TotalPortfolioValue))
        self.Debug("Lost buys: "+str(self.LostBuys))
# (c) Blash10
# Confidential

parameters = {}

parameters["StartCash"] = 100000                    # First Cash deposit for the Algorithm
parameters["MaxNumberOfTickersToInvest"] = 20       # Max Number of Equities we will invest in
parameters["CashBufferNumber"] = 1                  # We we allocate investment (% of Holdings) we will allocate to ["MaxNumberOfTickersToInvest"] + ["CashBufferNumber"] so we don't risk too low cash
parameters["CashTradeBuffer"] = 0.02                # Each trade(order) cash buffer

parameters["AllowDebt"] = False                     # ------ To Be implemented. True is we allow the algorithm to buy equity even without sufficient free cash. False is in no condition we allow to be cash negative.
parameters["AllowPortfolioRedestribution"] = False  # ??? if not enough free cash to buy a new stock. Do we sell the relevant part of the portfolio to generate Cash for the buy.
                                                    # if we don't AllowDebt or AllowPortfolioRedistribuion - no Buy will be made and we get a warning message.
parameters["AllowMultipleBuysBeforeSell"] = False   # Can you buy a ticker tou own before you sell it

parameters["TicketSizePctOfPortfolio"] = 0.05   # ??? Percent of total Portfolio Stocks and Cash to be used at a buying ticket value

parameters["SMABuyGapTrigger"] = 1.00       # For Buy current Price should be lower (reverse momentum) than Historic price times BuyPercentTrigger. The higher this number is we'll make more Buys
parameters["RSIBuyGapTrigger"] = 100        # <= For Buy 
parameters["RSITriggerDirection"] = "down"
parameters["PPOBuyGapTrigger"] = 3          # <=
parameters["PPOTriggerDirection"] = "down"

parameters["GainSellTrigger"] = 0.14        # Increase needed to Sell decision --- Tagged "Profit"
parameters["LossSellTrigger"] = 0.10         # Loss needed for Sell decision  --- Tagged "Loss"
parameters["SellCooldown"] = 0                  #  ??? How many trading days the Algoritm must wait before it is allowed to consider buying the Stock again.


parameters["SortCandidatesby"] = "PPO&RSI"  ##  this is a whitelist. only 'RSI', 'PPO', 'SMA', 'RSI&PPO', 'PPO&RSI' allowed. add here if new options


parameters["BuyFrequency"] = [DayOfWeek.Monday,DayOfWeek.Tuesday,DayOfWeek.Wednesday,DayOfWeek.Thursday,DayOfWeek.Friday]
parameters["SellFrequency"] = [DayOfWeek.Monday,DayOfWeek.Tuesday,DayOfWeek.Wednesday,DayOfWeek.Thursday,DayOfWeek.Friday]
parameters["SellLaggardsFrequency"] = [DayOfWeek.Monday]
parameters["SellLaggards"] = True

# Back testing dates
parameters["start_date_year"] = 2020
parameters["start_date_month"] = 1
parameters["start_date_day"] = 1
parameters["end_date_year"] = 2020
parameters["end_date_month"] = 12
parameters["end_date_day"] = 31

parameters["Benchmark"] = "SPY"


parameters["AllowMultipleBuysBeforeSell"] = False


parameters["MonkeyBuyer"] = False
parameters["MonkeySeller"] = False
parameters["SMA days"] = 30
#
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel

Uparam = {}

Uparam["SelectedUniverse"]= "nasdaq100_A2Z" # if blank go for Automatic Universe selection
Uparam["CoarseUniverseSize"] = 4000
Uparam["FineUniverseSize"] =102
Uparam["UniverseMinMarketCap"] = 1e9
Uparam["PrimaryExchangeID"] = ["NAS"]
Uparam["UniverseSectors"] = [MorningstarSectorCode.BasicMaterials,
                            MorningstarSectorCode.ConsumerCyclical,
                            MorningstarSectorCode.FinancialServices,
                            MorningstarSectorCode.RealEstate,
                            MorningstarSectorCode.ConsumerDefensive,
                            MorningstarSectorCode.Healthcare,
                            MorningstarSectorCode.Utilities,
                            MorningstarSectorCode.CommunicationServices,
                            MorningstarSectorCode.Energy,
                            MorningstarSectorCode.Industrials,
                            MorningstarSectorCode.Technology]

ManualSymbolsLists = {"nasdaq100_A2Z":["AAPL","ADBE","ADI","ADP","ADSK","AEP","ALGN","ALXN","AMAT","AMD","AMGN","AMZN","ANSS","ASML","ATVI","AVGO","BIDU","BIIB","BKNG","CDNS","CDW","CERN","CHKP","CHTR","CMCSA","COST","CPRT","CSCO","CSX","CTAS","CTSH","DLTR","DOCU","DXCM","EA","EBAY","EXC","FAST","FB","FISV","FOX","FOXA","GILD","GOOG","GOOGL","IDXX","ILMN","INCY","INTC","INTU","ISRG","JD","KDP","KHC","KLAC","LRCX","LULU","MAR","MCHP","MDLZ","MELI","MNST","MRNA","MRVL","MSFT","MTCH","MU","MXIM","NFLX","NTES","NVDA","NXPI","OKTA","ORLY","PAYX","PCAR","PDD","PEP","PTON","PYPL","QCOM","REGN","ROST","SBUX","SGEN","SIRI","SNPS","SPLK","SWKS","TCOM","TEAM","TMUS","TSLA","TXN","VRSK","VRSN","VRTX","WBA","WDAY","XEL","XLNX","ZM"],
                        
                        "nasdaq100_S2L":["FOX","FOXA","CHKP","CDW","TCOM","INCY","VRSN","CERN","MXIM","SIRI","DLTR","SWKS","CPRT","FAST","SPLK","PAYX","VRSK","ANSS","SGEN","ORLY","CTAS","OKTA",
                        "PCAR","XEL","ALXN","XLNX","MRVL","DXCM","MTCH","CDNS","EBAY","MAR","KHC","MCHP","ROST","AEP","WBA","SNPS","BIIB","EXC","IDXX","ALGN","EA","CTSH","KDP","LULU","MNST",
                        "PTON","KLAC","NXPI","DOCU","MRNA","WDAY","REGN","ADI","TEAM","ILMN","VRTX","ADSK","CSX","ADP","FISV","ATVI","NTES","MDLZ","LRCX","GILD","BKNG","BIDU","ISRG","MU","AMAT",
                        "MELI","INTU","AMD","ZM","SBUX","CHTR","JD","AMGN","TXN","COST","TMUS","QCOM","AVGO","CSCO","PEP","PDD","CMCSA","ADBE","INTC","ASML","NFLX","PYPL","NVDA","FB","TSLA",
                        "GOOGL","GOOG","AMZN","MSFT","AAPL"],

                        "nasdaq100_L2S":["AAPL","MSFT","AMZN","GOOG","GOOGL","TSLA","FB","NVDA","PYPL","NFLX","ASML","INTC","ADBE","CMCSA","PDD","PEP","CSCO","AVGO","QCOM","TMUS","COST","TXN","AMGN","JD","CHTR","SBUX","ZM","AMD","INTU","MELI","AMAT","MU","ISRG","BIDU","BKNG","GILD","LRCX","MDLZ","NTES","ATVI","FISV","ADP","CSX","ADSK","VRTX","ILMN","TEAM","ADI","REGN","WDAY","MRNA","DOCU","NXPI","KLAC","PTON","MNST","LULU","KDP","CTSH","EA","ALGN","IDXX","EXC","BIIB","SNPS","WBA","AEP","ROST","MCHP","KHC","MAR","EBAY","CDNS","MTCH","DXCM","MRVL","XLNX","ALXN","XEL","PCAR","OKTA","CTAS","ORLY","SGEN","ANSS","VRSK","PAYX","SPLK","FAST","CPRT","SWKS","DLTR","SIRI","MXIM","CERN","VRSN","INCY","TCOM","CDW","CHKP","FOXA","FOX"],

                        "debug_list":["MSFT"]
                        }

 # ------------------------------------- Symbol Data -----------------------------------------

class SymbolData:
    def __init__(self, symbol, rsi, sma, ppo):
        self.Symbol = symbol
        self.rsi = rsi
        self.sma = sma
        self.ppo = ppo

 
 # ------------------------------ QC500UniverseSelectionModel ---------------------------------

class QC500UniverseSelectionModel(FundamentalUniverseSelectionModel):
    '''Defines the QC500 universe as a universe selection model for framework algorithm
    For details: https://github.com/QuantConnect/Lean/pull/1663'''

    def __init__(self, filterFineData = True, universeSettings = None, securityInitializer = None):
        '''Initializes a new default instance of the QC500UniverseSelectionModel'''
        super().__init__(filterFineData, universeSettings, securityInitializer)
        self.numberOfSymbolsCoarse = Uparam["CoarseUniverseSize"]
        self.numberOfSymbolsFine = Uparam["FineUniverseSize"]
        self.sorted1BySymbol = {} 
        self.lastMonth = -1

    def SelectCoarse(self, algorithm, coarse):
        '''coarse selection
        Must have fundamental data
        Must have positive previous-day close price
        Must have positive volume on the previous trading day
        Sort by tarde Dollar Volume (previous day)
        '''
        
        if algorithm.Time.month == self.lastMonth:
            return Universe.Unchanged
        self.lastMonth = algorithm.Time.month
            
        sorted1 = sorted([x for x in coarse if x.HasFundamentalData and x.Volume > 0 and x.Price >0],
                        key = lambda x: x.DollarVolume, reverse=True)[:self.numberOfSymbolsCoarse]
        self.sorted1BySymbol = {x.Symbol:x.DollarVolume for x in sorted1}
        
        # If no security has met the QC500 criteria, the universe is unchanged.
        # A new selection will be attempted on the next trading day as self.lastMonth is not updated
        count = len(self.sorted1BySymbol)
        algorithm.Debug("U coarse:"+str(count))
        if count == 0:
            return Universe.Unchanged

        # return the symbol objects our sorted collection
        return list(self.sorted1BySymbol.keys())

    def SelectFine(self, algorithm, fine):
        
        # fine is a list of FineFundamental objects
        # expects is to return a list of Symbol objects
        
        # Sorts our list
        sortedBySector = sorted([x for x in fine if x.CompanyReference.PrimaryExchangeID in Uparam["PrimaryExchangeID"]
                                        #and x.CompanyReference.CountryId == "USA"
                                        #and (algorithm.Time - x.SecurityReference.IPODate).days > 30
                                        #and x.ValuationRatios.ForwardPERatio > 0
                                        and x.AssetClassification.MorningstarSectorCode in Uparam["UniverseSectors"]
                                        and x.MarketCap >= Uparam["UniverseMinMarketCap"]],
                               key = lambda x: x.MarketCap, reverse = True)
                               

        count = len(sortedBySector)
        algorithm.Debug("U fine:"+str(count))
        if count == 0:
            return Universe.Unchanged
        
        # converts our list of fine to a list of Symbols 
        sortedBySectorSymbols = [f.Symbol for f in sortedBySector]
        
        # logging fine Universe ???
        #algorithm.Debug(str(algorithm.Time)+" " +str(len(sortedBySectorSymbols)))
        #counter = self.numberOfSymbolsFine
        #for security in sortedBySectorSymbols:
        #    algorithm.Debug(security)
        #    counter +=1
        #    if counter == self.numberOfSymbolsFine:
        #        break
       
        return sortedBySectorSymbols[:self.numberOfSymbolsFine]