Overall Statistics |
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio -8.639 Tracking Error 0.315 Treynor Ratio 0 Total Fees $0.00 |
class UniverseSelection(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 4, 1) # Set Start Date self.SetEndDate(2020, 5, 7) # Set End Date self.SetCash(100000) # Set Strategy Cash self.UniverseSettings.Resolution = Resolution.Minute self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.SPY = self.AddEquity('SPY', Resolution.Minute).Symbol self.day = -1 self.num_coarse = 20 self.min_stock_price = 10 self.min_days_after_earnings = 10 self.max_days_after_earnings = 80 self.ema_period = 8 # 8 period on 5 Min TF is 40 on 1 Min TF self.sma_period = 55 # 55 period on 5 Min TF is 275 on 1 Min TF self.bb_period = 20 # 20 period on 30 min TF is 600 on 1 Min TF self.bb_k = 2 self.gap_distance = 0.02 # 2% self.data = {} self.selectedStocks = [] self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen(self.SPY, 30), self.SelectStocks) self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen(self.SPY, 1), self.AtMarketOpen) self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose(self.SPY, 1), self.BeforeMarketCloses) def CoarseSelectionFunction(self, coarse): if self.day == self.Time.day: return Universe.Unchanged self.day = self.Time.day # drop stocks which have no fundamental data or have too low prices selected = [x for x in coarse if (x.HasFundamentalData) and (float(x.Price) > self.min_stock_price)] # rank the stocks by dollar volume # ----------------------------------- filtered = sorted(selected, key=lambda x: x.DollarVolume, reverse=True) return [ x.Symbol for x in filtered[:self.num_coarse]] def FineSelectionFunction(self, fine): filtered = [x for x in fine if x.SecurityReference.IsPrimaryShare and x.SecurityReference.SecurityType == "ST00000001" and x.SecurityReference.IsDepositaryReceipt == 0 and x.CompanyReference.IsLimitedPartnership == 0 and x.EarningReports.FileDate < self.Time - timedelta(days=self.min_days_after_earnings) and x.EarningReports.FileDate > self.Time - timedelta(days=self.max_days_after_earnings)] return [x.Symbol for x in filtered] # def select def SelectStocks(self): for symbol in self.data.keys(): sd = self.data[symbol] isReady = sd.EMA.IsReady and sd.SMA.IsReady and sd.BB.IsReady if isReady and self.data[symbol].GapUp and self.data[symbol].EMA > self.data[symbol].SMA and self.data[symbol].BB.UpperBand.Current.Value < self.Securities[symbol].Close: self.selectedStocks.append(symbol) self.Debug(f'{symbol.ToString()} {self.Time.ctime()} GapUp:{sd.GapUp} EMA:{sd.EMA} SMA:{sd.SMA} BB:{sd.BB.UpperBand.Current.Value} Close:{self.Securities[symbol].Close}') def OnSecuritiesChanged(self, changes): for security in changes.RemovedSecurities: symbol_data = self.data.pop(security.Symbol, None) if symbol_data: symbol_data.dispose() for security in changes.AddedSecurities: if security.Symbol not in self.data: self.data[security.Symbol] = SymbolData(security.Symbol, self.ema_period, self.sma_period, self.bb_period, self.bb_k, self) def AtMarketOpen(self): for symbol in self.data.keys(): if self.data[symbol].LastClose == 0: self.data[symbol].GapUp = False continue gap = (self.Securities[symbol].Open - self.data[symbol].LastClose) / self.data[symbol].LastClose if gap > self.gap_distance: self.data[symbol].GapUp = True else: self.data[symbol].GapUp = False def BeforeMarketCloses(self): for symbol in self.data.keys(): self.data[symbol].LastClose = self.Securities[symbol].Close class SymbolData(object): def __init__(self, symbol, emaPeriod, smaPeriod, bbPeriod, bbKvalue, algorithm): self.Symbol = symbol self.LastClose = 0 self.GapUp = False self.EMA = ExponentialMovingAverage(emaPeriod) self.SMA = SimpleMovingAverage(smaPeriod) self.BB = BollingerBands(bbPeriod, bbKvalue, MovingAverageType.Exponential) self.algorithm = algorithm self.consolidator_5min = TradeBarConsolidator(timedelta(minutes=5)) self.consolidator_30min = TradeBarConsolidator(timedelta(minutes=30)) # Exit if no daily data. We need 1 bar of data (one day) # --------------------------------------------------------- history = algorithm.History(symbol, 1, Resolution.Daily) if history.empty or 'close' not in history.columns: return # iterate over historical data, get/store last close (of prev bar) # ------------------------------------------------------------------ else: for index, row in history.loc[symbol].iterrows(): self.LastClose = row['close'] history = algorithm.History(symbol, max(emaPeriod*5, smaPeriod*5, bbPeriod*30), Resolution.Minute) if history.empty or 'close' not in history.columns: return for index, row in history.loc[symbol].iterrows(): tradeBar = TradeBar() tradeBar.Close = row['close'] tradeBar.Open = row['open'] tradeBar.High = row['high'] tradeBar.Low = row['low'] tradeBar.Volume = row['volume'] tradeBar.Time = index tradeBar.Symbol = symbol self.consolidator_5min.Update(tradeBar) self.consolidator_30min.Update(tradeBar) algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator_5min) algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator_30min) algorithm.RegisterIndicator(symbol, self.EMA, self.consolidator_5min) algorithm.RegisterIndicator(symbol, self.SMA, self.consolidator_5min) algorithm.RegisterIndicator(symbol, self.BB, self.consolidator_30min) def dispose(self): self.algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.consolidator_5min) self.algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.consolidator_30min)