Overall Statistics
Total Trades
512
Average Win
0.19%
Average Loss
-0.17%
Compounding Annual Return
-22.945%
Drawdown
7.800%
Expectancy
-0.091
Net Profit
-4.265%
Sharpe Ratio
-1.433
Probabilistic Sharpe Ratio
12.516%
Loss Rate
57%
Win Rate
43%
Profit-Loss Ratio
1.13
Alpha
-0.434
Beta
0.553
Annual Standard Deviation
0.135
Annual Variance
0.018
Information Ratio
-5.077
Tracking Error
0.124
Treynor Ratio
-0.35
Total Fees
$1166.11
class Gap_Up(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 10, 1)  # Set Start Date
        self.SetCash(100000)  # Set Strategy Cash
        
        # Creates "SPY" Equity
        self.symbol = self.AddEquity('SPY', Resolution.Minute).Symbol
        
        # Initiates our Universe on the Daily Resolution
        self.UniverseSettings.Resolution = Resolution.Minute
        self.AddUniverse(self.CoarseSelectionFilter)
        
        # Schedule for 10 minutes before market close
        self.Schedule.On(self.DateRules.EveryDay(self.symbol), self.TimeRules.BeforeMarketClose(self.symbol, 10), self.BeforeMarketClose)

        # Whether or not to trade a given stock
        self.stop_trading = False
        
        # Whether a stock has already been traded
        self.made_trade = False
        
        # Dictionary of data for the universe selection
        self.stateData = { }
        
        # Dictionary of data for the stocks that satisfy the gap conditions
        self.Gaps = {}
        
        # Value for determining when the day starts
        self.curr_day = -1
    
    def OnData(self, data):
        # If the market is not open, return
        if not self.IsMarketOpen(self.symbol):
            return
        
        # Get current day open
        if self.curr_day != self.Time.day:
            for symbol in self.Gaps:
                # Resets StopTrading and MadeTrade for each stock in Gaps
                self.Gaps[symbol].StopTrading = False
                self.Gaps[symbol].MadeTrade = False
                # If the symbol exists, set the open to the days open price
                if data.ContainsKey(symbol) and data[symbol] is not None:
                    self.Gaps[symbol].Open = data[symbol].Open
                # Else, set the "Open" to -1
                else:
                    self.Gaps[symbol].Open = -1
    
        self.curr_day = self.Time.day
        
        # Checks to make sure all symbols in Gaps contain the data we need
        # If they don't sets StopTrading to True
        for symbol, gap in self.Gaps.items():
            # If the stock should not be traded, continue and StopTrading is True
            if gap.StopTrading:
                continue
            # If the the symbol does not have an open or close price, continue and StopTrading is True
            elif gap.Open < 0 or gap.Close < 0:
                gap.StopTrading = True
                continue
            # if gap up doesn't meet our threshold, continue and StopTrading is True
            elif gap.Open - gap.Close <= 0:
                gap.StopTrading = True
                continue
            # If symbol contains no data, continue
            elif not data.ContainsKey(symbol) or data[symbol] is None:
                continue

            curr_price = data[symbol].Close
            
            # Gives a decimal value so that SetHoldings always invests an equal amount per stock   
            order_perc = 1 / len(self.Gaps)
            
            # Executes a trade for the given symbol
            if not gap.MadeTrade:
                self.SetHoldings(symbol, order_perc)
                self.Log(f"Volume: {str(symbol)}:   {self.stateData[symbol].sma.Current.Value > 3500000}")
                gap.MadeTrade = True
       
        
    def BeforeMarketClose(self):
        # Liquidate all assets
        self.Liquidate()
        
        for symbol in self.Gaps:
            # If the symbol exists, sets the close for the day to Gaps.Close
            if self.CurrentSlice.ContainsKey(symbol) and self.CurrentSlice[symbol] is not None:
                self.Gaps[symbol].Close = self.Securities[symbol].Price
            # Otherwise, the close equals -1, so it will be flitered out
            else:
                self.Gaps[symbol].Close = -1
                
            # Sets the StopTrading back to False
            self.Gaps[symbol].StopTrading = False
            
    def OnSecuritiesChanged(self, changed):
        # Removes removed securites from Gaps
        for security in changed.RemovedSecurities:
            self.Gaps.pop(security.Symbol)
        
        # Adds Added Securites to Gaps
        for security in changed.AddedSecurities:
            self.Gaps[security.Symbol] = Gap()
            
    def CoarseSelectionFilter(self, coarse):
        self.coarse = coarse
        
        # We are going to use a dictionary to refer the object that will keep the moving averages
        # Adds the stocks that fit the volume criteria to the dictionary
        for c in coarse:
            if c.Symbol not in self.stateData:
                self.stateData[c.Symbol] = SelectionData(c.Symbol, 10)
                
            # Update the stateData to make sure it has the most recent stock data
            avg = self.stateData[c.Symbol]
            avg.update(c.EndTime, c.AdjustedPrice, c.DollarVolume)
        
        values = [x for x in self.stateData.values() if x.is_above_vol and x.price >= 1 and x.price <= 100]
        
        #values.sort(key=lambda x: x.volume, reverse=True)
        
        # Makes sure price is between 1 and 100
        #filtered = [symbol for symbol, sd in self.stateData.items() if sd.price >= 1 and sd.price <= 100]
        
                
        return [ x.symbol for x in values[:10] ]
                     
       # return filtered[:50]
        
class Gap:
    def __init__(self):
        self.Close = -1
        self.Open = -1
        self.MadeTrade = False
        self.StopTrading = False


class SelectionData(object):
    def __init__(self, symbol, period):
        self.symbol = symbol
        self.volume = 0
        self.price = 0
        self.sma = SimpleMovingAverage(period)
        self.is_above_vol = False
        
    def update(self, time, price, volume):
        self.volume = volume
        self.price = price
        if self.sma.Update(time, volume):
            self.is_above_vol = self.sma.Current.Value > 3500000