Overall Statistics
Total Trades
56
Average Win
0%
Average Loss
0.00%
Compounding Annual Return
-0.383%
Drawdown
0.100%
Expectancy
-1
Net Profit
-0.057%
Sharpe Ratio
-10.721
Probabilistic Sharpe Ratio
0%
Loss Rate
100%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
-0.003
Beta
-0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-3.25
Tracking Error
0.234
Treynor Ratio
32.246
Total Fees
$56.00
Estimated Strategy Capacity
$130000.00
Lowest Capacity Asset
AGFY XLIR2IEBJNFP
# region imports
from AlgorithmImports import *
# endregion

# Import statistics
import statistics

# Import datetime
import datetime

class VirtualGreenHornet(QCAlgorithm):

    def Initialize(self):

        # Set Start Date
        self.SetStartDate(2022, 10, 1)

        # Set End Date
        # self.SetEndDate(2022, 10, 5)

        # Set Strategy Cash
        self.SetCash(100000)

        # Add universe
        self.AddUniverse(self.Coarse, self.Fine)

        # Add SPY
        self.SPY_symbol = self.AddEquity("SPY", Resolution.Minute, extendedMarketHours = True).Symbol
        
        # Universe extended hours
        self.UniverseSettings.ExtendedMarketHours = True

        # Universe resolution
        self.UniverseSettings.Resolution = Resolution.Minute

        # # # Storage # # #

        # Symbols storage
        self.symbols_list = []

        # Consolidator dictionary
        self.consolidator_dictionary = {}

        # 9:30 am price
        self.day_starting_price = {}

        # Volume
        self.day_candles = {}

        # List to store stocks that pass scanning component
        self.list_1 = []

        # Counter
        self.counter = 0

        # 5-minute consolidator dictionary
        self.five_minute_consolidator_dictionary = {}

        # 5-minute bar dictionary
        self.five_minute_bar_dictionary = {}

        # Counter 2
        self.counter_2 = 0

        # 10-minute consolidator dictionary
        self.ten_minute_consolidator_dictionary = {}

        # 5-minute bar dictionary
        self.ten_minute_bar_dictionary = {}

        # Counter 3
        self.counter_3 = 0

        # List 2
        self.list_2 = []

        # RSI storage
        self.RSI_storage = {}

        # RSI values storage
        self.RSI_values = {}

        # Warm up
        self.warm_up = False

        # # # End storage # # #

        # Schedule 1
        self.Schedule.On(self.DateRules.EveryDay("SPY"),
                    self.TimeRules.AfterMarketOpen("SPY", 1),
                    self.MinuteAfterMarketOpen)

        # Schedule 2
        self.Schedule.On(self.DateRules.EveryDay("SPY"),
                    self.TimeRules.BeforeMarketClose("SPY", 5),
                    self.LiquidateHoldings)

    def Coarse(self, coarse):

        # Return list
        return_list = []

        # # List
        list_one = [x.Symbol for x in coarse if x.HasFundamentalData and x.Price > 0.4]

        # # History
        history = self.History(list_one, 90, Resolution.Daily)

        # # Loop
        for symbol in list_one:

            # Check if has history
            if str(symbol) in history.index:

                # Loc
                symbol_history = history.loc[symbol]

                # If length equal to 90
                if len(symbol_history) == 90:

                    # Average volume
                    average_volume = symbol_history["volume"].mean()

                    # If average volume above 500,000
                    if average_volume > 500000:

                        # Add to return list
                        return_list.append(symbol)

        # Return
        return return_list

    def Fine(self, fine):

        # Return
        return [x.Symbol for x in fine if x.CompanyReference.PrimaryExchangeID == "NAS"]

    def OnSecuritiesChanged(self, changes):

        # Symbols 
        symbols = [x.Symbol for x in changes.AddedSecurities]

        # 6-period rsi
        random_rsi = RelativeStrengthIndex(6)

        # Warm up period
        warm_up_period = random_rsi.WarmUpPeriod * 5

        # History
        history = self.History(symbols, warm_up_period, Resolution.Minute)

        # Warm up set to False
        self.warm_up = False

        problem = []

        # Loop added
        for symbol in symbols:

            # Check if symbol is SPY
            if symbol != self.SPY_symbol:

                # If symbol has historical data
                if str(symbol) in history.index:

                    # Loc
                    symbol_history = history.loc[symbol]

                    # If length == requested
                    if len(symbol_history) == warm_up_period:

                        # Add to symbols list
                        self.symbols_list.append(symbol)
                
                        # Consolidator
                        self.consolidator_dictionary[symbol] = TradeBarConsolidator(timedelta(minutes = 15))

                        # Consolidated handler
                        self.consolidator_dictionary[symbol].DataConsolidated += self.consolidation_handler

                        # Register the consolidator for automatic updates
                        self.SubscriptionManager.AddConsolidator(symbol, self.consolidator_dictionary[symbol])

                        # Consolidator
                        self.five_minute_consolidator_dictionary[symbol] = TradeBarConsolidator(timedelta(minutes = 5))

                        # Consolidated handler
                        self.five_minute_consolidator_dictionary[symbol].DataConsolidated += self.five_minute_consolidation_handler

                        # Register the consolidator for automatic updates
                        self.SubscriptionManager.AddConsolidator(symbol, self.five_minute_consolidator_dictionary[symbol])

                        # 10-minute consolidator
                        self.ten_minute_consolidator_dictionary[symbol] = TradeBarConsolidator(timedelta(minutes = 10))

                        # Consolidated handler
                        self.ten_minute_consolidator_dictionary[symbol].DataConsolidated += self.ten_minute_consolidation_handler

                        # Register the consolidator for automatic updates
                        self.SubscriptionManager.AddConsolidator(symbol, self.ten_minute_consolidator_dictionary[symbol])

                        # RSI
                        self.RSI_storage[symbol] = RelativeStrengthIndex(6)

                        # Register
                        self.RegisterIndicator(symbol = symbol, indicator = self.RSI_storage[symbol], consolidator = self.five_minute_consolidator_dictionary[symbol])

                        # Loop
                        for time, row in symbol_history.iterrows():
                            
                            # Try
                            try:

                                # Create candlestick
                                bar = TradeBar(time - datetime.timedelta(minutes = 1), symbol, row.open, row.high, row.low, row.close, row.volume, datetime.timedelta(minutes = 1))

                                # Update
                                self.five_minute_consolidator_dictionary[symbol].Update(bar)

                            # Except
                            except:
                                
                                # Symbol
                                if symbol not in problem:
                                    
                                    # Add
                                    problem.append(symbol)

        # Warm up set to True
        self.warm_up = True

        # Loop
        for symbol in problem:

            # Unregister consolidator
            self.SubscriptionManager.RemoveConsolidator(symbol, self.consolidator_dictionary[symbol])

            # Remove consolidator
            self.consolidator_dictionary.pop(symbol)

            # Remove
            self.symbols_list.remove(symbol)

            # Unregister consolidator
            self.SubscriptionManager.RemoveConsolidator(symbol, self.five_minute_consolidator_dictionary[symbol])

            # Remove consolidator
            self.five_minute_consolidator_dictionary.pop(symbol)

            # Unregister consolidator
            self.SubscriptionManager.RemoveConsolidator(symbol, self.ten_minute_consolidator_dictionary[symbol])

            # Remove consolidator
            self.ten_minute_consolidator_dictionary.pop(symbol)

            # RSI
            self.RSI_storage.pop(symbol)

        # Loop removed
        for security in changes.RemovedSecurities:
            
            # Symbol
            symbol = security.Symbol

            # If symbol in symbols list
            if symbol in self.symbols_list:

                # Unregister consolidator
                self.SubscriptionManager.RemoveConsolidator(symbol, self.consolidator_dictionary[symbol])

                # Remove consolidator
                self.consolidator_dictionary.pop(symbol)

                # Remove
                self.symbols_list.remove(symbol)

                # Unregister consolidator
                self.SubscriptionManager.RemoveConsolidator(symbol, self.five_minute_consolidator_dictionary[symbol])

                # Remove consolidator
                self.five_minute_consolidator_dictionary.pop(symbol)

                # Unregister consolidator
                self.SubscriptionManager.RemoveConsolidator(symbol, self.ten_minute_consolidator_dictionary[symbol])

                # Remove consolidator
                self.ten_minute_consolidator_dictionary.pop(symbol)

                # RSI
                self.RSI_storage.pop(symbol)

    def MinuteAfterMarketOpen(self):

        # History
        history = self.History(self.symbols_list, 1, Resolution.Daily)

        # Loop
        for symbol in self.symbols_list:

            # If symbol has data
            if str(symbol) in history.index:

                # If price not more than 60% from yesterday close
                if self.Securities[symbol].Open < history.loc[symbol]["close"][-1] * 1.6:

                    # Add to day starting price
                    self.day_starting_price[symbol] = self.Securities[symbol].Open

                    # Add to day candles
                    self.day_candles[symbol] = []

                    # Add to 5-minute bar storage
                    self.five_minute_bar_dictionary[symbol] = []

                    # Add to 10-minute bar storage
                    self.ten_minute_bar_dictionary[symbol] = []

                    # Add to RSI values
                    self.RSI_values[symbol] = []

    def consolidation_handler(self, sender, bar):

        # Current time
        current_time = datetime.time(self.Time.hour, self.Time.minute)

        # 11 am
        eleven = datetime.time(11, 00)

        # 3:55 pm
        three = datetime.time(15, 55)

        # If symbol in day volume 
        if bar.Symbol in self.day_candles:

            # Add
            self.day_candles[bar.Symbol].append(bar)
        
        # Add to counter
        self.counter += 1

        # If counter equal to number of stocks in algo
        if self.counter == len(self.consolidator_dictionary):

            # Reset
            self.counter = 0

            # If time is 11
            if current_time >= eleven and current_time <= three:

                # History symbols list
                history_symbols_list = []

                # Loop
                for symbol in self.day_starting_price:
                    
                    # If price greater than 5% from opening price
                    if self.Securities[symbol].Close > self.day_starting_price[symbol] * 1.05:

                        # Add
                        history_symbols_list.append(symbol)

                # If length
                if len(history_symbols_list) > 0:
                
                    # Get history
                    history = self.History(history_symbols_list[-1], 5, Resolution.Daily)

                    # Get timestamp
                    timestamp = history.index[-1][-1] - timedelta(days = 1)

                    # Start datetime
                    start_datetime = datetime.datetime(timestamp.year, timestamp.month, timestamp.day, 9, 30)

                    # End datetime
                    end_datetime = datetime.datetime(timestamp.year, timestamp.month, timestamp.day, self.Time.hour, self.Time.minute)
                        
                    # Get history
                    history = self.History(history_symbols_list, start_datetime, end_datetime, Resolution.Minute)

                    # Loop
                    for symbol in history_symbols_list:

                        # If symbol in history
                        if str(symbol) in history.index:

                            # Get current volume
                            current_volume = sum([x.Volume for x in self.day_candles[symbol]])

                            # If current volume greater than 300,000
                            if current_volume > 300000:

                                # Symbol history
                                symbol_history = history.loc[symbol]

                                # Previous day volume
                                previous_volume = symbol_history["volume"].sum()

                                # If current volume 5 times previous volume
                                if current_volume > previous_volume * 5:

                                    # If not in list 1
                                    if symbol not in self.list_1:

                                        # Add
                                        self.list_1.append(symbol)

    def five_minute_consolidation_handler(self, sender, bar):

        # If not warm up
        if self.warm_up == True:

            # Add 
            self.counter_2 += 1

            # If symbol in 5-minute bar dictionary
            if bar.Symbol in self.five_minute_bar_dictionary:

                # Add
                self.five_minute_bar_dictionary[bar.Symbol].append(bar)

            # If symbol in RSI values storage
            if bar.Symbol in self.RSI_values:

                # Add
                self.RSI_values[bar.Symbol].append(self.RSI_storage[bar.Symbol].Current.Value)

            # If counter equal length
            if self.counter_2 == len(self.consolidator_dictionary):

                # Reset
                self.counter_2 = 0

                # If length of list 2 above 0
                if len(self.list_2) > 0:

                    # Loop
                    for symbol in self.list_2:

                        # If not invested
                        if not self.Portfolio[symbol].Invested:

                            # Past 3 RSI
                            past_3_RSI = self.RSI_values[symbol][-3:]

                            # If max above 70
                            if max(past_3_RSI) > 70:

                                # If current bar is red
                                if self.five_minute_bar_dictionary[symbol][-1].Close < self.five_minute_bar_dictionary[symbol][-1].Open:

                                    # If 2 bars prior is green
                                    if self.five_minute_bar_dictionary[symbol][-3].Open < self.five_minute_bar_dictionary[symbol][-3].Close:

                                        # If current bar bigger than previous
                                        if abs(self.five_minute_bar_dictionary[symbol][-1].Close - self.five_minute_bar_dictionary[symbol][-1].Open) > abs(self.five_minute_bar_dictionary[symbol][-2].Close - self.five_minute_bar_dictionary[symbol][-2].Open):

                                            # If 2 bars prior bigger than previous
                                            if abs(self.five_minute_bar_dictionary[symbol][-3].Close - self.five_minute_bar_dictionary[symbol][-3].Open) > abs(self.five_minute_bar_dictionary[symbol][-2].Close - self.five_minute_bar_dictionary[symbol][-2].Open):

                                                # Short
                                                self.MarketOrder(symbol, -1)

    def ten_minute_consolidation_handler(self, sender, bar):

        # Add 
        self.counter_3 += 1

        # If symbol in 10-minute bar dictionary
        if bar.Symbol in self.ten_minute_bar_dictionary:

            # Add
            self.ten_minute_bar_dictionary[bar.Symbol].append(bar)

        # If counter equal length
        if self.counter_3 == len(self.consolidator_dictionary):

            # Reset
            self.counter_3 = 0

            # If length of list 1 above 0
            if len(self.list_1) > 0:

                # Loop
                for symbol in self.list_1:

                    # Bars
                    bars = self.ten_minute_bar_dictionary[symbol]

                    # Recent bar value
                    recent_bar_value = bars[-1].Close - bars[-1].Open

                    # List
                    new_list = [(x.Close - x.Open) for x in bars]

                    # Reverse
                    new_list.reverse()

                    # Bars reverse
                    bars.reverse()

                    # If red
                    if recent_bar_value < 0:

                        # Loop
                        for value in new_list:

                            # If value greater than 0
                            if value > 0:

                                # Get value as direcional change
                                directional_change = bars[new_list.index(value)].Close

                                # Get highest close
                                highest_close = max([x.Close for x in bars])

                                # Get open
                                open_price = self.day_starting_price[symbol]

                                # Difference
                                difference = highest_close - open_price

                                # 40% of difference
                                fourty_percent = difference * 0.4

                                # If current close - directional change more than 40% of difference
                                if self.ten_minute_bar_dictionary[symbol][-1].Close - directional_change > fourty_percent:

                                    # Add to list 2
                                    self.list_2.append(symbol)

                    # If green
                    elif recent_bar_value > 0:

                        # Loop
                        for value in new_list:

                            # If value less than 0
                            if value < 0:

                                # Get value as direcional change
                                directional_change = bars[new_list.index(value)].Close

                                # Get highest close
                                highest_close = max([x.Close for x in bars])

                                # Get open
                                open_price = self.day_starting_price[symbol]

                                # Difference
                                difference = highest_close - open_price

                                # 40% of difference
                                fourty_percent = difference * 0.4

                                # If current close - directional change more than 40% of difference
                                if self.ten_minute_bar_dictionary[symbol][-1].Close - directional_change > fourty_percent:

                                    # Add to list 2
                                    self.list_2.append(symbol)

    def LiquidateHoldings(self):

        # Sell everything
        self.Liquidate()
        
        # Remove
        self.list_1.clear()

        # Remove
        self.day_starting_price.clear()

        # Remove
        self.day_candles.clear()

        # Remove
        self.five_minute_bar_dictionary.clear()

        # Remove
        self.ten_minute_bar_dictionary.clear()

        # Remove
        self.list_2.clear()

        # Remove
        self.RSI_values.clear()

    def OnData(self, data: Slice):
        pass