Overall Statistics
import pandas as pd
class SleepyYellowGreenTapir(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 1, 2)
        self.SetEndDate(2020, 1, 10)
        self.SetCash(100000) 
        self.AddUniverse(self.CoarseFilter, self.FineFilter)
        self.UniverseSettings.Resolution = Resolution.Daily
        
        self.macd_indicator = dict()
        
        self.capacity = 20

        # 5. Hedge stratigic assets
        # 6. Schedule functions
    #     self.Schedule.On(
    #         self.DateRules.EveryDay(),
    #         self.TimeRules.At(14, 40),
    #         self.MonitorAndExit
    #     )
        
    #     self.Schedule.On(
    #         self.DateRules.EveryDay(),
    #         self.TimeRules.At(14, 50),
    #         self.MonitorAndEnter
    #     )
    
    # def MonitorAndExit(self):
    #     self.Debug("MonitorAndExit fired at: {0}".format(self.Time))
        
    # def MonitorAndEnter(self):
    #     self.Debug("MonitorAndEnter fired at: {0}".format(self.Time))
        
    #     for key, value in self.macd_indicator.items():
    #         if not value.IsReady:
    #             self.Debug(f'MACD {key} warm up not ready')
    #             return
    #     else:
    #         self.Debug(f'MACD warm up are ready!!!')
            
    #     invested_list = [x.Key for x in self.Portfolio if x.Value.Invested]
    #     invested = len(invested_list)
        
    #     open_order = 0
    #     for key, value in self.macd_indicator.items():
    #         if invested + open_order >= self.capacity:
    #             break
    #         if self.macd_indicator[key].Histogram.Current.Value > 0:
    #             self.SetHoldings(key, 1/self.capacity)
    #             open_order += 1
        
    def CoarseFilter(self, coarse):
        return [x.Symbol for x in coarse if x.HasFundamentalData]

    def FineFilter(self, fine):
        # Tech industry
        filteredFine = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Technology]
        
        sortedByPERatio = sorted(filteredFine, key=lambda f: f.ValuationRatios.PERatio, reverse=True)[:int(0.6*len(filteredFine))]
        sortedByEPS = sorted(filteredFine, key=lambda f: f.EarningReports.BasicEPS.ThreeMonths, reverse=True)[:int(0.6*len(filteredFine))]
        sortedByROE = sorted(filteredFine, key=lambda f: f.OperationRatios.ROE.ThreeMonths, reverse=True)[:int(0.6*len(filteredFine))]
        sortedByNetIncome = sorted(filteredFine, key=lambda f: f.FinancialStatements.IncomeStatement.NetIncome.ThreeMonths, reverse=True)[:int(0.6*len(filteredFine))]
        
        s = set()
        s = set(sortedByPERatio) & set(sortedByEPS) & set(sortedByROE) & set(sortedByNetIncome)
        
        # Sort the list by pb ratio
        return [x.Symbol for x in sorted(list(s), key=lambda f: f.ValuationRatios.PBRatio, reverse=True)]
        
    def OnSecuritiesChanged(self, changes):
        # close positions in removed securities
        for x in changes.RemovedSecurities:
            symbol = x.Symbol
            
            # Remove rolling windows
            if symbol in self.macd_indicator:
                del self.macd_indicator[symbol]
        
        self.Debug(f'AddedSecurities: {len(changes.AddedSecurities)}')

        # can't open positions here since data might not be added correctly yet
        for x in changes.AddedSecurities:
            symbol = x.Symbol
            if symbol not in self.macd_indicator:
                self.macd_indicator[symbol] = self.MACD(symbol, 12, 26, 9, MovingAverageType.Exponential, Resolution.Daily)
                warmUpData = self.History(symbol, 40, Resolution.Daily)
                for bar in warmUpData.loc[symbol, :].itertuples():
                    self.macd_indicator[symbol].Update(bar.Index, bar.close)
            # Add rolling windows
            # self.Debug(f'{symbol} warm up done')
        
        # 2. Add MACD indicators and Awesome indicators
        # 4. Add hedge symbols
        
    def OnData(self, data):
        ''' OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
            Arguments:
                data: Slice object keyed by symbol containing the stock data
        '''
        self.Debug(f'Today is {self.Time} in OnData')
        
        for key, value in self.macd_indicator.items():
            if not value.IsReady:
                self.Debug(f'MACD {key} warm up not ready')
                return
        else:
            self.Debug(f'MACD warm up are ready!!!')
            
        # TODO: Need to update the security that are not in above security list??
        # self.Debug(f'Dir: {dir(data)}')
        # self.Debug(f'Type: {type(data)}')
        # self.Debug(f'Dir 2: {dir(data.Bars)}')
        # self.Debug(f'Type 2: {type(data.Bars)}')
        # for key, value in self.macd_indicator.items():
        #     self.Debug(f'Updating {key} MACD')
        #     value.Update(self.Time, data.Bars[key])
        #     pass
        
        # self.Debug(f'Key: {key}\n' + 
        #     f'Fast: {self.macd_indicator[key].Fast.Current.Value}\n' + 
        #     f'Slow: {self.macd_indicator[key].Slow.Current.Value}\n' + 
        #     f'Signal: {self.macd_indicator[key].Signal.Current.Value}\n' +
        #     f'Hist: {self.macd_indicator[key].Histogram.Current.Value}\n'
        # )
        
        # self.Debug(self.macd_indicator[key].ToDetailedString())
            
        invested_list = [x.Key for x in self.Portfolio if x.Value.Invested]
        invested = len(invested_list)
        
        open_order = 0
        for key, value in self.macd_indicator.items():
            if invested + open_order >= self.capacity:
                break
            if self.macd_indicator[key].Histogram.Current.Value > 0:
                self.SetHoldings(key, 1/self.capacity)
                open_order += 1
        
        # 2. Add MACD indicators and Awesome indicators
        # 3. Make the trade
        pass