Overall Statistics |
Total Trades 43 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 0 Tracking Error 0 Treynor Ratio 0 Total Fees $43.00 Estimated Strategy Capacity $6100000.00 Lowest Capacity Asset DRI R735QTJ8XC9X Portfolio Turnover 450.72% |
#region imports from AlgorithmImports import * import pandas as pd import datetime #endregion class ParticleTransdimensionalAutosequencers(QCAlgorithm): def Initialize(self): # BASIC SETTINGS self.SetStartDate(2023, 6, 22) self.SetEndDate(2023, 6, 22) self.SetCash(50000) self.cash = 50000 self.equityls = ['SPY'] for symbol in self.equityls: self.AddEquity(symbol, Resolution.Second, extendedMarketHours=False) self.AddUniverseSelection(CoarseFundamentalUniverseSelectionModel(self.CoarseSelectionFunction)) self.UniverseSettings.Resolution = Resolution.Second self.UniverseSettings.extendedMarketHours = True # SCHEDULED EVENTS self.TrackOpen_hour = 9 self.TrackOpen_min = 29 self.TrackOpen_sec = 55 self.SelectUniverse_hour = 10 self.SelectUniverse_min = 0 # Track open price of the day self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(self.TrackOpen_hour, self.TrackOpen_min, self.TrackOpen_sec), self.TrackOpen) # Select stocks with highest RVOL in the first 15 mins of trading # self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(self.SelectUniverse_hour , self.SelectUniverse_min), self.SelectUniverse) # Track PM levels + 9:30 - selectUniverse levels self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(self.TrackOpen_hour, self.TrackOpen_min, self.TrackOpen_sec +1), self.DetectLevels) # Liquidate all position before market close self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(9, 45), self.ClosePositions) # Cancel all open orders before market close self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(9, 30), self.CancelOpenOrders) # METRICS TO TRACK # List or dictionary to keep track of values for the universe selection self.universe = [] self.universe_neg = [] self.universe_combined = [] self.volume_by_symbol = {} self.open_by_symbol = {} self.gap = {} self.gap_neg = {} self.vol_after_gap = {} self.vol_after_gap_neg = {} self.rvol_by_symbol = {} self.rvol_by_symbol_neg = {} 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.consol_dict = {} self.vwap_df = {} self.vwap_cross_flag = {} self.PM_rs_cross_flag = {} self.PM_sp_cross_flag = {} self.vwap_above_flag = {} self.vwap_above_flag_20_bars = {} self.vwap_high_flag = {} self.vwap_low_flag = {} self.high_vol_flag = {} self.green_red_flag = {} self.can_trade_flag = False self.atr_dict = {} self.HoDay = {} self.LoDay = {} self.entry_orders = {} self.order_type = {} self.SL = {} self.SL_id = {} self.TP = {} self.TP_id = {} self.entry_prices = {} self.ST_range = {} self.sma = {} self.support = {} self.support_levels = {} self.resistance = {} self.resistance_levels = {} self.all_levels = {} # PARAMETERS TO ADJUST self.PT_Pct = 0.2 self.bars_tracked = 20 self.gap_thres = 0.03 self.high_vol_thres = 1.3 self.rvol_threshold = 0.0 self.stop_thres_atr = 0.1 self.RRR = 1.0 self.actual_RRR_cap_reversal_1 = 0.25 self.actual_RRR_cap_reversal_2 = 0.35 self.actual_RRR_cap_reversal_3 = 0.5 self.actual_RRR_cap_reversal_4 = 0.75 self.actual_RRR_cap_reversal_5 = 1.0 self.take_profit_quantity_1 = 0.5 self.take_profit_quantity_2 = 0.2 self.take_profit_quantity_3 = 0.15 self.take_profit_quantity_4 = 0.1 self.take_profit_quantity_5 = 0.05 self.actual_RRR_cap_breakout = 1.0 self.daily_stop = 0.02 self.daily_PL = 0 self.no_of_trades = 10 self.bars_apart = 0 self.levels_lookback = 2 self.levels_lookforward = 2 self.levels_lookback_PM = 1 self.levels_lookforward_PM = 1 # UNIVERSE SELECTION (00:00) # 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 > 5 and c.Price < 10000 and c.Volume > 1000000 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() self.daily_PL = 0 # 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','low','vwap_high', 'vwap_low','range','sma_fast','sma_slow','trend','volume']) self.vwap_cross_flag[symbol] = False self.PM_rs_cross_flag[symbol] = False self.PM_sp_cross_flag[symbol] = False self.green_red_flag[symbol] = 0 self.high_vol_flag[symbol] = False self.vwap_above_flag[symbol] = 0.0 self.vwap_high_flag[symbol] = False self.vwap_low_flag[symbol] = False try: self.vwap_dict[symbol] = SymbolData(symbol, self) self.sma[symbol] = SMAData(symbol, self) except: #self.Debug(f"{symbol} data not available. Need to be added using AddSecurity()") self.AddEquity(symbol, Resolution.Second, extendedMarketHours=False) self.vwap_dict[symbol] = SymbolData(symbol, self) self.sma[symbol] = SMAData(symbol, self) #self.Debug(f"Added {symbol} data using AddSecurity()") return list(self.volume_by_symbol.keys()) # DATA INFLOW (09:30) # OnData executed whenever there is new data arriving. In this case, new data arrive at 9:30 every day at customised interval (due to Resolution.Second and consolidator) def OnDataConsolidated(self, sender, bar): symbol = sender.Consolidated.Symbol stop_time = self.Time.replace(hour=9, minute=45, second=0) if self.Time > stop_time: return # register VWAP indicator at 9:30 if self.Time.hour == 9 and self.Time.minute == 30: for symbol in self.universe_combined: self.vwap_dict[symbol] = SymbolData(symbol, self) self.sma[symbol] = SMAData(symbol, self) # Track if stocks are tradable at that time range if (self.Time.hour == 9 and self.Time.minute <=35): self.can_trade_flag = True else: self.can_trade_flag = False if self.daily_PL < - self.Portfolio.TotalPortfolioValue * self.daily_stop: self.can_trade_flag = False # Every bar, data got pumped in for many securities. We record the volume data for stocks that passes our coarse selection filter try: self.volume_by_symbol[symbol] += bar.Volume # if the bar is green, record the volume in a seperate dictionary if bar.Close > bar.Open: self.green_vol[symbol] += bar.Volume self.green_red_df[symbol].loc[self.Time] = [bar.Volume, "Green"] if bar.Close < bar.Open: self.red_vol[symbol] += bar.Volume self.green_red_df[symbol].loc[self.Time] = [bar.Volume, "Red"] except: self.Log(f'{symbol} has data[symbol] as non-type') # Every bar, data got pumped in for many securities. We record the volume data for stocks that passes our coarse selection filter try: self.vwap_dict[symbol].Update(bar) vwap = self.vwap_dict[symbol].vwap.Current.Value self.sma[symbol].Update(self.Time, bar.Close) sma_fast = round(self.sma[symbol].sma5.Current.Value,1) sma_slow = round(self.sma[symbol].sma10.Current.Value,1) if self.Time.hour == 9 and self.Time.minute <= 29: trend = "none" if sma_fast > sma_slow and sma_fast > self.vwap_df[symbol].iloc[-1]['sma_fast'] and self.vwap_df[symbol].iloc[-1]['sma_fast'] > self.vwap_df[symbol].iloc[-2]['sma_fast']: trend = "uptrend" elif sma_fast < sma_slow and sma_fast < self.vwap_df[symbol].iloc[-1]['sma_fast'] and self.vwap_df[symbol].iloc[-1]['sma_fast'] < self.vwap_df[symbol].iloc[-2]['sma_fast']: trend = "downtrend" else: trend = "none" self.vwap_df[symbol].loc[self.Time] = [vwap, bar.Close, bar.High, bar.Low, bar.High - vwap, vwap - bar.Low, bar.High - bar.Low, sma_fast, sma_slow, trend, bar.Volume] except: self.Log(f'{symbol} has data[symbol] as non-type') # When volume_by_symbol is cleared (it is cleared 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 for gap up stocks after volume filter: {len(self.universe)} at {self.Time}") self.Debug(f"Universe size for gap down stocks after volume filter: {len(self.universe_neg)} at {self.Time}") self.Debug(f"Combined universe size after volume filter: {len(self.universe_combined)} at {self.Time}") self.Debug(f"Number of trades: {self.no_of_trades} at {self.Time}") #return try: data_symbol = self.green_red_df[symbol] length_of_data = data_symbol.shape[0] if length_of_data <= self.bars_tracked: last10barvol = data_symbol[-length_of_data:] else: last10barvol = data_symbol[-self.bars_tracked:] 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}') #if self.Time.hour == 9 and self.Time.minute == 32: #self.Debug(f'it is {self.Time} now') bullishness_last10 = 0.5 if bullishness_last10 >= 0.75: self.green_red_flag[symbol] = bullishness_last10 elif bullishness_last10 <= 0.25: self.green_red_flag[symbol] = bullishness_last10 else: self.green_red_flag[symbol] = bullishness_last10 except: self.Log(f'{symbol} fail to register near term bullishness') try: self.vwap_cross_flag[symbol] = False # reset flag to 0 every bar self.PM_rs_cross_flag[symbol] = False self.PM_sp_cross_flag[symbol] = False self.vwap_above_flag[symbol] = 0.0 # reset flag to 0 every bar try: PM_rs_max_key = max(list(self.resistance_levels[symbol].keys())) PM_rs_max = list(self.resistance_levels[symbol][PM_rs_max_key].keys())[0] PM_sp_min_key = min(list(self.support_levels[symbol].keys())) PM_sp_min = list(self.support_levels[symbol][PM_sp_min_key].keys())[0] #self.Debug(f"{symbol} has PM min support level of {PM_sp_min} and max resistance level of {PM_rs_max} at {self.Time}") except: PM_rs_max = 1000000 PM_sp_min = -100000 # self.Debug(f"{symbol} has no PM levels") self.vwap_dict[symbol].Update(bar) vwap = self.vwap_dict[symbol].vwap.Current.Value self.sma[symbol].Update(self.Time, bar.Close) # vwap cross flag if (bar.High > vwap and bar.Low < vwap) and (bar.Close > bar.Open): self.vwap_cross_flag[symbol] = 1 elif (bar.High > vwap and bar.Low < vwap) and (bar.Close < bar.Open): self.vwap_cross_flag[symbol] = -1 else: self.vwap_cross_flag[symbol] = False # PM resistance cross flag if (bar.High > PM_rs_max and bar.Low < PM_rs_max) and (bar.Close > bar.Open): self.PM_rs_cross_flag[symbol] = 1 elif (bar.High > PM_rs_max and bar.Low < PM_rs_max) and (bar.Close < bar.Open): self.PM_rs_cross_flag[symbol] = -1 else: self.PM_rs_cross_flag[symbol] = False # PM support cross flag if (bar.High > PM_sp_min and bar.Low < PM_sp_min) and (bar.Close > bar.Open): self.PM_sp_cross_flag[symbol] = 1 elif (bar.High > PM_sp_min and bar.Low < PM_sp_min) and (bar.Close < bar.Open): self.PM_sp_cross_flag[symbol] = -1 else: self.PM_sp_cross_flag[symbol] = False # vwap df tracking sma_fast = round(self.sma[symbol].sma5.Current.Value,1) sma_slow = round(self.sma[symbol].sma10.Current.Value,1) if sma_fast > sma_slow and sma_fast > self.vwap_df[symbol].iloc[-1]['sma_fast'] and self.vwap_df[symbol].iloc[-1]['sma_fast'] > self.vwap_df[symbol].iloc[-2]['sma_fast']: trend = "uptrend" elif sma_fast < sma_slow and sma_fast < self.vwap_df[symbol].iloc[-1]['sma_fast'] and self.vwap_df[symbol].iloc[-1]['sma_fast'] < self.vwap_df[symbol].iloc[-2]['sma_fast']: trend = "downtrend" else: trend = "none" self.vwap_df[symbol].loc[self.Time] = [vwap, bar.Close, bar.High, bar.Low, bar.High - vwap, vwap - bar.Low, bar.High - bar.Low, sma_fast , sma_slow, trend, bar.Volume] 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 # vwap above for last 20 bars if no_of_bars >= self.bars_tracked: vwap_last_20_bars = self.vwap_df[symbol][-self.bars_tracked:] no_of_bars_above_vwap_20_bars = len(vwap_last_20_bars[self.vwap_df[symbol]['close'] > self.vwap_df[symbol]['vwap']]) self.vwap_above_flag_20_bars[symbol] = no_of_bars_above_vwap_20_bars / self.bars_tracked else: self.vwap_above_flag_20_bars[symbol] = self.vwap_above_flag[symbol] # High of Day tracking self.HoDay[symbol] = self.vwap_df[symbol]['high'].max() # Low of Day tracking self.LoDay[symbol] = self.vwap_df[symbol]['low'].min() # Track vwap distance (long) if self.vwap_high_flag[symbol] == False: vwap_high = max(self.vwap_df[symbol]['vwap_high']) if vwap_high > self.atr_dict[symbol].atr.Current.Value * 1: self.vwap_high_flag[symbol] = True # Track vwap distance (short) if self.vwap_low_flag[symbol] == False: vwap_low = max(self.vwap_df[symbol]['vwap_low']) if vwap_low > self.atr_dict[symbol].atr.Current.Value * 1: self.vwap_low_flag[symbol] = True # Latest range tracking vwap_last_5_bars = self.vwap_df[symbol][-5:] self.ST_range[symbol] = vwap_last_5_bars['range'].mean() except: self.Log(f'{symbol} fail to register near VWAP, resistance and support levels cross') #except: # self.vwap_cross_flag[symbol] = False # self.vwap_above_flag[symbol] = 0.0 try: # Track Levels if self.Time.hour >= self.SelectUniverse_hour and self.Time.minute >= self.SelectUniverse_min + 2: n1 = self.levels_lookback n2 = self.levels_lookforward history = self.vwap_df[symbol].tail(n1 + n2 + 1) l = len(history) for row in range(n1, l-n2): if support(history,row,n1,n2): self.support[symbol][history.index[row-1]] = history.low[row] rounded_level = round(history.low[row],0) if rounded_level not in list(self.support_levels[symbol].keys()): self.support_levels[symbol][rounded_level] = {history.low[row]: history.volume[row]} else: previous_walvl = list(self.support_levels[symbol][rounded_level].keys())[0] previous_vol = list(self.support_levels[symbol][rounded_level].values())[0] new_lvl_input = history.low[row] new_lvl_vol = history.volume[row] updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol) updated_vol = previous_vol + new_lvl_vol self.support_levels[symbol][rounded_level] = {updated_walvl: updated_vol} if rounded_level not in list(self.all_levels[symbol].keys()): self.all_levels[symbol][rounded_level] = {history.low[row]: history.volume[row]} else: previous_walvl = list(self.all_levels[symbol][rounded_level].keys())[0] previous_vol = list(self.all_levels[symbol][rounded_level].values())[0] new_lvl_input = history.low[row] new_lvl_vol = history.volume[row] updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol) updated_vol = previous_vol + new_lvl_vol self.all_levels[symbol][rounded_level] = {updated_walvl: updated_vol} if resistance(history,row,n1,n2): self.resistance[symbol][history.index[row-1]] = history.high[row] rounded_level = round(history.high[row],0) if rounded_level not in list(self.resistance_levels[symbol].keys()): self.resistance_levels[symbol][rounded_level] = {history.high[row]: history.volume[row]} else: previous_walvl = list(self.resistance_levels[symbol][rounded_level].keys())[0] previous_vol = list(self.resistance_levels[symbol][rounded_level].values())[0] new_lvl_input = history.high[row] new_lvl_vol = history.volume[row] updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol) updated_vol = previous_vol + new_lvl_vol self.resistance_levels[symbol][rounded_level] = {updated_walvl: updated_vol} if rounded_level not in list(self.all_levels[symbol].keys()): self.all_levels[symbol][rounded_level] = {history.high[row]: history.volume[row]} else: previous_walvl = list(self.all_levels[symbol][rounded_level].keys())[0] previous_vol = list(self.all_levels[symbol][rounded_level].values())[0] new_lvl_input = history.high[row] new_lvl_vol = history.volume[row] updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol) updated_vol = previous_vol + new_lvl_vol self.all_levels[symbol][rounded_level] = {updated_walvl: updated_vol} except: self.Log(f'{symbol} fail to track levels') # Track volume of current bar vs the past 20 bars try: self.high_vol_flag[symbol] = False try: vol_last_20_bar = self.SMA(symbol, self.bars_tracked, Resolution.Second, Field.Volume) if bar.Volume > self.high_vol_thres * vol_last_20_bar.Current.Value: self.high_vol_flag[symbol] = True except: self.high_vol_flag[symbol] = False self.Log(f'{symbol} fail to high vol flag') except: self.Log(f'{symbol} fail to high vol flag') # Buying criteria try: invested = [x.Symbol for x in self.Portfolio.Values if x.Invested] try: if (bar.Close - self.LoDay[symbol]) > 0: day_range_RRR = (self.HoDay[symbol] - bar.Close)/(bar.Close - self.LoDay[symbol]) else: day_range_RRR = 0 except: day_range_RRR = 0 try: last_support_time = list(self.support[symbol].keys())[-1] if self.Time - last_support_time <= datetime.timedelta(minutes=5): just_support = True else: just_support = False except: just_support = False long_reversal_condition = (symbol not in invested and self.PM_rs_cross_flag[symbol] == 1 and self.can_trade_flag == True and self.vwap_df[symbol].iloc[-1]['trend'] == "uptrend") long_breakout_condition = (symbol not in invested and self.vwap_high_flag[symbol] == True and self.vwap_cross_flag[symbol] == 1 and self.can_trade_flag == True and self.green_red_flag[symbol] >= 0.6 and self.high_vol_flag[symbol] == True and self.Time.hour < 14 and self.vwap_above_flag[symbol] >= 0.75 and self.vwap_df[symbol].iloc[-1]['trend'] == "uptrend") if long_reversal_condition: # if self.high_vol_flag[symbol] == True and self.vwap_cross_flag[symbol] == 1 and self.green_red_flag[symbol] >= 0.75 and symbol not in invested and self.can_trade_flag == True and self.vwap_above_flag[symbol] >= 0.5 and self.vwap_above_flag_20_bars[symbol] >= 0.75 and self.vwap_high_flag[symbol] == True: # Bracket take_profit = data[symbol].Close * 20 profit_potential = take_profit - bar.Close stop_loss_support = list(self.support[symbol].values())[-1] - self.ST_range[symbol] * 0.5 stop_loss_latest_atr = self.vwap_dict[symbol].vwap.Current.Value - self.ST_range[symbol] * 1 # stop_loss = max(stop_loss_day_range , stop_loss_latest_atr) stop_loss = stop_loss_support risk = bar.Close - stop_loss # Quantity risk_quantum = self.daily_stop * self.Portfolio.TotalPortfolioValue * 1 / self.no_of_trades theoretical_quantity = risk_quantum / risk buying_power = self.Portfolio.GetBuyingPower(symbol, OrderDirection.Buy) binding_quantity = buying_power / (self.vwap_dict[symbol].vwap.Current.Value * 1.02) quantity = min(theoretical_quantity, binding_quantity) # Entry criteria if profit_potential / risk >= self.RRR: # Debug on conditions self.Debug(f"BUY {symbol} at {self.Time} meet long_reversal_condition: {long_reversal_condition}, long_breakout_condition: {long_breakout_condition}") self.Debug(f"BUY {symbol} at {self.Time} with implied RRR {profit_potential / risk}") try: PM_rs_max_key = max(list(self.resistance_levels[symbol].keys())) PM_rs_max = list(self.resistance_levels[symbol][PM_rs_max_key].keys())[0] PM_sp_min_key = min(list(self.support_levels[symbol].keys())) PM_sp_min = list(self.support_levels[symbol][PM_sp_min_key].keys())[0] except: PM_rs_max = 1000000 PM_sp_min = -100000 self.Debug(f"PM resistance at {PM_rs_max}") self.Debug(f"PM support at {PM_sp_min}") self.Debug(f"self.PM_rs_cross_flag[symbol] at {self.PM_rs_cross_flag[symbol]}") self.Debug(f"self.vwap_df[symbol].iloc[-1]['trend'] at {self.vwap_df[symbol].iloc[-1]['trend']}") self.entry_orders[symbol] = True self.MarketOrder(symbol, quantity) # Liquidate all stocks without stoploss try: if symbol in invested and self.SL[symbol] is None: self.Liquidate(symbol) except: pass except: self.Log(f'{symbol} fail to execute buy criteria') # Short Sell criteria try: invested = [x.Symbol for x in self.Portfolio.Values if x.Invested] try: if (bar.Close - self.LoDay[symbol]) > 0: day_range_RRR = (self.HoDay[symbol] - bar.Close)/(bar.Close - self.LoDay[symbol]) else: day_range_RRR = 5 except: day_range_RRR = 5 try: last_resistance_time = list(self.resistance[symbol].keys())[-1] if self.Time - last_resistance_time < datetime.timedelta(minutes=5): just_resistance = True else: just_resistance = False except: just_resistance = False short_reversal_condition = (symbol not in invested and self.PM_sp_cross_flag[symbol] == -1 and self.can_trade_flag == True and self.vwap_df[symbol].iloc[-1]['trend'] == "downtrend") short_breakout_condition = (symbol not in invested and self.vwap_low_flag[symbol] == True and self.vwap_cross_flag[symbol] == -1 and self.can_trade_flag == True and self.green_red_flag[symbol] <= 0.4 and self.high_vol_flag[symbol] == True and self.Time.hour < 14 and self.vwap_above_flag[symbol] <= 0.25 and self.vwap_df[symbol].iloc[-1]['trend'] == "downtrend") if short_reversal_condition: # if self.high_vol_flag[symbol] == True and self.vwap_cross_flag[symbol] == -1 and self.green_red_flag[symbol] <= 0.25 and symbol not in invested and self.can_trade_flag == True and self.vwap_above_flag[symbol] <= 0.5 and self.vwap_above_flag_20_bars[symbol] <= 0.25 and self.vwap_low_flag[symbol] == True: # Bracket take_profit = self.LoDay[symbol] * 0.005 profit_potential = bar.Close - take_profit stop_loss_resistance = list(self.resistance[symbol].values())[-1] + self.ST_range[symbol] * 0.5 stop_loss_latest_atr = self.vwap_dict[symbol].vwap.Current.Value + self.ST_range[symbol] * 1 stop_loss = stop_loss_resistance risk = stop_loss - bar.Close # Quantity risk_quantum = self.daily_stop * self.Portfolio.TotalPortfolioValue * 1/ self.no_of_trades theoretical_quantity = risk_quantum / risk buying_power = self.Portfolio.GetBuyingPower(symbol, OrderDirection.Sell) binding_quantity = buying_power / (self.vwap_dict[symbol].vwap.Current.Value * 1.02) quantity = min(theoretical_quantity, binding_quantity) # Entry criteria if profit_potential / risk >= self.RRR: # Debug on conditions self.Debug(f"SELL {symbol} at {self.Time} meet short_reversal_condition: {short_reversal_condition}, short_breakout_condition: {short_breakout_condition}") self.Debug(f"SELL {symbol} at {self.Time} with implied RRR of {profit_potential / risk}") try: PM_rs_max_key = max(list(self.resistance_levels[symbol].keys())) PM_rs_max = list(self.resistance_levels[symbol][PM_rs_max_key].keys())[0] PM_sp_min_key = min(list(self.support_levels[symbol].keys())) PM_sp_min = list(self.support_levels[symbol][PM_sp_min_key].keys())[0] except: PM_rs_max = 1000000 PM_sp_min = -100000 self.Debug(f"PM resistance at {PM_rs_max}") self.Debug(f"PM support at {PM_sp_min}") self.Debug(f"self.PM_sp_cross_flag[symbol] at {self.PM_sp_cross_flag[symbol]}") self.Debug(f"self.vwap_df[symbol].iloc[-1]['trend'] at {self.vwap_df[symbol].iloc[-1]['trend']}") self.entry_orders[symbol] = True self.MarketOrder(symbol, -quantity) # Liquidate all stocks without stoploss try: if symbol in invested and self.SL[symbol] is None: self.Liquidate(symbol) except: pass except: self.Log(f'{symbol} fail to execute short sell criteria') # 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): pass # WHEN ORDER IS MADE, ATTACHED A BRACKET # Orderevent: This function is triggered automatically every time an order evenet occurs def OnOrderEvent(self, orderEvent): if orderEvent.Status == OrderStatus.Filled: if self.entry_orders[orderEvent.Symbol] == True: # For buy orders if orderEvent.FillQuantity > 0: #stop_loss_price = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value - self.atr_dict[orderEvent.Symbol].atr.Current.Value* self.stop_thres_atr stop_loss_day_range = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value - (self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol]) * self.stop_thres_atr stop_loss_latest_atr = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value - self.ST_range[orderEvent.Symbol] * 1 # stop_loss_price = max(stop_loss_day_range , stop_loss_latest_atr) PM_rs_max_key = max(list(self.resistance_levels[orderEvent.Symbol].keys())) PM_rs_max = list(self.resistance_levels[orderEvent.Symbol][PM_rs_max_key].keys())[0] stop_loss_price = PM_rs_max - self.ST_range[orderEvent.Symbol] * 0.5 risk = orderEvent.FillPrice - stop_loss_price #stop_loss_price = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value - (self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol]) * self.stop_thres_atr #risk = min(orderEvent.FillPrice - stop_loss_price , self.ST_range[orderEvent.Symbol] * 2) actual_RRR_cap = self.actual_RRR_cap_reversal_1 #take_profit_price = min(self.HoDay[orderEvent.Symbol] * 0.995, orderEvent.FillPrice + risk * actual_RRR_cap) take_profit_price_1 = orderEvent.FillPrice + risk * self.actual_RRR_cap_reversal_1 take_profit_price_2 = orderEvent.FillPrice + risk * self.actual_RRR_cap_reversal_2 take_profit_price_3 = orderEvent.FillPrice + risk * self.actual_RRR_cap_reversal_3 take_profit_price_4 = orderEvent.FillPrice + risk * self.actual_RRR_cap_reversal_4 take_profit_price_5 = orderEvent.FillPrice + risk * self.actual_RRR_cap_reversal_5 take_profit_quantity_1 = orderEvent.FillQuantity * self.take_profit_quantity_1 take_profit_quantity_2 = orderEvent.FillQuantity * self.take_profit_quantity_2 take_profit_quantity_3 = orderEvent.FillQuantity * self.take_profit_quantity_3 take_profit_quantity_4 = orderEvent.FillQuantity * self.take_profit_quantity_4 take_profit_quantity_5 = orderEvent.FillQuantity * self.take_profit_quantity_5 #take_profit_price = min(self.HoDay[orderEvent.Symbol] * 0.995, orderEvent.FillPrice + risk * actual_RRR_cap) profit_potential = take_profit_price_1 - orderEvent.FillPrice implied_RRR = profit_potential / risk self.Debug(f"BUY {orderEvent.Symbol} at {self.Time} with market entry at {orderEvent.FillPrice}, stop loss at {stop_loss_price}, RRR at {implied_RRR}, risk at {risk} atr at {self.atr_dict[orderEvent.Symbol].atr}") self.Debug(f"VWAP at {self.vwap_dict[orderEvent.Symbol].vwap.Current.Value}, day range at {self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol]} and take profit at {take_profit_price_1}") self.Debug(f"Day high is at {self.HoDay[orderEvent.Symbol]} and Day low is at {self.LoDay[orderEvent.Symbol]}") self.SL[orderEvent.Symbol] = [self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_1, stop_loss_price), self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_2, stop_loss_price), self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_3, stop_loss_price), self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_4, stop_loss_price), self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_5, stop_loss_price)] self.SL_id[orderEvent.Symbol] = [self.SL[orderEvent.Symbol][0].OrderId, self.SL[orderEvent.Symbol][1].OrderId, self.SL[orderEvent.Symbol][2].OrderId, self.SL[orderEvent.Symbol][3].OrderId, self.SL[orderEvent.Symbol][4].OrderId] self.TP[orderEvent.Symbol] = [self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_1, take_profit_price_1), self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_2, take_profit_price_2), self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_3, take_profit_price_3), self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_4, take_profit_price_4), self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_5, take_profit_price_5)] self.TP_id[orderEvent.Symbol] = [self.TP[orderEvent.Symbol][0].OrderId, self.TP[orderEvent.Symbol][1].OrderId, self.TP[orderEvent.Symbol][2].OrderId, self.TP[orderEvent.Symbol][3].OrderId, self.TP[orderEvent.Symbol][4].OrderId] self.entry_prices[orderEvent.Symbol] = orderEvent.FillPrice # For sell orders elif orderEvent.FillQuantity < 0: #stop_loss_price = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value + self.atr_dict[orderEvent.Symbol].atr.Current.Value* self.stop_thres_atr stop_loss_day_range = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value + (self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol]) * self.stop_thres_atr stop_loss_latest_atr = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value + self.ST_range[orderEvent.Symbol] * 1 # stop_loss_price = min(stop_loss_day_range , stop_loss_latest_atr) PM_sp_min_key = min(list(self.support_levels[orderEvent.Symbol].keys())) PM_sp_min = list(self.support_levels[orderEvent.Symbol][PM_sp_min_key].keys())[0] stop_loss_price = PM_sp_min + self.ST_range[orderEvent.Symbol] * 0.5 risk = stop_loss_price - orderEvent.FillPrice #stop_loss_price = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value + (self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol])* self.stop_thres_atr #risk = min(stop_loss_price - orderEvent.FillPrice, self.ST_range[orderEvent.Symbol] * 2) actual_RRR_cap = self.actual_RRR_cap_reversal_1 # take_profit_price = max(self.LoDay[orderEvent.Symbol] * 1.005, orderEvent.FillPrice - risk * actual_RRR_cap) take_profit_price_1 = orderEvent.FillPrice - risk * self.actual_RRR_cap_reversal_1 take_profit_price_2 = orderEvent.FillPrice - risk * self.actual_RRR_cap_reversal_2 take_profit_price_3 = orderEvent.FillPrice - risk * self.actual_RRR_cap_reversal_3 take_profit_price_4 = orderEvent.FillPrice - risk * self.actual_RRR_cap_reversal_4 take_profit_price_5 = orderEvent.FillPrice - risk * self.actual_RRR_cap_reversal_5 take_profit_quantity_1 = orderEvent.FillQuantity * self.take_profit_quantity_1 take_profit_quantity_2 = orderEvent.FillQuantity * self.take_profit_quantity_2 take_profit_quantity_3 = orderEvent.FillQuantity * self.take_profit_quantity_3 take_profit_quantity_4 = orderEvent.FillQuantity * self.take_profit_quantity_4 take_profit_quantity_5 = orderEvent.FillQuantity * self.take_profit_quantity_5 profit_potential = orderEvent.FillPrice - take_profit_price_1 implied_RRR = profit_potential / risk self.Debug(f"SELL {orderEvent.Symbol} at {self.Time} with market entry at {orderEvent.FillPrice}, stop loss at {stop_loss_price}, RRR at {implied_RRR}, risk at {risk} atr at {self.atr_dict[orderEvent.Symbol].atr}") self.Debug(f"VWAP at {self.vwap_dict[orderEvent.Symbol].vwap.Current.Value}, day range at {self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol]} and take profit at {take_profit_price_1}") self.Debug(f"Day high is at {self.HoDay[orderEvent.Symbol]} and Day low is at {self.LoDay[orderEvent.Symbol]}") self.SL[orderEvent.Symbol] = [self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_1, stop_loss_price), self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_2, stop_loss_price), self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_3, stop_loss_price), self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_4, stop_loss_price), self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_5, stop_loss_price)] self.SL_id[orderEvent.Symbol] = [self.SL[orderEvent.Symbol][0].OrderId, self.SL[orderEvent.Symbol][1].OrderId, self.SL[orderEvent.Symbol][2].OrderId, self.SL[orderEvent.Symbol][3].OrderId, self.SL[orderEvent.Symbol][4].OrderId] self.TP[orderEvent.Symbol] = [self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_1, take_profit_price_1), self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_2, take_profit_price_2), self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_3, take_profit_price_3), self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_4, take_profit_price_4), self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_5, take_profit_price_5)] self.TP_id[orderEvent.Symbol] = [self.TP[orderEvent.Symbol][0].OrderId, self.TP[orderEvent.Symbol][1].OrderId, self.TP[orderEvent.Symbol][2].OrderId, self.TP[orderEvent.Symbol][3].OrderId, self.TP[orderEvent.Symbol][4].OrderId] #self.SL[orderEvent.Symbol] = self.StopMarketOrder(orderEvent.Symbol, -orderEvent.FillQuantity, stop_loss_price) #self.TP[orderEvent.Symbol] = self.LimitOrder(orderEvent.Symbol, -orderEvent.FillQuantity, take_profit_price) self.entry_prices[orderEvent.Symbol] = orderEvent.FillPrice self.entry_orders[orderEvent.Symbol] = False elif (self.SL[orderEvent.Symbol] is not None) and (self.TP[orderEvent.Symbol] is not None): if self.SL[orderEvent.Symbol] is not None and orderEvent.OrderId in self.SL_id[orderEvent.Symbol]: # self.TP[orderEvent.Symbol].Cancel() position_of_order = self.SL_id[orderEvent.Symbol].index(orderEvent.OrderId) self.TP[orderEvent.Symbol][position_of_order].Cancel() self.TP_id[orderEvent.Symbol].remove(self.TP_id[orderEvent.Symbol][position_of_order]) self.SL_id[orderEvent.Symbol].remove(self.SL_id[orderEvent.Symbol][position_of_order]) if self.SL_id[orderEvent.Symbol] == []: self.SL[orderEvent.Symbol] = None self.Debug(f"Take loss at {orderEvent.FillPrice} and cancelled {orderEvent.Symbol} take profit. {len(self.TP_id[orderEvent.Symbol])} of take profit left at {self.Time}") self.daily_PL += orderEvent.FillQuantity * (self.entry_prices[orderEvent.Symbol] - orderEvent.FillPrice) self.Debug(f"Daily P&L is now {self.daily_PL}") if self.SL[orderEvent.Symbol] == []: self.entry_prices[orderEvent.Symbol] = None elif self.TP[orderEvent.Symbol] is not None and orderEvent.OrderId in self.TP_id[orderEvent.Symbol]: position_of_order = self.TP_id[orderEvent.Symbol].index(orderEvent.OrderId) self.SL[orderEvent.Symbol][position_of_order].Cancel() self.TP_id[orderEvent.Symbol].remove(self.TP_id[orderEvent.Symbol][position_of_order]) self.SL_id[orderEvent.Symbol].remove(self.SL_id[orderEvent.Symbol][position_of_order]) if self.SL_id[orderEvent.Symbol] == []: self.SL[orderEvent.Symbol] = None self.Debug(f"Take profit at {orderEvent.FillPrice} and cancelled {orderEvent.Symbol} stop loss. {len(self.SL_id[orderEvent.Symbol])} of stop loss left at {self.Time}") self.daily_PL += orderEvent.FillQuantity * (self.entry_prices[orderEvent.Symbol] - orderEvent.FillPrice) self.Debug(f"Daily P&L is now {self.daily_PL}") if self.SL_id[orderEvent.Symbol] == []: self.entry_prices[orderEvent.Symbol] = None # SCHEDULED EVENTS # 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(): previous_day = self.Time - datetime.timedelta(days=1) start = self.Time.replace(hour=9, minute=29, second=0) stop = self.Time historyDataMin = self.History(symbol, start, stop, Resolution.Second, extendedMarketHours=True) try: open_price_sym = historyDataMin['open'][-1] except: open_price_sym = 0 self.open_by_symbol[symbol] = open_price_sym # Calculate gap% for symbol, volume in self.volume_by_symbol.items(): historyData = self.History(symbol,2,Resolution.Daily) try: today_open = self.open_by_symbol[symbol] ytd_close = historyData['close'][-1] except: self.Debug(f"History data unavailable for {symbol.Value}") continue priceGap = today_open - ytd_close percentGap = priceGap / ytd_close if (percentGap > self.gap_thres): self.gap[symbol] = percentGap self.Debug(f'Gap up stock {symbol} closes at {ytd_close} previously and opens at {today_open} now, it has gap up percentage of {percentGap} at {self.Time}') self.vol_after_gap[symbol] = volume if (percentGap < -self.gap_thres) and (percentGap > -1): self.gap_neg[symbol] = percentGap self.Debug(f'Gap down stock {symbol} closes at {ytd_close} previously and opens at {today_open} now, it has gap down percentage of {percentGap} at {self.Time}') self.vol_after_gap_neg[symbol] = volume self.Debug(f'Universe size after gap positive filter: {len(self.gap)} and negative filter: {len(self.gap_neg)} at {self.Time}') # SelectUniverse is executed to calculate gap %, then calculate rvol, sort the narrowed list after gap filter by rvol, then output the top few def SelectUniverse(self): self.universe = [] self.universe_neg = [] self.universe_combined = [] # Calculate rvol for gap up stocks and sort that 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 if symbol_sma.Current.Value >= 0: 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] > self.rvol_threshold: # A stock has to trade at least 30% of average daily volume to be qualified as in-play stocks temp_ls.append(tuple[0]) self.universe = temp_ls[:300] for symbol in self.universe: self.atr_dict[symbol] = AtrData(symbol, self) atr = self.atr_dict[symbol].atr self.Debug(f'Gap up stock {symbol} has avg daily ATR of {atr}, 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}') # Calculate rvol for gap down stocks and sort that for symbol, gap in self.gap_neg.items(): volume = self.vol_after_gap_neg[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 if symbol_sma.Current.Value >= 0: self.rvol_by_symbol_neg[symbol] = symbol_rvol sorted_rvol_ls_neg = sorted(self.rvol_by_symbol_neg.items(), key=lambda x:x[1], reverse=True) temp_ls = [] for tuple in sorted_rvol_ls_neg: if tuple[1] > self.rvol_threshold: # A stock has to trade at least 30% of average daily volume to be qualified as in-play stocks temp_ls.append(tuple[0]) self.universe_neg = temp_ls[:300] for symbol in self.universe_neg: self.atr_dict[symbol] = AtrData(symbol, self) atr = self.atr_dict[symbol].atr self.Debug(f'Gap down stock {symbol} has avg daily ATR of {atr}, 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_neg[symbol]} and gap of {self.gap_neg[symbol]} and green vol of {self.green_vol[symbol]} and red vol of {self.red_vol[symbol]} at {self.Time}') self.universe_combined = self.universe + self.universe_neg # Clear all dictionary to start fresh the next day self.volume_by_symbol.clear() self.rvol_by_symbol.clear() self.rvol_by_symbol_neg.clear() self.open_by_symbol.clear() self.gap.clear() self.gap_neg.clear() self.vol_after_gap.clear() self.vol_after_gap_neg.clear() self.logged = False self.can_trade_flag = True def ClosePositions(self): if self.Portfolio.Invested: self.Liquidate() self.Debug(f"All stocks liquidated. Daily P&L at {self.daily_PL} at {self.Time}") openOrders = self.Transactions.GetOpenOrders() if len(openOrders)> 0: for x in openOrders: self.Transactions.CancelOrder(x.Id) self.Debug(f"Open order for {x} cancelled. Daily P&L at {self.daily_PL} at {self.Time}") def CancelOpenOrders(self): openOrders = self.Transactions.GetOpenOrders() if len(openOrders)> 0: for x in openOrders: self.Transactions.CancelOrder(x.Id) def DetectLevels(self): def support(df1, l, n1, n2): #n1 n2 before and after candle l for i in range(l-n1+1, l+1): if(df1.low[i]>df1.low[i-1]): return 0 for i in range(l+1,l+n2+1): if(df1.low[i]<df1.low[i-1]): return 0 return 1 def resistance(df1, l, n1, n2): #n1 n2 before and after candle l for i in range(l-n1+1, l+1): if(df1.high[i]<df1.high[i-1]): return 0 for i in range(l+1,l+n2+1): if(df1.high[i]>df1.high[i-1]): return 0 return 1 self.universe = [] self.universe_neg = [] self.universe_combined = [] gap_combined = [] for symbol, gap in self.gap.items(): gap_combined.append(symbol) self.universe.append(symbol) self.universe_combined.append(symbol) for symbol, gap in self.gap_neg.items(): gap_combined.append(symbol) self.universe_neg.append(symbol) self.universe_combined.append(symbol) for symbol in self.universe_neg: self.atr_dict[symbol] = AtrData(symbol, self) for symbol in self.universe: self.atr_dict[symbol] = AtrData(symbol, self) atr = self.atr_dict[symbol].atr for removed in self.consol_dict.items(): if removed is not None: self.SubscriptionManager.RemoveConsolidator(removed[1].symbol, removed[1].TenSecConsolidator) self.consol_dict = {} for symbol in self.universe_combined: self.consol_dict[symbol] = SymbolData(symbol, self) self.consol_dict[symbol].TenSecConsolidator.DataConsolidated += self.OnDataConsolidated self.SubscriptionManager.AddConsolidator(symbol, self.consol_dict[symbol].TenSecConsolidator) #self.Debug(f'Reg {symbol} in consolidator at {self.Time}') for symbol in gap_combined: #try: ss = {} ss_levels = {} rr = {} rr_levels = {} all_levels = {} # previous_day = self.Time - datetime.timedelta(days=1) # start = previous_day.replace(hour=16, minute=0, second=0) start = self.Time.replace(hour=8, minute=30, second=0) stop = self.Time history = self.History(symbol, start, stop, Resolution.Minute, extendedMarketHours=True) # history = self.History(symbol, 630 , Resolution.Minute, extendedMarketHours=True) n1 = self.levels_lookback_PM n2 = self.levels_lookforward_PM l = len(history) for row in range(n1, l-n2): if support(history,row,n1,n2): ss[history.index[row-1][1]] = history.low[row] rounded_level = round(history.low[row],0) if rounded_level not in list(ss_levels.keys()): try: ss_levels[rounded_level] = {history.low[row]: history.volume[row]} except: self.Debug(f"volume not available for {symbol} at {self.Time}") ss_levels[rounded_level] = {history.low[row]: 0} else: previous_walvl = list(ss_levels[rounded_level].keys())[0] previous_vol = list(ss_levels[rounded_level].values())[0] new_lvl_input = history.low[row] try: new_lvl_vol = history.volume[row] except: new_lvl_vol = 0 updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol) updated_vol = previous_vol + new_lvl_vol ss_levels[rounded_level] = {updated_walvl: updated_vol} if rounded_level not in list(all_levels.keys()): try: all_levels[rounded_level] = {history.low[row]: history.volume[row]} except: self.Debug(f"volume not available for {symbol} at {self.Time}") all_levels[rounded_level] = {history.low[row]: 0} else: previous_walvl = list(all_levels[rounded_level].keys())[0] previous_vol = list(all_levels[rounded_level].values())[0] new_lvl_input = history.low[row] try: new_lvl_vol = history.volume[row] except: new_lvl_vol = 0 updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol) updated_vol = previous_vol + new_lvl_vol all_levels[rounded_level] = {updated_walvl: updated_vol} if resistance(history,row,n1,n2): rr[history.index[row-1][1]] = history.high[row] rounded_level = round(history.high[row],0) if rounded_level not in list(rr_levels.keys()): try: rr_levels[rounded_level] = {history.high[row]: history.volume[row]} except: self.Debug(f"volume not available for {symbol} at {self.Time}") rr_levels[rounded_level] = {history.high[row]: 0} else: previous_walvl = list(rr_levels[rounded_level].keys())[0] previous_vol = list(rr_levels[rounded_level].values())[0] new_lvl_input = history.high[row] try: new_lvl_vol = history.volume[row] except: new_lvl_vol = 0 updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol) updated_vol = previous_vol + new_lvl_vol rr_levels[rounded_level] = {updated_walvl: updated_vol} if rounded_level not in list(all_levels.keys()): try: all_levels[rounded_level] = {history.high[row]: history.volume[row]} except: all_levels[rounded_level] = {history.high[row]: 0} else: previous_walvl = list(all_levels[rounded_level].keys())[0] previous_vol = list(all_levels[rounded_level].values())[0] new_lvl_input = history.high[row] try: new_lvl_vol = history.volume[row] except: new_lvl_vol = 0 updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol) updated_vol = previous_vol + new_lvl_vol all_levels[rounded_level] = {updated_walvl: updated_vol} self.support[symbol] = ss self.resistance[symbol] = rr self.support_levels[symbol] = ss_levels self.resistance_levels[symbol] = rr_levels self.all_levels[symbol] = all_levels self.Debug(f"support and resistance levels ready for {symbol} at {self.Time}") # Clear all dictionary to start fresh the next day self.volume_by_symbol.clear() self.rvol_by_symbol.clear() self.rvol_by_symbol_neg.clear() self.open_by_symbol.clear() self.gap.clear() self.gap_neg.clear() self.vol_after_gap.clear() self.vol_after_gap_neg.clear() self.logged = False self.can_trade_flag = True #except: # self.Debug(f"Can't detect levels for {symbol}") # DATA STRCUTRE TO STORE VWAP AND ATR class SymbolData: def __init__(self,symbol,algo): self.algo = algo self.symbol = symbol self.vwap = algo.VWAP(self.symbol) self.TenSecConsolidator = TradeBarConsolidator(timedelta(seconds=10)) def Update(self,bar): self.vwap.Update(bar) class SMAData: def __init__(self,symbol,algo): self.algo = algo self.symbol = symbol self.sma5 = SimpleMovingAverage(3) self.sma10 = SimpleMovingAverage(5) def Update(self,time,close): self.sma5.Update(time,close) self.sma10.Update(time,close) class AtrData: def __init__(self,symbol,algo): self.algo = algo self.symbol = symbol self.atr = AverageTrueRange(self.symbol, 20) history = algo.History(symbol,20,Resolution.Daily) for bar in history.itertuples(): tradebar = TradeBar(bar.Index[1], self.symbol, bar.open, bar.high, bar.low, bar.close, bar.volume) self.atr.Update(tradebar) def Update(self,time,close): history = algo.History(symbol,1,Resolution.Daily) for bar in history.itertuples(): tradebar = TradeBar(bar.Index[1], self.symbol, bar.open, bar.high, bar.low, bar.close, bar.volume) self.atr.Update(tradebar) def support(df1, l, n1, n2): #n1 n2 before and after candle l for i in range(l-n1+1, l+1): if(df1.low[i]>df1.low[i-1]): return 0 for i in range(l+1,l+n2+1): if(df1.low[i]<df1.low[i-1]): return 0 return 1 def resistance(df1, l, n1, n2): #n1 n2 before and after candle l for i in range(l-n1+1, l+1): if(df1.high[i]<df1.high[i-1]): return 0 for i in range(l+1,l+n2+1): if(df1.high[i]>df1.high[i-1]): return 0 return 1