Overall Statistics
Total Trades
4538
Average Win
3.29%
Average Loss
-2.67%
Compounding Annual Return
206.229%
Drawdown
56.000%
Expectancy
0.121
Net Profit
15743.732%
Sharpe Ratio
2.472
Probabilistic Sharpe Ratio
81.594%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
1.23
Alpha
2.086
Beta
-0.252
Annual Standard Deviation
0.83
Annual Variance
0.688
Information Ratio
2.246
Tracking Error
0.853
Treynor Ratio
-8.138
Total Fees
$0.00
Estimated Strategy Capacity
$340000.00
Lowest Capacity Asset
WVVI R735QTJ8XC9X
import pandas as pd
from io import StringIO

class ShortGappers(QCAlgorithm):
    
    estimated_capacity = None
    gapper_data = None
    premarket_high = {}
    traded_today = set()
    
    def Initialize(self):
        self.gapper_data = pd.read_csv(StringIO(self.Download("https://raw.githubusercontent.com/lieblius/financial-data/main/gappers.csv")), index_col='Date')
        
        self.estimated_capacity = int(self.GetParameter("estimated-capacity"))
        
        self.SetStartDate(2016, 10, 26)  
        self.SetEndDate(2021, 5, 4)  
        self.SetCash(25000)
        
        self.SetExecution(ImmediateExecutionModel())
        self.UniverseSettings.Resolution = Resolution.Minute
        self.SetSecurityInitializer(self.CustomSecurityInitializer)
        
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(15, 30) , self.ClosePositions)
        
        self.AddUniverseSelection(ScheduledUniverseSelectionModel(
            self.DateRules.EveryDay(),
            self.TimeRules.At(00, 00),
            self.SelectSymbols
        ))

    def CustomSecurityInitializer(self, security):
        security.SetDataNormalizationMode(DataNormalizationMode.Raw)
        security.SetFeeModel(ConstantFeeModel(0))
        security.SetSlippageModel(ConstantSlippageModel(0))
        security.SetFillModel(ImmediateFillModel())
        
    def OnData(self, data):
        if 9 <= self.Time.hour < 12 :
            if self.Time.hour == 9 and self.Time.minute < 30:
                return
            for security in self.ActiveSecurities:
                equity = security.Value
                symbol = equity.Symbol
                symbol_string = symbol.Value
                if not equity.HasData:
                    continue
                if equity.Invested:
                    continue 
                if symbol_string not in self.premarket_high:
                    continue
                if symbol_string in self.traded_today:
                    continue
                if symbol_string == 'BRPA':
                    # There is a bug in data for this symbol
                    continue
                
                current_price = data[symbol].Close
                if current_price >= self.premarket_high[symbol_string]:
                    continue
                
                quantity = int((min(self.Portfolio.TotalPortfolioValue, self.estimated_capacity) / 4 / current_price))
                
                self.traded_today.add(symbol_string)
                self.MarketOrder(symbol, -quantity)
                self.StopMarketOrder(symbol, quantity, round(self.premarket_high[symbol_string], 2))

    
    def ClosePositions(self):
        self.premarket_high = {}
        self.traded_today = set()
        self.Liquidate()
        self.Transactions.CancelOpenOrders()

    def SelectSymbols(self, dateTime):
        min_gap = float(self.GetParameter("min-gap-pct"))
        min_pmh_price = float(self.GetParameter("min-premarkethigh-price"))
        max_daily_trades = int(self.GetParameter("max-daily-trades"))
        symbols = []
        date = str(dateTime.date())
        if date in self.gapper_data.index:
            gappers = self.gapper_data.loc[date]
            gappers = self.gapper_data.loc[str(date)]
            if isinstance(gappers, pd.Series):
                symbol = gappers['Symbol']
                if gappers['GAP%'] >= min_gap and gappers['Premarket High'] >= min_pmh_price:# \
                # and gappers['Outstanding Shares'] <= 20000000 and gappers['Market Cap'] <= 20000000:
                    self.premarket_high[symbol] = gappers['Premarket High']
                    symbols.append(Symbol.Create(symbol, SecurityType.Equity, Market.USA))
            elif len(gappers) <= max_daily_trades:
                for i in range(len(gappers)):
                    symbol = gappers.iloc[i]['Symbol']
                    if gappers.iloc[i]['GAP%'] >= min_gap and gappers.iloc[i]['Premarket High'] >= min_pmh_price:# \
                    # and gappers.iloc[i]['Outstanding Shares'] <= 20000000 and gappers.iloc[i]['Market Cap'] <= 20000000:
                            self.premarket_high[symbol] = gappers.iloc[i]['Premarket High']
                            symbols.append(Symbol.Create(symbol, SecurityType.Equity, Market.USA))
            else:
                top_list = []
                for i in range(len(gappers)):
                    symbol = gappers.iloc[i]['Symbol']
                    if gappers.iloc[i]['GAP%'] >= min_gap and gappers.iloc[i]['Premarket High'] >= min_pmh_price:# \
                    # and gappers.iloc[i]['Outstanding Shares'] <= 20000000 and gappers.iloc[i]['Market Cap'] <= 20000000:
                        top_list.append(gappers.iloc[i])
                top_list = sorted(top_list, key=lambda g: g['GAP%'], reverse=True)[:max_daily_trades]
                for gapper in top_list:
                    symbol = gapper['Symbol']
                    self.premarket_high[symbol] = gapper['Premarket High']
                    symbols.append(Symbol.Create(symbol, SecurityType.Equity, Market.USA))
        return symbols