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