Overall Statistics |
Total Trades 4746 Average Win 0.05% Average Loss -0.09% Compounding Annual Return -34.794% Drawdown 22.200% Expectancy -0.094 Net Profit -19.293% Sharpe Ratio -2.35 Probabilistic Sharpe Ratio 0.052% Loss Rate 39% Win Rate 61% Profit-Loss Ratio 0.49 Alpha -0.414 Beta 0.409 Annual Standard Deviation 0.125 Annual Variance 0.016 Information Ratio -4.347 Tracking Error 0.136 Treynor Ratio -0.718 Total Fees $5216.93 Estimated Strategy Capacity $5500000.00 Lowest Capacity Asset NTLA WAB9H50ZHGF9 |
from itertools import groupby class UniverseRollingAlgorithm(QCAlgorithm): def Initialize(self): #Initialize Dates, Cash, Equities, Fees, Allocation, Parameters, Indicators, Charts # Set Start Date, End Date, and Cash #------------------------------------------------------- self.SetTimeZone(TimeZones.NewYork) #EDIT: Added Timezon self.SetStartDate(2021, 1, 1) # Set Start Date # self.SetEndDate(2020, 4, 15) # Set End Date self.SetCash(100000) # Set Strategy Cash #------------------------------------------------------- # Set Custom Universe #------------------------------------------------------- self.AddUniverse(self.CoarseSelectionFilter, self.FineSelectionFilter) self.UniverseSettings.Resolution = Resolution.Minute #Needs to change to Resolution.Minute once code works, leaving Daily for now to minimize data self.UniverseSettings.SetDataNormalizationMode = DataNormalizationMode.SplitAdjusted self.UniverseSettings.FeeModel = ConstantFeeModel(0.0) self.UniverseSettings.Leverage = 1 self.universe = [] #------------------------------------------------------- # self.SetBrokerageModel(BrokerageName.Alpaca, AccountType.Cash) #EDIT: Added Brokerage, appears to have set fees to zero self.EMA_Period_Fast = 20 self.EMA_Period_Slow = 50 self.__numberOfSymbols = 100 self.__numberOfSymbolsFine = 10 self.indicators = {} # Define Percentage Allocation #------------------------------------------------------- self.percentagebuy = 0.1 #------------------------------------------------------- # Sector Selectior self.numberOfSymbolsCoarse = 500 self.exposureToSector = 2 self.lastMonth = -1 self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x))) def CoarseSelectionFilter(self, coarse): if self.Time.month == self.lastMonth: return Universe.Unchanged self.lastMonth = self.Time.month allCoarse = [x for x in coarse if x.HasFundamentalData and x.Price > 1 and x.Volume > 1] finalCoarse = sorted(allCoarse, key = lambda x: x.DollarVolume, reverse = True) return [x.Symbol for x in finalCoarse][:self.numberOfSymbolsCoarse] #Old #sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True) # sort descending by daily dollar volume #return [ x.Symbol for x in sortedByDollarVolume[:self.__numberOfSymbols] ] # return the symbol objects of the top entries from our sorted collection def FineSelectionFilter(self, fine): # sort the data by P/E ratio and take the top 'NumberOfSymbolsFine' filteredSymbols = [] sortedBySector = [x for x in fine] # self.Log("ORDER NOTIFICATION >> {}".format(str(AssetClassification.MorningstarSectorCode))) for code, g in groupby(sortedBySector, lambda x: x.AssetClassification.MorningstarSectorCode): for x in sorted(g, key = lambda x: x.ValuationRatios.PERatio, reverse = True)[:self.exposureToSector]: filteredSymbols.append(x.Symbol) self.universe = filteredSymbols[:5] return self.universe #Old #sortedByPeRatio = sorted(fine, key=lambda x: x.OperationRatios.OperationMargin.Value, reverse=False) # sort descending by P/E ratio #self.universe = [ x.Symbol for x in sortedByPeRatio[:self.__numberOfSymbolsFine] ] # take the top entries from our sorted collection #return self.universe def OnSecuritiesChanged(self, changes): # Create indicator for each new security for security in changes.AddedSecurities: self.indicators[security.Symbol] = SymbolData(security.Symbol, self, self.EMA_Period_Fast, self.EMA_Period_Slow) for security in changes.RemovedSecurities: if security.Invested: self.Liquidate(security.Symbol, "Universe Removed Security") if security in self.indicators: self.indicators.pop(security.Symbol, None) def OnData(self, data): #Entry Point for Data and algorithm - Check Data, Define Buy Quantity, Process Volume, Check Portfolio, Check RSI, Execute Buy/Sell orders, Chart Plots for symbol in self.universe: if not data.ContainsKey(symbol): #Tested and Valid/Necessary continue if data[symbol] is None: #Tested and Valid/Necessary continue if not symbol in self.indicators: #Tested and Valid/Necessary continue # Ensure indicators are ready to update rolling windows if not self.indicators[symbol].slow_ema.IsReady: continue # Update EMA rolling windows self.indicators[symbol].fast_ema_window.Add(self.indicators[symbol].get_fast_EMA()) self.indicators[symbol].slow_ema_window.Add(self.indicators[symbol].get_slow_EMA()) # Check for Indicator Readiness within Rolling Window #------------------------------------------------------- if not (self.indicators[symbol].fast_ema_window.IsReady and self.indicators[symbol].slow_ema_window.IsReady): continue #return #EDIT #EXECUTE TRADING LOGIC HERE - if self.Portfolio[symbol].Invested: # Sell condition if (self.indicators[symbol].fast_ema_window[1] >= self.indicators[symbol].slow_ema_window[1]) and (self.indicators[symbol].fast_ema_window[4] < self.indicators[symbol].slow_ema_window[4]): self.Liquidate(symbol) # Buy conditions elif self.Portfolio.MarginRemaining > 0.9 * self.percentagebuy * self.Portfolio.TotalPortfolioValue: if self.indicators[symbol].fast_ema_window[1] <= self.indicators[symbol].slow_ema_window[1] and \ (self.indicators[symbol].fast_ema_window[4] > self.indicators[symbol].slow_ema_window[4]): self.buyquantity = round((self.percentagebuy*self.Portfolio.TotalPortfolioValue)/data[symbol].Close) self.MarketOrder(symbol, self.buyquantity) class SymbolData(object): rolling_window_length = 5 def __init__(self, symbol, context, fast_ema_period, slow_ema_period): self.symbol = symbol self.fast_ema_period = fast_ema_period self.slow_ema_period = slow_ema_period self.fast_ema = context.EMA(symbol, self.fast_ema_period, Resolution.Minute) #, fillDataForward = True, leverage = 1, extendedMarketHours = False) self.slow_ema = context.EMA(symbol, self.slow_ema_period, Resolution.Minute) #, fillDataForward = True, leverage = 1, extendedMarketHours = False) self.fast_ema_window = RollingWindow[float](self.rolling_window_length) self.slow_ema_window = RollingWindow[float](self.rolling_window_length) # Warm up EMA indicators history = context.History([symbol], slow_ema_period + self.rolling_window_length, Resolution.Minute) for time, row in history.loc[symbol].iterrows(): self.fast_ema.Update(time, row["close"]) self.slow_ema.Update(time, row["close"]) # Warm up rolling windows if self.fast_ema.IsReady: self.fast_ema_window.Add(self.fast_ema.Current.Value) if self.slow_ema.IsReady: self.slow_ema_window.Add(self.slow_ema.Current.Value) def get_fast_EMA(self): return self.fast_ema.Current.Value def get_slow_EMA(self): return self.slow_ema.Current.Value