Overall Statistics
Total Trades
490
Average Win
0.82%
Average Loss
-0.67%
Compounding Annual Return
18.445%
Drawdown
32.700%
Expectancy
0.283
Net Profit
66.244%
Sharpe Ratio
0.707
Probabilistic Sharpe Ratio
24.026%
Loss Rate
42%
Win Rate
58%
Profit-Loss Ratio
1.22
Alpha
0.044
Beta
1.227
Annual Standard Deviation
0.212
Annual Variance
0.045
Information Ratio
0.61
Tracking Error
0.104
Treynor Ratio
0.122
Total Fees
$907.10
Estimated Strategy Capacity
$110000.00
Lowest Capacity Asset
CVCO SPYCIDBUES85
MAX_POSITIONS = 20
REBALANCE_PERIOD = 20

class DataTesting(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2010, 1, 1)
        self.SetEndDate(2012, 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]
        
        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]
        
        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)