Overall Statistics
Total Trades
2171
Average Win
0.09%
Average Loss
-0.11%
Compounding Annual Return
0.564%
Drawdown
17.900%
Expectancy
-0.020
Net Profit
3.189%
Sharpe Ratio
0.101
Probabilistic Sharpe Ratio
0.529%
Loss Rate
45%
Win Rate
55%
Profit-Loss Ratio
0.78
Alpha
0.027
Beta
-0.202
Annual Standard Deviation
0.052
Annual Variance
0.003
Information Ratio
-0.779
Tracking Error
0.132
Treynor Ratio
-0.026
Total Fees
$2171.00
Estimated Strategy Capacity
$820000.00
Lowest Capacity Asset
CEA R735QTJ8XC9X
#region imports
from AlgorithmImports import *
#endregion
class TwelveMonthCycle(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2013, 1, 1)  
        self.SetEndDate(2018, 8, 1)  
        self.SetCash(100000) 

        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
        self.AddEquity("SPY", Resolution.Daily)
        
        self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.AfterMarketOpen("SPY"), self.Rebalance)
        
        self.monthly_rebalance = False 
        self.filtered_fine = None
        
    def CoarseSelectionFunction(self, coarse):
        if self.monthly_rebalance:
            coarse = [x for x in coarse if (x.HasFundamentalData)
                                        and (x.Market == "usa")]
            return [i.Symbol for i in coarse]

        else:
            return []
    
    def FineSelectionFunction(self, fine):
        if self.monthly_rebalance:
            fine = [i for i in fine if ((i.SecurityReference.ExchangeId == "NYS") or (i.SecurityReference.ExchangeId == "ASE"))]
            
            self.filtered_fine = []
            for i in fine:
                history_start = self.History([i.Symbol], TimeSpan.FromDays(365))
                history_end = self.History([i.Symbol],TimeSpan.FromDays(335))
                if not history_start.empty and not history_end.empty:
                    i.Returns = float(history_end.iloc[0]["close"] - history_start.iloc[0]["close"])
                    self.filtered_fine.append(i)
            
            size = int(len(fine)*.3)
            self.filtered_fine = sorted(self.filtered_fine, key = lambda x: x.MarketCap, reverse=True)[:size]
            self.filtered_fine = sorted(self.filtered_fine, key = lambda x: x.Returns, reverse=True)
            self.filtered_fine = [i.Symbol for i in self.filtered_fine]
            return self.filtered_fine

        else:
            return []
    
    def Rebalance(self):
        self.monthly_rebalance = True
    
    def OnData(self, data):
        if not (self.monthly_rebalance): return
        if not (self.filtered_fine): return
        self.monthly_rebalance = False
        
        portfolio_size = int(len(self.filtered_fine)/10)
        short_stocks = self.filtered_fine[-portfolio_size:]
        long_stocks = self.filtered_fine[:portfolio_size]

        stocks_invested = [x.Key for x in self.Portfolio]
        for i in stocks_invested:
            #liquidate the stocks not in the filtered balance sheet accrual list
            if i not in self.filtered_fine:
                self.Liquidate(i) 
            #long the stocks in the list
            elif i in long_stocks:
                self.SetHoldings(i, 1/(portfolio_size*2))
            #short the stocks in the list
            elif i in short_stocks:
                self.SetHoldings(i,-1/(portfolio_size*2))