Overall Statistics |
Total Trades 590 Average Win 1.43% Average Loss -1.12% Compounding Annual Return 15.261% Drawdown 29.800% Expectancy 0.438 Net Profit 314.318% Sharpe Ratio 0.69 Probabilistic Sharpe Ratio 9.328% Loss Rate 37% Win Rate 63% Profit-Loss Ratio 1.28 Alpha -0.006 Beta 1.172 Annual Standard Deviation 0.174 Annual Variance 0.03 Information Ratio 0.129 Tracking Error 0.097 Treynor Ratio 0.102 Total Fees $791.31 Estimated Strategy Capacity $5000000.00 Lowest Capacity Asset ILMN RWQR2INKP0TH |
#region imports from AlgorithmImports import * #endregion #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 * class BasicTemplateAlgorithm(QCAlgorithm): def __init__(self): # set the flag for rebalance self.reb = 1 # Number of stocks to pass CoarseSelection process self.num_coarse = 100 # Number of stocks to long/short self.num_fine = 10 self.symbols = None self.mom = {} # Dict of Momentum indicator keyed by Symbol self.lookback = 252 # Momentum indicator lookback period def Initialize(self): self.SetCash(100000) self.SetStartDate(2009, 7, 1) # Set Start Date self.SetEndDate(2019, 7, 1) # Set Start Date # self.SetStartDate(2000,1,1) # if not specified, the Backtesting EndDate would be today # self.SetEndDate(2017,1,1) self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol # self.sma = self.SMA("SPY", 5, Resolution.Daily) self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction,self.FineSelectionFunction) # Schedule the rebalance function to execute at the begining of each month self.Schedule.On(self.DateRules.MonthStart(self.spy), self.TimeRules.AfterMarketOpen(self.spy,5), Action(self.rebalance)) def CoarseSelectionFunction(self, coarse): # if the rebalance flag is not 1, return null list to save time. if self.reb == 0: return Universe.Unchanged # return self.long + self.short 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] topSymbols = [i.Symbol for i in top] return topSymbols def FineSelectionFunction(self, fine): # return null list if it's not time to rebalance if self.reb == 0: return Universe.Unchanged self.reb = 0 toPop = [security for security in self.mom if security not in [i.Symbol for i in fine]] for security in toPop: self.mom.pop(security) for security in fine: if security.Symbol not in self.Securities: self.AddEquity(security.Symbol, Resolution.Daily) self.mom[security.Symbol] = Momentum(self.lookback) addedSymbols = [k for k,v in self.mom.items() if not v.IsReady] history = self.History(addedSymbols, 1 + self.lookback, Resolution.Daily) history = history.close.unstack(level=0) for symbol in addedSymbols: ticker = str(symbol) if ticker in history: for time, value in history[ticker].items(): try: item = IndicatorDataPoint(symbol, time, value) self.mom[symbol].Update(item) except: self.Debug(value) filtered_fine = [x for x in fine if x.Symbol in self.mom and self.mom[x.Symbol].IsReady and x.OperationRatios.OperationMargin.ThreeMonths # # and x.ValuationRatios.EVToForwardEBIT # # and x.FinancialStatements.IncomeStatement.GrossProfit.ThreeMonths # and x.FinancialStatements.IncomeStatement.TotalRevenue.ThreeMonths # and x.FinancialStatements.IncomeStatement.SellingGeneralAndAdministration.ThreeMonths # and x.OperationRatios.RevenueGrowth.ThreeMonths # and x.ValuationRatios.CashReturn # and x.OperationRatios.OperationIncomeGrowth.ThreeMonths # and x.OperationRatios.NetMargin.ThreeMonths # and x.OperationRatios.ROIC.ThreeMonths ] self.Debug('remained to select %d'%(len(filtered_fine))) operationMargin = sorted(filtered_fine, key=lambda x: x.OperationRatios.OperationMargin.Value, reverse=True) # EVToForwardEBIT = sorted(filtered_fine, key=lambda x: x.ValuationRatios.EVToForwardEBIT, reverse=True) # grossMargin = sorted(filtered_fine, key=lambda x: x.FinancialStatements.IncomeStatement.GrossProfit.ThreeMonths/max(x.FinancialStatements.IncomeStatement.TotalRevenue.ThreeMonths,1), reverse=True) # sgaMargin = sorted(filtered_fine, key=lambda x: x.FinancialStatements.IncomeStatement.SellingGeneralAndAdministration.ThreeMonths/max(x.FinancialStatements.IncomeStatement.TotalRevenue.ThreeMonths,1), reverse=True) # revenueGrowth = sorted(filtered_fine, key=lambda x: x.OperationRatios.RevenueGrowth.ThreeMonths, reverse=True) # operationIncomeGrowth = sorted(filtered_fine, key=lambda x: x.OperationRatios.OperationIncomeGrowth.ThreeMonths, reverse=True) # netMargin = sorted(filtered_fine, key=lambda x: x.OperationRatios.NetMargin.ThreeMonths, reverse=True) # roic = sorted(filtered_fine, key=lambda x: x.OperationRatios.ROIC.ThreeMonths, reverse=True) stock_dict = {} mom = sorted(filtered_fine, key=lambda x: self.mom[x.Symbol].Current.Value, reverse=True) # mom = sorted([v for k,v in self.mom.items() if v.IsReady], key=lambda x: x.Current.Value, reverse=True) for i,ele in enumerate(operationMargin): # operationMargin_R = operationMargin.index(ele) # EVToForwardEBIT_R = EVToForwardEBIT.index(ele) # grossMargin_R = grossMargin.index(ele) # sgaMargin_R = sgaMargin.index(ele) # roic_R = roic.index(ele) # operationIncomeGrowth_R = operationIncomeGrowth.index(ele) # revenueGrowth_R = revenueGrowth.index(ele) #score = sum([i, operationMargin_R,roic_R]) mom_r = mom.index(ele) score = sum([mom_r]) stock_dict[ele] = score # sort the stocks by their scores self.sorted_stock = sorted(stock_dict.items(), key=lambda d:d[1],reverse=False) sorted_symbol = [x[0] for x in self.sorted_stock] # sotre the top stocks into the long_list and the bottom ones into the short_list self.long = [x.Symbol for x in sorted_symbol[:self.num_fine]] self.short = [x.Symbol for x in sorted_symbol[-self.num_fine:]] return self.long + self.short def OnData(self, data): # pass for symbol, mom in self.mom.items(): mom.Update(self.Time, self.Securities[symbol].Close) def rebalance(self): self.Debug("rebalance") if self.Time.month % 4 > 0: return # if this month the stock are not going to be long/short, liquidate it. long_short_list = self.long + self.short for i in self.Portfolio.Values: if (i.Invested) and (i not in long_short_list): self.Liquidate(i.Symbol) # Assign each stock equally. Alternatively you can design your own portfolio construction method for i in self.long: self.SetHoldings(i, 1/self.num_fine) # always short # for i in self.short: # self.SetHoldings(i, -1/self.num_fine) # tactical short # if self.Securities["SPY"].Price < self.sma.Current.Value: # for i in self.short: # self.SetHoldings(i, -1/self.num_fine) # else: # for i in self.Portfolio.Values: # if (i.Invested) and (i.IsShort): # self.Liquidate(i.Symbol) self.reb = 1