Overall Statistics |
Total Trades 8140 Average Win 0.19% Average Loss -0.02% Compounding Annual Return 9.307% Drawdown 43.400% Expectancy 0.173 Net Profit 11.134% Sharpe Ratio 0.411 Probabilistic Sharpe Ratio 22.881% Loss Rate 88% Win Rate 12% Profit-Loss Ratio 8.41 Alpha -0.045 Beta 1.295 Annual Standard Deviation 0.384 Annual Variance 0.148 Information Ratio 0.003 Tracking Error 0.304 Treynor Ratio 0.122 Total Fees $9087.64 |
class SiegfriedsRework(QCAlgorithm): def Initialize(self): self.SetStartDate(2010, 8, 29) self.SetEndDate(2011, 11, 5) self.SetCash(100000) self.UniverseSettings.Resolution = Resolution.Minute self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel()) self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.SelectCoarse, self.SelectFine)) self.spy = self.AddEquity("SPY", Resolution.Minute).Symbol self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.AfterMarketOpen("SPY", 120), self.Rebalance) self.num_symb_coarse = 8000 self.monthly_rebalance = False self.new_symbols = [] self.invested_stocks = [] self.min_market_cap = 5 self.min_roic = 0.7 self.max_roic = 20 # max ROIC at purcahse only. if stock has roic intially below this threshold, but rises while in holdings, stock won't be liquidated self.min_volume = 200000 # minimum trading volume self.max_eveb = 100 self.recently_rebalanced = False def OnData(self, data): if not self.recently_rebalanced: return insights = [] for security in self.ActiveSecurities.Values: symbol = security.Symbol if symbol == self.spy: # skip SPY continue if security.Fundamentals.FinancialStatements.IncomeStatement.EBIT.TwelveMonths/security.Fundamentals.FinancialStatements.BalanceSheet.InvestedCapital.TwelveMonths < self.min_roic \ or security.Fundamentals.MarketCap/1000000 < self.min_market_cap: insights.append(Insight.Price(symbol, Expiry.EndOfMonth, InsightDirection.Flat)) else: insights.append(Insight.Price(symbol, Expiry.EndOfMonth, InsightDirection.Up)) self.EmitInsights(insights) # coarse/fine universe selection runs everyday at midnight def SelectCoarse(self, coarse): if self.monthly_rebalance == False: self.recently_rebalanced = False return Universe.Unchanged # if selectcoarse returns universe.unchanged, selectfine is not called self.recently_rebalanced = True self.monthly_rebalance = False filtered_coarse = [x for x in coarse if x.HasFundamentalData] # removing ETFs, ETNs sorted_coarse = sorted(filtered_coarse, key=lambda k:k.DollarVolume, reverse=True) min_vol_coarse = [x for x in sorted_coarse if x.DollarVolume > self.min_volume] top_liquid_coarse = min_vol_coarse[:self.num_symb_coarse] return [i.Symbol for i in top_liquid_coarse if i.Symbol.Value != 'PDLI'] def SelectFine(self, fine): filtered_fine = [x for x in fine if x.CompanyReference.CountryId == "USA" and x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.Energy and x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.FinancialServices and x.MarketCap/1000000 > self.min_market_cap and x.FinancialStatements.BalanceSheet.InvestedCapital.TwelveMonths > 0 and x.FinancialStatements.IncomeStatement.EBIT.TwelveMonths/x.FinancialStatements.BalanceSheet.InvestedCapital.TwelveMonths > self.min_roic and x.FinancialStatements.IncomeStatement.EBIT.TwelveMonths/x.FinancialStatements.BalanceSheet.InvestedCapital.TwelveMonths < self.max_roic and x.ValuationRatios.EVToEBIT3YrAvg > 0 and x.ValuationRatios.EVToEBIT3YrAvg < self.max_eveb] sorted_fine = sorted(filtered_fine, key=lambda x:x.ValuationRatios.EVToEBIT3YrAvg, reverse = False) triptile = 1 if len(sorted_fine)/4 <= 0 else int(round((len(sorted_fine)/4))) bottom_triptile = sorted_fine[:triptile] # self.Debug(f"filtered_fine: {[str(i.Symbol) for i in filtered_fine]}") self.new_symbols = [i.Symbol for i in bottom_triptile] # self.Debug(f"chosen_fine: {[str(i) for i in self.new_symbols]}") return self.new_symbols # new symbols to add to our watchlist # # this is called each time a security is added or removed from our universe # def OnSecuritiesChanged(self, changes): # # liquidate securities based on their fundamentals, remove from watchlist # for symbol in self.Portfolio.Keys: # # we only care about securities we are invested in # if not self.Portfolio[symbol].Invested: # continue # security = self.Securities[symbol] # # liquidate stocks that go below min ROIC # if security.Fundamentals.FinancialStatements.IncomeStatement.EBIT.TwelveMonths/security.Fundamentals.FinancialStatements.BalanceSheet.InvestedCapital.TwelveMonths < self.min_roic \ # or security.Fundamentals.MarketCap/1000000 < self.min_market_cap: # self.invested_stocks.remove(security.Symbol) # self.Liquidate(security.Symbol) # # self.Debug(f"!!!Selling {security} @ {security.Price}," \ # # + f" ROIC is {security.Fundamentals.FinancialStatements.IncomeStatement.EBIT.TwelveMonths/security.Fundamentals.FinancialStatements.BalanceSheet.InvestedCapital.TwelveMonths}," \ # # + f" EBITDAEV is {security.Fundamentals.ValuationRatios.EVToEBIT3YrAvg}," \ # # + f" MarketCap is {security.Fundamentals.MarketCap/1000000}" \ # # + f" and Volume is {security.Fundamentals.DollarVolume/1000000}") # if len(self.new_symbols) > 0: # for security in changes.AddedSecurities: # if security.Fundamentals == None: # i.e. is an ETF/is SPY # continue # # prevent duplicate entry in invested_stocks # if security.Symbol not in self.invested_stocks and security.Symbol.Value is not str("PDLI R735QTJ8XC9X"): # self.invested_stocks.append(security.Symbol) # add new securities to watchlist # if len(self.invested_stocks) > 0: # # for symbol in self.invested_stocks: # # security = self.Securities[symbol] # # self.SetHoldings(security.Symbol, 0.9/len(self.invested_stocks)) #0.9 to keep cash buffer # # self.Debug(f"Buying {security.Symbol} @ {security.Price}," \ # # + f" ROIC is {security.Fundamentals.FinancialStatements.IncomeStatement.EBIT.TwelveMonths/security.Fundamentals.FinancialStatements.BalanceSheet.InvestedCapital.TwelveMonths}," \ # # + f" EBITEV is {security.Fundamentals.ValuationRatios.EVToEBIT3YrAvg}," \ # # + f" MarketCap is {security.Fundamentals.MarketCap/1000000}" \ # # + f" and Volume is {security.Fundamentals.DollarVolume/1000000}") # for symbol in self.invested_stocks: # security = self.Securities[symbol] # self.SetHoldings([PortfolioTarget(security.Symbol, (0.9/len(self.invested_stocks)))]) #0.9 to keep cash buffer # # self.Debug(f"Buying {security.Symbol} @ {security.Price}," \ # # + f" ROIC is {security.Fundamentals.FinancialStatements.IncomeStatement.EBIT.TwelveMonths/security.Fundamentals.FinancialStatements.BalanceSheet.InvestedCapital.TwelveMonths}," \ # # + f" EBITEV is {security.Fundamentals.ValuationRatios.EVToEBIT3YrAvg}," \ # # + f" MarketCap is {security.Fundamentals.MarketCap/1000000}" \ # # + f" and Volume is {security.Fundamentals.DollarVolume/1000000}") # if len(self.new_symbols) > 0: # self.Debug(f"Invested Stocks List: {[str(symbol) for symbol in self.invested_stocks]}") # self.Debug(f"Portfolio Invested Stocks: {[str(symbol) for symbol in self.Portfolio.Keys if self.Portfolio[symbol].Invested]}") # self.Debug(f"Value of Stocks: {[float(self.Portfolio[symbol].Quantity * self.Portfolio[symbol].Price) for symbol in self.Portfolio.Keys if self.Portfolio[symbol].Invested]}") # self.Debug(f"Portfolio % Invested: {(self.Portfolio.TotalPortfolioValue - self.Portfolio.Cash)/self.Portfolio.TotalPortfolioValue}") # # daily data is received at midnight # def OnData(self, data): # pass # this is called according to Schedule.On in Initialize def Rebalance(self): self.monthly_rebalance = True # def OnEndOfDay(self): # # If there's a bug in QC data (e.g., PDLI currently showing incorrect stock price of $0), # # symbols could end up in self.invested_stocks but not actually invested in portfolio # self.invested_stocks = [symbol for symbol in self.Portfolio.Keys if self.Portfolio[symbol].Invested] # self.Debug(f"Invested Stocks List: {[str(symbol) for symbol in self.invested_stocks]}") # self.Debug(f"Portfolio Invested Stocks: {[str(symbol) for symbol in self.Portfolio.Keys if self.Portfolio[symbol].Invested]}") # self.Debug(f"Value of Stocks: {[float(self.Portfolio[symbol].Quantity * self.Portfolio[symbol].Price) for symbol in self.Portfolio.Keys if self.Portfolio[symbol].Invested]}") # self.Debug(f"Portfolio % Invested: {(self.Portfolio.TotalPortfolioValue - self.Portfolio.Cash)/self.Portfolio.TotalPortfolioValue}")