Overall Statistics
#This is a Template of dynamic stock selection.
#You can try your own fundamental factor and ranking method by editing the CoarseSelectionFunction and FineSelectionFunction

from QuantConnect.Data.UniverseSelection import *
from sklearn.datasets import load_iris
from sklearn import preprocessing
import subfactors

class BasicTemplateAlgorithm(QCAlgorithm):

            
    def __init__(self):
    # set the flag for rebalance
        self.reb = 1
        self.months = -1
    # Number of stocks to pass CoarseSelection process
        self.num_coarse = 400
    # Number of stocks to long - not used
        self.symbols = None
    #Declare empty score lists
        self.long_score_q = []
    
    def Initialize(self):
        self.SetCash(1000000)
        self.SetStartDate(2005,12,29)
    # if not specified, the Backtesting EndDate would be today 
        self.SetEndDate(2006,3,1)
        #Set benchmark
        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
        self.SetBenchmark("SPY")
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction,self.FineSelectionFunction)
    
        
        
    # Schedule the rebalance function to execute at the begining of each month or week
        #self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), 
        #self.TimeRules.AfterMarketOpen(self.spy,20), Action(self.rebalance))
        self.Schedule.On(self.DateRules.MonthStart(self.spy), 
        self.TimeRules.AfterMarketOpen(self.spy,20), Action(self.rebalance))
   
    def OnData(self, data):
        pass
                  
    
    def CoarseSelectionFunction(self, coarse):
    # if the rebalance flag is not 1, return null list to save time.
        if self.reb != 1:
            return self.longq + self.shortq 
    # make universe selection once a month
    # drop stocks which have no fundamental data or have too low prices
        selected = [x for x in coarse if (x.HasFundamentalData) 
                    and (float(x.Price) > 1)]
                    
        sortedByDollarVolume = sorted(selected, key=lambda x: x.DollarVolume, reverse=True) 
        top = sortedByDollarVolume[:self.num_coarse]
        #self.Log(top[-1].DollarVolume)
        
        return [i.Symbol for i in top]
        
    def FineSelectionFunction(self, fine):
    # return null list if it's not time to rebalance
        if self.reb != 1:
            return self.longq + self.shortq 
            
        self.reb = 0
        data_issues = {"MAA", "FR", "FRT"}
        filtered_fine = [x for x in fine if x.OperationRatios.DebttoAssets.Value
                                        and x.OperationRatios.ROA.Value
                                        and x.OperationRatios.ROA5YrAvg.Value
                                        and x.OperationRatios.OperationMargin.Value
                                        and x.OperationRatios.OperationIncomeGrowth.FiveYears
                                        and x.ValuationRatios.PERatio
                                        and x.OperationRatios.CurrentRatio.Value
                                        and x.AssetClassification.MorningstarIndustryGroupCode != MorningstarIndustryGroupCode.Banks
                                        and x.AssetClassification.MorningstarIndustryGroupCode != MorningstarIndustryGroupCode.REITs
                                        and (x.CompanyReference.CountryId == "USA")
                                        and x.CompanyReference.PrimaryExchangeID in ["NYS","NAS"]
                                        and (x.Symbol.Value not in data_issues)
                                        ]
        bankscoarse = [x for x in fine if      x.AssetClassification.MorningstarIndustryGroupCode == MorningstarIndustryGroupCode.Banks
                                        and (not x.SecurityReference.IsDepositaryReceipt)
                                        and (x.CompanyReference.CountryId == "USA")
                                        and x.CompanyReference.PrimaryExchangeID in ["NYS","NAS"]
                                        and (x.Symbol.Value not in data_issues)
                                        ]
        banks = [x for x in bankscoarse if  x.ValuationRatios.TangibleBookValuePerShare
                                        and x.FinancialStatements.TotalRiskBasedCapital.Value
                                        and x.OperationRatios.ROE.Value
                                        and x.OperationRatios.ROE5YrAvg.Value
                                        
        ]
        # put REITs in seperate category
        REITs_coarse = [x for x in fine if      x.AssetClassification.MorningstarIndustryGroupCode == MorningstarIndustryGroupCode.REITs
                                        and (not x.SecurityReference.IsDepositaryReceipt)
                                        and (x.CompanyReference.CountryId == "USA")
                                        and x.CompanyReference.PrimaryExchangeID in ["NYS","NAS"]
                                        and (x.Symbol.Value not in data_issues)
                                        ]
        REITs = [x for x in REITs_coarse if  x.ValuationRatios.FFOPerShare
                                        and x.OperationRatios.FinancialLeverage.Value
                                        and x.OperationRatios.ROE5YrAvg.Value
                                        
        ]

        
        # Here I do things
        
        
        #Put factors as scores
        REITs_score = {}
        bank_score = {}
        all_score = {}

        #Simplified        
        for i,ele in enumerate(filtered_fine):

            all_score[ele] = i
            

        for i,ele in enumerate(banks):

            bank_score[ele] = i

        
        for i,ele in enumerate(REITs):
            REITs_score[ele] = i
        
        min_all = min(all_score.values())
        max_all = max(all_score.values())
        for i,ele in enumerate(all_score):
            all_score[ele] = (all_score[ele]-min_all) / (max_all - min_all)

        
        min_bank = min(bank_score.values())
        max_bank = max(bank_score.values())
        for i,ele in enumerate(bank_score):
            bank_score[ele] = (bank_score[ele]-min_bank) / (max_bank - min_bank)
        
        
        min_REIT = min(REITs_score.values())
        max_REIT = max(REITs_score.values())
        for i,ele in enumerate(REITs_score):
            REITs_score[ele] = (REITs_score[ele]-min_REIT) / (max_REIT - min_REIT)
        
        all_stocks = {**bank_score, **REITs_score, **all_score}

        # sort the stocks by their ranks low to high
        self.sorted_stockvq = sorted(all_stocks.items(), key=lambda d:d[1],reverse=False)
        sorted_score = [x[0] for x in self.sorted_stockvq]
        sorted_vq_score = [x[1] for x in self.sorted_stockvq]
        
        # sort the top stocks into each factor
        sliced = int(round(len(sorted_score)/10))
        self.longq = [x.Symbol for x in sorted_score[:sliced]]
        self.long_score_q = [x for x in sorted_vq_score[:sliced]]
        self.shortq = [x.Symbol for x in sorted_score[-sliced:]]
        self.short_score_q = [x for x in sorted_vq_score[-sliced:]]
        return self.longq + self.shortq 

    
    def rebalance(self):
    # if this month the stock are not going to be long, liquidate it.
            
        total_list = self.longq + self.shortq
        long_score = self.long_score_q
        total_list.clear()    
        long_list =  self.longq   
        short_list = self.shortq
        short_score = self.short_score_q
        
        maxwgt = 0.2
        slope = 0.55
        gross_exposure = 0.85
        gross_exp = gross_exposure
        
        sum_long_score =0
        sum_short_score =0
        total_wgts = []
        #long_wgts = []
        i =0
        num_short =0
            
        """while gross_exp > 0 and maxwgt-(slope*(long_score[i]**2)) > 0:
        #for i in range(0, len(long_score)):
            weight = maxwgt-(slope*(long_score[i]**2))
            total_wgts.append(weight)
            total_list.append(long_list[i])
            gross_exp -= weight
            i += 1"""
            
            
        """gross_exp = 0.85   
        k = len(short_score)-1
        while gross_exp > 0 and maxwgt-(slope*(short_score[k]**2)) < 0: 
            weight = maxwgt-(slope*(short_score[k]**2))
            total_wgts.append(weight)
            total_list.append(short_list[k])
            gross_exp += weight
            k -= 1"""

            #sum_short_score += max(short_score[i]**0.75,0.15) 
        for i in range(0, len(long_score)):
            sum_long_score += (1/max(long_score[i]**0.75,0.15))
                
        for i in range(0, len(long_score)):
            total_wgts.append((1/max(long_score[i]**0.75,0.15))/sum_long_score)
            
        """for i in range(0, len(short_score)):
            short_wgts.append(1/len(short_score))
                #short_wgts.append(max(short_score[i]**0.75,0.15)/sum_short_score)
             """    
        
        total_list = long_list #+ short_list
            
   
            
        for i in self.Portfolio.Values:
            if (i.Invested) and (i.Symbol not in total_list) and (i.Symbol.Value != "SPY"):
                self.Liquidate(i.Symbol)
  
    # Assign each stock equally 
        k = 0
        for i in total_list:
            #self.Log(";Buying;" +str(long_wgts[k]) + ";of;"+ str(i) + ";with score;" + str(long_score[k]))
            self.SetHoldings(i, total_wgts[k]*gross_exposure)
            self.Log(i)
            self.Log(total_wgts[k])
            self.Log(long_score[k])
            k+=1
        #k = 0
        """for i in short_list:
            #self.Log(";Shorting;" +str(short_wgts[k]) + ";of;"+ str(i) + ";with score;" + str(short_score[k]))
            self.SetHoldings(i, -short_wgts[k]*gross_exposure)
            sum_short += -short_wgts[k]*gross_exposure
            k+=1"""
        self.SetHoldings("SPY", -gross_exposure)
            
        self.reb = 1
class subfactor():

    def normalise(data):
        min_data = min(data)
        max_data = max(data)
        for i in range(0, len(data)):
            data[i] = (data[i]-min_data) / (max_data - min_data)
            
        return data
# Your New Python File