Overall Statistics |
Total Trades 4 Average Win 0% Average Loss -0.79% Compounding Annual Return -61.935% Drawdown 2.000% Expectancy -1 Net Profit -1.575% Sharpe Ratio -4.905 Probabilistic Sharpe Ratio 1.125% Loss Rate 100% Win Rate 0% Profit-Loss Ratio 0 Alpha -0.509 Beta 0.147 Annual Standard Deviation 0.112 Annual Variance 0.013 Information Ratio -1.367 Tracking Error 0.203 Treynor Ratio -3.726 Total Fees $20.00 Estimated Strategy Capacity $350000.00 Lowest Capacity Asset YETI WYZEPGZ8HZ6T |
#region imports from AlgorithmImports import * import pandas as pd #endregion class ParticleTransdimensionalAutosequencers(QCAlgorithm): def Initialize(self): self.SetStartDate(2023, 1, 15) self.SetEndDate(2023, 1, 20) self.SetCash(50000) self.cash = 50000 self.daily_stop = 0.02 # List of equities that would cause trouble when running the algo and needed to be manually added at initialization self.equityls = ['SPY','HCM','MWGP','EXPD','PLAY','PRGS','ADPT','CDW','SIX','TWOU','DHR'] for symbol in self.equityls: self.AddEquity(symbol, Resolution.Minute, extendedMarketHours=True) self.AddUniverseSelection(CoarseFundamentalUniverseSelectionModel(self.CoarseSelectionFunction)) self.UniverseSettings.Resolution = Resolution.Minute self.UniverseSettings.extendedMarketHours = True # Track open price of the day self.Schedule.On( self.DateRules.EveryDay("SPY"), self.TimeRules.At(9, 31), self.TrackOpen ) # Select stocks with highest RVOL in the first 15 mins of trading self.Schedule.On( self.DateRules.EveryDay("SPY"), self.TimeRules.At(9, 35), self.SelectUniverse ) # Liquidate all position before market close self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 1), self.ClosePositions) # List or dictionary to keep track of values for the universe selection self.universe = [] self.volume_by_symbol = {} self.open_by_symbol = {} self.gap = {} self.vol_after_gap = {} self.rvol_by_symbol = {} self.logged = False # Essential to have this automatic warmup, otherwise indicators are all zero and indicator.is_ready = False self.EnableAutomaticIndicatorWarmUp = True # List of dictionary to keep track of indicators that determines entry and exit criteria self.green_vol = {} self.red_vol = {} self.green_red_df = {} self.vwap_dict = {} self.vwap_df = {} self.vwap_cross_flag = {} self.vwap_above_flag = {} self.high_vol_flag = {} self.green_red_flag = {} self.can_trade_flag = False # Coarse Selection Filter executed at 00:00 on every day (once only) based on data of the previous trading day def CoarseSelectionFunction(self, coarse): self.volume_by_symbol = {c.Symbol: 0 for c in coarse if c.Price > 30 and c.Price < 400 and c.Volume > 2000000 and c.HasFundamentalData} self.Debug(f"Universe size before volume filter: {len(self.volume_by_symbol)} at {self.Time}") self.green_vol = self.volume_by_symbol.copy() self.red_vol = self.volume_by_symbol.copy() # initialize df and various flags self.can_trade_flag = False for symbol in self.volume_by_symbol.keys(): self.green_red_df[symbol] = pd.DataFrame([],columns = ['volume', 'green_red']) self.vwap_df[symbol] = pd.DataFrame([],columns = ['vwap', 'close','high']) self.vwap_cross_flag[symbol] = False self.green_red_flag[symbol] = 0 self.high_vol_flag[symbol] = False self.vwap_above_flag[symbol] = 0.0 #for symbol, volume in self.volume_by_symbol.items(): # try: # self.vwap_dict[symbol] = SymbolData(symbol, self) # except: # self.Debug(f'{symbol} cannot log vwap data') return list(self.volume_by_symbol.keys()) # OnData executed whenever there is new data arriving. In this case, new data arrive at 9:30 every day at minute interval (due to Resolution.Minute) def OnData(self, data): # register VWAP indicator at 9:30 if self.Time.hour == 9 and self.Time.minute == 30: for symbol in self.volume_by_symbol.keys(): self.vwap_dict[symbol] = SymbolData(symbol, self) # Every minute, data got pumped in for many securities. We record the minute volume data for stocks that passes our coarse selection filter for symbol in self.volume_by_symbol.keys(): if data.ContainsKey(symbol): try: #self.Log(f'{symbol} has minute volume of {data[symbol].Volume}') self.volume_by_symbol[symbol] += data[symbol].Volume # if the bar is green, record the volume in a seperate dictionary if data[symbol].Close > data[symbol].Open: self.green_vol[symbol] += data[symbol].Volume self.green_red_df[symbol].loc[self.Time] = [data[symbol].Volume, "Green"] if data[symbol].Close < data[symbol].Open: self.red_vol[symbol] += data[symbol].Volume self.green_red_df[symbol].loc[self.Time] = [data[symbol].Volume, "Red"] self.vwap_dict[symbol].Update(data[symbol].EndTime, data[symbol].Close) except: self.Log(f'{symbol} has data[symbol] as non-type') # When volume_by_symbol is cleared (it is cleared at 9:35 by a scheduled event), it will print the length of the final universe to be traded if len(self.volume_by_symbol) == 0: if not self.logged: self.logged = True self.Debug(f"Universe size after volume filter: {len(self.universe)} at {self.Time}") #return # from 9:35 onwards, only keep track of indicators for selected universe # Track green vs red volume every min for symbol in self.universe: if data.ContainsKey(symbol): try: # Track green and red bar volume if data[symbol].Close > data[symbol].Open: #self.green_vol[symbol] += data[symbol].Volume self.green_red_df[symbol].loc[self.Time] = [data[symbol].Volume, "Green"] if data[symbol].Close < data[symbol].Open: #self.red_vol[symbol] += data[symbol].Volume self.green_red_df[symbol].loc[self.Time] = [data[symbol].Volume, "Red"] except: self.Log(f'{symbol} has data[symbol] as non-type') # Track green vs red volume in last 10 bars for symbol in self.universe: if data.ContainsKey(symbol): data_symbol = self.green_red_df[symbol] length_of_data = data_symbol.shape[0] if length_of_data <= 10: last10barvol = data_symbol[-length_of_data:] else: last10barvol = data_symbol[-10:] green_vol_last10 = last10barvol.loc[last10barvol['green_red'] == "Green", "volume"].sum() red_vol_last10 = last10barvol.loc[last10barvol['green_red'] == "Red", "volume"].sum() try: bullishness_last10 = green_vol_last10 / (green_vol_last10 + red_vol_last10) except: self.Debug(f'{symbol} has green vol of {green_vol_last10} and red vol of {red_vol_last10}') bullishness_last10 = 0.5 if bullishness_last10 >= 0.75: self.green_red_flag[symbol] = 1 elif bullishness_last10 <= 0.25: self.green_red_flag[symbol] = -1 else: self.green_red_flag[symbol] = 0 # Track VWAP and whether price crosses VWAP for symbol in self.universe: self.vwap_cross_flag[symbol] = False # reset flag to 0 every bar self.vwap_above_flag[symbol] = 0.0 # reset flag to 0 every bar if data.ContainsKey(symbol): # Track vwap #self.vwap_dict[symbol].Update(data[symbol].EndTime, data[symbol].Close) try: vwap = self.vwap_dict[symbol].vwap.Current.Value # vwap cross flag if (data[symbol].High > vwap and data[symbol].Low < vwap) and (data[symbol].Close > data[symbol].Open): self.vwap_cross_flag[symbol] = 1 elif (data[symbol].High > vwap and data[symbol].Low < vwap) and (data[symbol].Close < data[symbol].Open): self.vwap_cross_flag[symbol] = -1 else: self.vwap_cross_flag[symbol] = False # vwap df tracking self.vwap_df[symbol].loc[self.Time] = [vwap, data[symbol].Close, data[symbol].High] no_of_bars = self.vwap_df[symbol].shape[0] no_of_bars_above_vwap = len(self.vwap_df[symbol][self.vwap_df[symbol]['close'] > self.vwap_df[symbol]['vwap']]) perc_of_bars_above_vwap = no_of_bars_above_vwap / no_of_bars self.vwap_above_flag[symbol] = perc_of_bars_above_vwap except: self.vwap_cross_flag[symbol] = False self.vwap_above_flag[symbol] = 0.0 #if self.vwap_cross_flag[symbol] == 1: #and self.Time.hour >= 13 and self.Time.minute >= 25: # self.Debug(f"{symbol} has vwap of {self.vwap_dict[symbol].vwap.Current.Value} vs open price of {data[symbol].Open} and close price of {data[symbol].Close} and vwap flag of {self.vwap_cross_flag[symbol]} at {self.Time}") # self.Debug(f"{symbol} has cross vwap at {self.Time}") # Track volume of current bar vs the past 20 bars for symbol in self.universe: self.high_vol_flag[symbol] = False try: if data.ContainsKey(symbol): vol_last_20_bar = self.SMA(symbol, 20, Resolution.Minute, Field.Volume) if data[symbol].Volume > 2.0 * vol_last_20_bar.Current.Value: self.high_vol_flag[symbol] = True except: self.high_vol_flag[symbol] = False #if self.high_vol_flag[symbol] == True: #and self.Time.hour >= 13 and self.Time.minute >= 25: # self.Debug(f"{symbol} has volume of {data[symbol].Volume} vs avg of last 10 bar of {vol_last_10_bar.Current.Value} and high_vol_flag of {self.high_vol_flag[symbol]} at {self.Time}") # Buying criteria for symbol in self.universe: if data.ContainsKey(symbol): invested = [x.Symbol.Value for x in self.Portfolio.Values if x.Invested] if (self.Time.hour == 15 and self.Time.minute >=50) or (self.Time.hour == 16): self.can_trade_flag == False if self.high_vol_flag[symbol] == True and self.vwap_cross_flag[symbol] == 1 and self.green_red_flag[symbol] == 1 and symbol not in invested and self.can_trade_flag == True and self.vwap_above_flag[symbol] >= 0.5: risk_quantum = self.daily_stop * self.cash stop_loss = self.vwap_dict[symbol].vwap.Current.Value - self.ATR(symbol, 20, Resolution.Daily).Current.Value*2 risk = self.ATR(symbol, 20, Resolution.Daily).Current.Value*2 take_profit = self.vwap_dict[symbol].vwap.Current.Value + risk * 3 quantity = 1000 self.MarketOrder(symbol, quantity) self.StopMarketOrder(symbol, -quantity, stop_loss) self.LimitOrder(symbol, -quantity, take_profit) #self.SetHoldings(symbol, 0.33) self.Debug(f"Buy {symbol} at {self.Time} with market entry, stop loss at {stop_loss}, risk at {risk} atr at {self.ATR(symbol, 20, Resolution.Daily).Current.Value} and take profit at {take_profit} because its vol flag is {self.high_vol_flag[symbol]}, vwap_cross_flag is {self.vwap_cross_flag[symbol]}, vwap_above_flag is {self.vwap_above_flag[symbol]} and green_red flag is {self.green_red_flag[symbol]}") """ for symbol in self.universe: if self.vwap_cross_flag[symbol] == 1 and self.high_vol_flag[symbol] == True: self.Debug(f"{symbol} meet both vwap cross and high vol criteria at {self.Time}.") for symbol in self.universe: if self.green_red_flag[symbol] == 1: self.Debug(f"{symbol} has more green volume than red in the past 10 bars at {self.Time}.") if self.green_red_flag[symbol] == -1: self.Debug(f"{symbol} has more red volume than green in the past 10 bars at {self.Time}.") """ # Check if variables are tracked throughout the session by looking at various dict at 15:49 every day #if self.Time.hour == 9 and self.Time.minute > 34 and self.Time.minute <= 45: if self.Time.hour == 15 and self.Time.minute == 59: self.Debug(f"it is {self.Time} now.") for symbol in self.universe: #bullishness = self.green_vol[symbol] / (self.green_vol[symbol] + self.red_vol[symbol]) data = self.green_red_df[symbol] df_green_vol = data.loc[data['green_red'] == "Green", "volume"].sum() df_red_vol = data.loc[data['green_red'] == "Red", "volume"].sum() self.Debug(f"According to dataframe, {symbol} has green volume of {df_green_vol} and red volume of {df_red_vol} at {self.Time}") #self.Debug(f"{symbol} has vwap of {self.vwap_dict[symbol].vwap.Current.Value} vs open price of {data[symbol].Open} and close price of {data[symbol].Close} at {self.Time}") # TrackOpen is executed everyday at 9:31 and track the opening price of the current trading day. Slight descrepency with actual opening price but close enough def TrackOpen(self): for symbol, volume in self.volume_by_symbol.items(): historyDataMin = self.History(symbol,1,Resolution.Minute) try: open_price_sym = historyDataMin['open'][-1] except: self.Debug(f"Opening price data for current day unavailable for {symbol.Value}") self.open_by_symbol[symbol] = open_price_sym #self.Debug(f'{symbol} has open price of {open_price_sym} at {self.Time}') # Calculate gap% for symbol, volume in self.volume_by_symbol.items(): historyData = self.History(symbol,2,Resolution.Daily) try: #openDayAfterEarnings = historyData['open'][0] openDayAfterEarnings = self.open_by_symbol[symbol] closeDayBeforeEarnings = historyData['close'][-1] except: self.Debug(f"History data unavailable for {symbol.Value}") continue priceGap = openDayAfterEarnings - closeDayBeforeEarnings percentGap = priceGap / closeDayBeforeEarnings if (percentGap > 0.03): self.gap[symbol] = percentGap self.Debug(f'{symbol} closes at {closeDayBeforeEarnings} previously and opens at {openDayAfterEarnings} now, it has gap percentage of {percentGap} at {self.Time}') self.vol_after_gap[symbol] = volume #self.vwap_dict[symbol] = SymbolData(symbol, self) self.Debug(f'Universe size after gap filter: {len(self.gap)}') # SelectUniverse is executed at 9:35 everyday to calculate gap %, filter by +-5%, then calculate rvol, sort the narrowed list after gap filter by rvol, then output the top 10 def SelectUniverse(self): self.universe = [] # Calculate rvol and sort that. Get top 10 stocks for symbol, gap in self.gap.items(): volume = self.vol_after_gap[symbol] symbol_sma = self.SMA(symbol, 20, Resolution.Daily, Field.Volume) symbol_rvol = self.volume_by_symbol[symbol] / self.SMA(symbol, 20, Resolution.Daily, Field.Volume).Current.Value self.rvol_by_symbol[symbol] = symbol_rvol sorted_rvol_ls = sorted(self.rvol_by_symbol.items(), key=lambda x:x[1], reverse=True) temp_ls = [] for tuple in sorted_rvol_ls: if tuple[1] > 0: temp_ls.append(tuple[0]) self.universe = temp_ls[:10] for symbol in self.universe: self.Debug(f'{symbol} has avg daily ATR of {self.ATR(symbol, 20, Resolution.Daily).Current.Value}, avg daily volume of {self.SMA(symbol, 20, Resolution.Daily, Field.Volume)} and intraday volume of {self.volume_by_symbol[symbol]} and rvol of {self.rvol_by_symbol[symbol]} and gap of {self.gap[symbol]} and green vol of {self.green_vol[symbol]} and red vol of {self.red_vol[symbol]} at {self.Time}') #self.vwap_dict[symbol] = SymbolData(symbol, self) # Clear all dictionary to start fresh the next day self.volume_by_symbol.clear() self.rvol_by_symbol.clear() self.open_by_symbol.clear() self.gap.clear() self.vol_after_gap.clear() self.logged = False self.can_trade_flag = True def ClosePositions(self): if self.Portfolio.Invested: self.Liquidate() class SymbolData: def __init__(self,symbol,algo): self.algo = algo self.symbol = symbol self.vwap = algo.VWAP(self.symbol) def Update(self,time,close): self.vwap.Update(time,close)