Overall Statistics
# https://quantpedia.com/Screener/Details/51

import numpy as np
class MomentumShortTermReversalAlgorithm(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2004,1,1) 
        self.SetEndDate(2018,5,10)    
        self.SetCash(100000)         

        self.UniverseSettings.Resolution = Resolution.Daily

        self.AddEquity("SPY", Resolution.Daily)
        self.AddUniverse(self.CoarseSelectionFunction)
        self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.At(0, 0), self.Rebalance)
        self.month_start = False
        self.coarse = False
        self.SymbolPrice = {}
        self.decrease_winner = None
        self.increase_loser = None

        

    def CoarseSelectionFunction(self, coarse):
        if self.month_start:
            self.coarse = True
            # coarse = [i for i in coarse if i.AdjustedPrice > 5]
            for i in coarse:
                if i.Symbol not in self.SymbolPrice:
                    self.SymbolPrice[i.Symbol] = SymbolData(i.Symbol)
                
                self.SymbolPrice[i.Symbol].window.Add(float(i.AdjustedPrice))
                if self.SymbolPrice[i.Symbol].window.IsReady:
                    price = np.array([i for i in self.SymbolPrice[i.Symbol].window])
                    returns = (price[:-1]-price[1:])/price[1:]
                    self.SymbolPrice[i.Symbol].yearly_return = (price[0]-price[-1])/price[-1]
                    GARR_12 = np.prod([(1+i)**(1/12) for i in returns])-1
                    GARR_1 = (1+returns[0])**(1/12)-1
                    self.SymbolPrice[i.Symbol].GARR_ratio = GARR_1 / GARR_12

            ReadySymbolPrice = {symbol: SymbolData for symbol, SymbolData in self.SymbolPrice.items() if SymbolData.window.IsReady}
            if ReadySymbolPrice and len(ReadySymbolPrice)>50:        
                sorted_by_return = sorted(ReadySymbolPrice, key = lambda x: ReadySymbolPrice[x].yearly_return, reverse = True)
                winner = sorted_by_return[:int(len(sorted_by_return)*0.3)]
                loser = sorted_by_return[-int(len(sorted_by_return)*0.3):]
                self.decrease_winner = sorted(winner, key = lambda x: ReadySymbolPrice[x].GARR_ratio)[:15] 
                self.increase_loser = sorted(loser, key = lambda x: ReadySymbolPrice[x].GARR_ratio)[-15:] 
                return self.decrease_winner+self.increase_loser
            else:
                return []
        else:
            return []
                

    def OnData(self, data):
        if self.month_start and self.coarse:
            self.month_start = False
            self.coarse = False
            
            if all([self.decrease_winner, self.increase_loser]):
                
                stocks_invested = [x.Key for x in self.Portfolio]
                for i in stocks_invested:
                    if i not in self.decrease_winner+self.increase_loser:
                        self.Liquidate(i) 
                
                short_weight = 0.5/len(self.increase_loser)
                for j in self.increase_loser:
                    self.SetHoldings(j, -short_weight)
                    
                long_weight = 0.5/len(self.decrease_winner)
                for i in self.decrease_winner:
                    self.SetHoldings(i, long_weight)


    def Rebalance(self):
        self.month_start = True

 
class SymbolData:
    def __init__(self, symbol):
        self.symbol = symbol
        self.window = RollingWindow[float](13)
        self.GARR_ratio = None
        self.yearly_return = None