Overall Statistics
from math import ceil,floor


class CoarseFineFundamentalComboAlgorithm(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2009,1,2)  # Set Start Date
        self.SetEndDate(2017,5,2)    # Set End Date
        self.SetCash(50000)          # Set Strategy Cash
        
        self.AddUniverseSelection(
            FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction)
        )
        self.AddAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(30)))
        
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(lambda time:None))
        
        self.AddEquity("SPY")
        self.numberOfSymbols = 300
        self.numberOfSymbolsFine = 10
        self.num_portfolios = 6
        
        self.curr_month = self.Time.month
        
    def CoarseSelectionFunction(self, coarse):
        
        if self.curr_month == self.Time.month:
            return Universe.Unchanged
        
        self.curr_month = self.Time.month
        
        CoarseWithFundamental = [x for x in coarse if x.HasFundamentalData]
        sortedByDollarVolume = sorted(CoarseWithFundamental, key=lambda x: x.DollarVolume, reverse=True) 
        top = sortedByDollarVolume[:self.numberOfSymbols]
        return [i.Symbol for i in top]
        
    def FineSelectionFunction(self, fine):
        
        filtered_fine = [x for x in fine if x.EarningReports.TotalDividendPerShare.ThreeMonths
                                        and x.ValuationRatios.PriceChange1M 
                                        and x.ValuationRatios.BookValuePerShare
                                        and x.ValuationRatios.FCFYield]

        sortedByfactor1 = sorted(filtered_fine, key=lambda x: x.EarningReports.TotalDividendPerShare.ThreeMonths, reverse=True)
        sortedByfactor2 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.PriceChange1M, reverse=False)
        sortedByfactor3 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.BookValuePerShare, reverse=True)
        sortedByfactor4 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.FCFYield, reverse=True)
        
        num_stocks = floor(len(filtered_fine)/self.num_portfolios)
    
        stock_dict = {}
        
        for i,ele in enumerate(sortedByfactor1):
            rank1 = i
            rank2 = sortedByfactor2.index(ele)
            rank3 = sortedByfactor3.index(ele)
            rank4 = sortedByfactor4.index(ele)
            score = [ceil(rank1/num_stocks),
                     ceil(rank2/num_stocks),
                     ceil(rank3/num_stocks),
                     ceil(rank4/num_stocks)]
            score = sum(score)
            stock_dict[ele] = score
        #self.Log("score" + str(score))
        self.sorted_stock = sorted(stock_dict.items(), key=lambda d:d[1],reverse=True)
        sorted_symbol = [self.sorted_stock[i][0] for i in range(len(self.sorted_stock))]
        topFine = sorted_symbol[:self.numberOfSymbolsFine]
        
        return [i.Symbol for i in topFine]