Overall Statistics
Total Trades
475
Average Win
1.21%
Average Loss
-1.11%
Compounding Annual Return
-100%
Drawdown
89.000%
Expectancy
-0.732
Net Profit
-88.952%
Sharpe Ratio
-0.233
Probabilistic Sharpe Ratio
0.009%
Loss Rate
87%
Win Rate
13%
Profit-Loss Ratio
1.09
Alpha
-4.351
Beta
8.767
Annual Standard Deviation
4.285
Annual Variance
18.359
Information Ratio
-0.325
Tracking Error
4.254
Treynor Ratio
-0.114
Total Fees
$85170.23
Estimated Strategy Capacity
$62000.00
Lowest Capacity Asset
BMJ TDP0JIUCTNJ9
class QuantumHorizontalRegulators(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 7, 1)  # Set Start Date
        self.SetEndDate(2020, 8, 31)
        self.SetCash(333333)  # Set Strategy Cash
        
        
        self.backtestSymbolsPerDay = {}
        self.current_universe = []

        self.UniverseSettings.Resolution = Resolution.Second
        
        self.SetUniverseSelection(ScheduledUniverseSelectionModel(
            self.DateRules.Every(DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday),
            self.TimeRules.Every(timedelta(minutes = 15)),
            self.SelectSymbols
            ))

        self.AddAlpha(ShortSqueezeModel(self))
        
        self.SetExecution(ImmediateExecutionModel())

        self.SetPortfolioConstruction(AccumulativeInsightPortfolioConstructionModel(lambda time: None))
        
        self.SetRiskManagement(TrailingStopRiskManagementModel(0.02))
        

    def SelectSymbols(self, dateTime):
         # handle live mode file format
        if self.LiveMode:
            # fetch the file from dropbox
            str = self.Download("https://www.dropbox.com/s/x4ptyv396uv0ezu/Watchlist%202.csv?dl=1")
            # if we have a file for today, return symbols, else leave universe unchanged
            self.current_universe = str.split(',') if len(str) > 0 else self.current_universe
            return self.current_universe

        # backtest - first cache the entire file
        if len(self.backtestSymbolsPerDay) == 0:

            # No need for headers for authorization with dropbox, these two lines are for example purposes 
            #byteKey = base64.b64encode("UserName:Password".encode('ASCII'))
            # The headers must be passed to the Download method as dictionary
            #headers = { 'Authorization' : f'Basic ({byteKey.decode("ASCII")})' }

            #str = self.Download("https://www.dropbox.com/s/x4ptyv396uv0ezu/Watchlist%202.csv?dl=1", headers)
            csv_data = self.Download("https://www.dropbox.com/s/x4ptyv396uv0ezu/Watchlist%202.csv?dl=1")
            for line in csv_data.splitlines():
                data = line.split(',')
                self.backtestSymbolsPerDay[data[0]] = data[1:]
        
        index = dateTime.date().strftime("%Y%m%d")
        
        if index in self.backtestSymbolsPerDay.keys():
            tickers = self.backtestSymbolsPerDay[index]
            self.current_universe = [Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in tickers]
        
        return self.current_universe
        

class ShortSqueezeModel(AlphaModel):
    symbolData = {}

    def __init__(self, algo):
        self.algo = algo
        
    
    def Update(self, algorithm, slice):
        insights = []
        
        # Create insights for symbols up at least 40% on the day
        for symbol, symbol_data in self.symbolData.items():
            
            # If already invested, continue to next symbol
            if algorithm.Securities[symbol].Invested or symbol not in slice.Bars or not symbol_data.max.IsReady:
                continue
            
            # Calculate return sign yesterday's close
            yest_close = symbol_data.yest_close
            close = slice[symbol].Close 
            ret = (close - yest_close) / yest_close
            high_of_day_break = close > symbol_data.max.Current.Value
            
            if ret >= 0.4 and high_of_day_break:    # Up 20% on the day & breaks high of day
                hours = algorithm.Securities[symbol].Exchange.Hours 
                closeTime = hours.GetNextMarketClose(algorithm.Time, False) - timedelta(minutes=2) # exit 1-minute before the close
                insights.append(Insight.Price(symbol, closeTime, InsightDirection.Up))
        
        return insights
    
    
    
    def OnSecuritiesChanged(self, algorithm, changes):
        if len(changes.AddedSecurities) > 0:
            # Get history of symbols over lookback window
            added_symbols = [x.Symbol for x in changes.AddedSecurities]
            history = algorithm.History(added_symbols, 1, Resolution.Daily)
            if history.empty:
                    return
            history  = history['close']
            for added in changes.AddedSecurities:
                # Save yesterday's close
                closes = history.loc[[str(added.Symbol.ID)]].values
                if len(closes) < 1:
                    continue
                self.symbolData[added.Symbol] = SymbolData(closes[0], algorithm, added.Symbol)
            
        for removed in changes.RemovedSecurities:
            # Delete yesterday's close tracker
            symbol_data = self.symbolData.pop(removed.Symbol, None)
            if symbol_data:
                symbol_data.dispose()

class SymbolData:
    def __init__(self, yest_close, algo, symbol):
        self.yest_close = yest_close
        self.algo = algo
        self.symbol = symbol
        self.event = algo.Schedule.On(algo.DateRules.EveryDay(symbol), algo.TimeRules.AfterMarketOpen(symbol, 375), self.reset)
        self.internal_max = Maximum(45) # 45 minutes
        self.max = Delay(1) 
        self.daily_consolidator = TradeBarConsolidator(timedelta(days = 1))    ## 1 Day TradeBar Consolidator
        self.daily_consolidator.DataConsolidated += self.DailyConsolidator  ## Add fuction to do what you want every day with your data
        self.algo.SubscriptionManager.AddConsolidator(self.symbol, self.daily_consolidator)
        
        self.minute_consolidator = TradeBarConsolidator(timedelta(minutes = 1))    ## 1 Day TradeBar Consolidator
        self.minute_consolidator.DataConsolidated += self.MinuteConsolidator  ## Add fuction to do what you want every day with your data
        self.algo.SubscriptionManager.AddConsolidator(self.symbol, self.minute_consolidator)
    
    def MinuteConsolidator(self, sender, bar):
        self.internal_max.Update(bar.Time,bar.High)
        self.max.Update(bar.Time, self.internal_max.Current.Value)
        
    def DailyConsolidator(self, sender, bar):
        self.yest_close = bar.Close
        
    def reset(self):
        self.internal_max.Reset()
        self.max.Reset()
        
    def dispose(self):
        self.algo.Schedule.Remove(self.event)
        
        # Remove consolidators
        self.algo.SubscriptionManager.RemoveConsolidator(self.symbol, self.daily_consolidator)
        self.algo.SubscriptionManager.RemoveConsolidator(self.symbol, self.minute_consolidator)