Overall Statistics
# https://quantpedia.com/Screener/Details/199
class ROAEffectWithinStocks(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2014, 1, 1)  
        self.SetEndDate(2018, 8, 1)  
        self.SetCash(10000000) 
        self.AddEquity("SPY", Resolution.Daily)
        self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.AfterMarketOpen("SPY"), self.Rebalance)
        self.monthly_rebalance = False 
        self.coarse = False
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
        
    def CoarseSelectionFunction(self, coarse):
        if self.monthly_rebalance:
            self.coarse = True
            filteredCoarse = [x.Symbol for x in coarse if x.HasFundamentalData]
            return filteredCoarse
        else:
            return []
    
    def FineSelectionFunction(self, fine):
        if self.monthly_rebalance:
            fine =[i for i in fine if i.EarningReports.BasicAverageShares.ThreeMonths != 0
                                  and i.EarningReports.BasicEPS.TwelveMonths != 0
                                  and i.ValuationRatios.PERatio != 0
                                  # sales is greater than 10 million
                                  and i.ValuationRatios.SalesPerShare*i.EarningReports.DilutedAverageShares.Value > 10000000
                                  and i.OperationRatios.ROA.Value != 0]
            for i in fine:
                i.MarketCap = float(i.EarningReports.BasicAverageShares.ThreeMonths * (i.EarningReports.BasicEPS.TwelveMonths*i.ValuationRatios.PERatio))
            # sort into 2 halfs based on market capitalization
            sorted_market_cap = sorted(fine, key = lambda x:x.MarketCap, reverse=True)
            top = sorted_market_cap[:int(len(sorted_market_cap)*0.5)]
            bottom = sorted_market_cap[-int(len(sorted_market_cap)*0.5):]
            # each half is then divided into deciles based on Return on Assets (ROA)
            sortedTopByROA = sorted(top, key = lambda x: x.OperationRatios.ROA.Value, reverse = True)
            sortedBottomByROA = sorted(bottom, key = lambda x: x.OperationRatios.ROA.Value, reverse = True)
            # long top decile from each market capitalization group 
            long = sortedTopByROA[:int(len(sortedTopByROA)*0.1)] + sortedBottomByROA[:int(len(sortedTopByROA)*0.1)]
            self.longStocks = [i.Symbol for i in long]
            # short bottom decile from each market capitalization group 
            short = sortedTopByROA[-int(len(sortedTopByROA)*0.1):] + sortedBottomByROA[-int(len(sortedTopByROA)*0.1):]
            self.shortStocks = [i.Symbol for i in short]
            
            return self.longStocks+self.shortStocks
        else:
            return []
    
    def Rebalance(self):
        self.monthly_rebalance = True
            
    def OnData(self, data):
        if not (self.monthly_rebalance and self.coarse): return
        self.coarse = False
        self.monthly_rebalance = False
        stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested]
        for i in stocks_invested:
            if i not in self.longStocks+self.shortStocks:
                self.Liquidate(i)

        long_weight = 0.5/len(self.longStocks)
        for i in self.longStocks:
            self.SetHoldings(i, long_weight)

        short_weight = 0.5/len(self.shortStocks)
        for i in self.shortStocks:
            self.SetHoldings(i, -short_weight)