Overall Statistics
from clr import AddReference
AddReference('System')
AddReference('QuantConnect.Common')
AddReference('QuantConnect.Indicators')
AddReference('QuantConnect.Algorithm.Framework')

from QuantConnect.Data.UniverseSelection import *
from QuantConnect.Indicators import SimpleMovingAverage
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel

class CostumUniverseSelectionModel(FundamentalUniverseSelectionModel):

    def __init__(self, filterFineData = True, universeSettings = None, securityInitializer = True):
        super().__init__(filterFineData, universeSettings, securityInitializer)
        self.NumberOfSymbolsCoarse = 1000
        self.NumberOfSymbolsFine = 500
        self.dollarVolumeBySymbol = {}
        self.symbols = []
    
    def SelectCoarse(self, algorithm, coarse):
        coarse = list(coarse)
        
        if len(coarse) == 0:
            return self.symbols
        
        filtered = [x for x in coarse if x.HasFundamentalData and x.Volume > 100000]
        top = sorted(filtered, key = lambda x: x.DollarVolume, reverse = True)[:self.NumberOfSymbolsCoarse]
        self.dollarVolumeBySymbol = {i.Symbol: i.DollarVolume for i in top}
        self.symbols = [self.dollarVolumeBySymbol.keys()]
        
        return self.symbols
    
    def SelectFine(self, algorithm, fine):
        filteredFine = [x for x in fine if x.ValuationRatios.FCFRatio < 20
                                        and x.OperationRatios.ROIC.OneYear > 0
                                        and x.OperationRatios.ROE.OneYear > 0
                                        and x.OperationRatios.TotalDebtEquityRatio.OneYear < 1]
        sortedByROE = sorted(filteredFine, key = lambda x: x.OperationRatios.ROE.OneYear, reverse = True)[:self.NumberOfSymbolsFine]
        self.symbols = [f.Symbol for f in sortedByROE]
        
        return self.symbols
        
class SMAUniverseSelectionModel(FundamentalUniverseSelectionModel):
    
    def __init__(self, fastPeriod = 20, intermediatePeriod = 50, slowPeriod = 100, universeCount = 500, universeSettings = None, securityInitializer = None):
        super().__init__(False, universeSettings, securityInitializer)
        self.fastPeriod = fastPeriod
        self.intermediatePeriod = intermediatePeriod
        self.slowPeriod = slowPeriod
        self.universeCount = universeCount
        self.tolerance = 0.01
        self.averages = {}
    
    def SelectCoarse(self, algorithm, coarse):
        filtered = []
        
        for cf in coarse:
            if cf.Symbol not in self.averages:
                self.averages[cf.Symbol] = self.SelectionData(cf.Symbol, self.fastPeriod, self.intermediatePeriod, self.slowPeriod)
            
            avg = self.averages.get(cf.Symbol)
            
            if avg.Update(cf.EndTime, cf.AdjustedPrice) and self.Securities[cf].Price > avg.fast * (1 + self.tolerance) and avg.fast > avg.intermediate * (1 + self.tolerance) and avg.intermediate > avg.slow * (1 + self.tolerance):
                filtered.append(avg)
        
        filtered = sorted(filtered, key = lambda avg: avg.ScaledDelta, reverse = True)
        
        return [x.Symbol for x in filtered[:self.universeCount]]

class SelectionData:
    def __init__(self, symbol, fastPeriod, intermediatePeriod, slowPeriod):
        self.Symbol = symbol
        self.fastSma = SimpleMovingAverage(fastPeriod)
        self.intermediateSma = SimpleMovingAverage(intermediatePeriod)
        self.slowSma = SimpleMovingAverage(slowPeriod)
    
    @property
    def Fast(self):
        return float(self.fastSma.Current.Value)
    
    @property
    def intermediate(self):
        return float(self.intermediateSma.Current.Value)
    
    @property
    def slow(self):
        return float(self.slowSma.Current.Value)
    
    @property
    def ScaledDelta(self):
        return (self.fast - self.intermediate) / ((self.fast + self.intermediate) / 2)
    
    def Update(self, time, value):
        return self.fastSma.Update(time, value) & self.intermediateSma.Update(time, value) & self.slowSma.Update(time, value)