Overall Statistics
Total Trades
144
Average Win
0.55%
Average Loss
-0.58%
Compounding Annual Return
27.398%
Drawdown
20.000%
Expectancy
0.187
Net Profit
27.398%
Sharpe Ratio
1.096
Probabilistic Sharpe Ratio
49.903%
Loss Rate
39%
Win Rate
61%
Profit-Loss Ratio
0.94
Alpha
0.077
Beta
1.1
Annual Standard Deviation
0.185
Annual Variance
0.034
Information Ratio
1.022
Tracking Error
0.087
Treynor Ratio
0.184
Total Fees
$239.28
Estimated Strategy Capacity
$780000.00
Lowest Capacity Asset
HOL TYJB6RKZ4JTX
MAX_POSITIONS = 20
REBALANCE_PERIOD = 20

class DataTesting(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2010, 1, 1)
        self.SetEndDate(2010, 12, 31)
        self.SetCash(100000)
        
        self.UniverseSettings.Resolution = Resolution.Minute
        self.AddUniverse(self.CoarseFilter, self.FineFilter)
        
        self.rebalance = 0
        self.buy = []
        self.sell = []
        
    def CoarseFilter(self, coarse):
        if self.rebalance and self.rebalance <= REBALANCE_PERIOD:
            self.rebalance += 1
            return Universe.Unchanged
            
        self.rebalance = 1
            
        #filtered = [x for x in coarse if x.HasFundamentalData]
        #filtered = [x for x in filtered if float(x.AdjustedPrice) > 5]
        #filtered = [x.Symbol for x in filtered]
        filtered = [x.Symbol for x in coarse if x.HasFundamentalData and (x.AdjustedPrice > 5.0)]
        
        return filtered
        
    def FineFilter(self, fine):
        filtered = list(filter(self.FineFundamentalFilter, fine))
        
        return self.FineSort(filtered)[:MAX_POSITIONS]
        
    def FineFundamentalFilter(self, security):
        eps3m = security.EarningRatios.DilutedEPSGrowth.ThreeMonths > 0
        eps1y = security.EarningRatios.DilutedEPSGrowth.OneYear > 0
        
        fcf3m = security.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > 0
        fcf1y = security.FinancialStatements.CashFlowStatement.FreeCashFlow.TwelveMonths > 0
        
        roe3m = security.OperationRatios.ROE.ThreeMonths > 0
        roe1y = security.OperationRatios.ROE.OneYear > 0
        
        return eps3m and eps1y and fcf3m and fcf1y and roe3m and roe1y
        
    def FineSort(self, fine):
        ranked = {}
        ranks = {
            'earnings_growth': [x.Symbol for x in sorted(fine, key=lambda x: 
                (x.EarningRatios.DilutedEPSGrowth.ThreeMonths +
                x.EarningRatios.DilutedEPSGrowth.OneYear + 
                x.EarningRatios.DilutedEPSGrowth.ThreeYears) / 3    
            )],
            'revenue_growth': [x.Symbol for x in sorted(fine, key=lambda x: 
                (x.OperationRatios.RevenueGrowth.ThreeMonths +
                x.OperationRatios.RevenueGrowth.OneYear + 
                x.OperationRatios.RevenueGrowth.ThreeYears) / 3  
            )],
            'book_growth': [x.Symbol for x in sorted(fine, key=lambda x: 
                (x.EarningRatios.BookValuePerShareGrowth.ThreeMonths +
                x.EarningRatios.BookValuePerShareGrowth.OneYear + 
                x.EarningRatios.BookValuePerShareGrowth.ThreeYears) / 3
            )],
            'fcf_growth': [x.Symbol for x in sorted(fine, key=lambda x: 
                (x.EarningRatios.FCFPerShareGrowth.ThreeMonths +
                x.EarningRatios.FCFPerShareGrowth.OneYear + 
                x.EarningRatios.FCFPerShareGrowth.ThreeYears) / 3
            )]
        }
        
        for security in fine:
            symbol = security.Symbol
            ranked[symbol] = 0
            for key, sort in ranks.items():
                ranked[symbol] += (sort.index(symbol) + 1)
        
        #ranked = dict(sorted(ranked.items(), key=lambda x: x[1], reverse=True))
        #ranked = [symbol for symbol in ranked]
        ranked = [symbol for symbol, rank in sorted(ranked.items(), key=lambda t: t[1], reverse=True)]
        
        return ranked
        
    def OnSecuritiesChanged(self, changes):
        for added in changes.AddedSecurities:
            self.buy.append(added.Symbol)
        for removed in changes.RemovedSecurities:
            self.sell.append(removed.Symbol)
        
    def OnData(self, data):
        #sell = self.sell[:]
        #buy = self.buy[:]
        
        #if len(sell) > 0:
        #    for symbol in sell:
        #        if data.ContainsKey(symbol):
        #            self.sell.remove(symbol)
        #            self.Liquidate(symbol)
           
        #if len(buy) > 0:
        #    for symbol in buy:
        #        if data.ContainsKey(symbol):
        #            self.buy.remove(symbol)
        #            self.SetHoldings(symbol, 1 / MAX_POSITIONS)
        
        for symbol in [*self.sell]:
            self.sell.remove(symbol)
            self.Liquidate(symbol)
           
        for symbol in [*self.buy]:
            if data.ContainsKey(symbol) and data[symbol] is not None:
                self.buy.remove(symbol)
                self.SetHoldings(symbol, 1 / MAX_POSITIONS)